2021-09-24 05:15:40 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
2021-10-08 14:38:35 +00:00
|
|
|
"io"
|
2021-09-24 05:15:40 +00:00
|
|
|
"log"
|
2021-10-08 14:38:35 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
2021-09-24 05:15:40 +00:00
|
|
|
|
2021-10-28 01:23:45 +00:00
|
|
|
"git.netflux.io/rob/clipper/media"
|
2021-09-24 05:15:40 +00:00
|
|
|
|
|
|
|
youtubev2 "github.com/kkdai/youtube/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var (
|
2021-10-08 14:38:35 +00:00
|
|
|
verbose bool
|
|
|
|
printMode bool
|
|
|
|
downloadMode bool
|
|
|
|
audioOnly bool
|
|
|
|
videoOnly bool
|
2021-09-24 05:15:40 +00:00
|
|
|
)
|
|
|
|
flag.BoolVar(&verbose, "v", false, "verbose output")
|
2021-10-08 14:38:35 +00:00
|
|
|
flag.BoolVar(&printMode, "print", true, "print format info")
|
|
|
|
flag.BoolVar(&downloadMode, "download", false, "download all media to ./debug")
|
2021-09-24 05:15:40 +00:00
|
|
|
flag.BoolVar(&audioOnly, "audio", false, "only print audio formats")
|
|
|
|
flag.BoolVar(&videoOnly, "video", false, "only print video formats")
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
videoID := flag.Arg(0)
|
|
|
|
ctx := context.Background()
|
|
|
|
var youtubeClient youtubev2.Client
|
|
|
|
|
|
|
|
video, err := youtubeClient.GetVideoContext(ctx, videoID)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
formats := video.Formats
|
|
|
|
|
2021-10-08 14:38:35 +00:00
|
|
|
if downloadMode {
|
|
|
|
downloadAll(formats)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-24 05:15:40 +00:00
|
|
|
switch {
|
|
|
|
case audioOnly:
|
2021-10-28 01:23:45 +00:00
|
|
|
formats = media.FilterYoutubeAudio(formats)
|
2021-09-24 05:15:40 +00:00
|
|
|
case videoOnly:
|
2021-10-28 01:23:45 +00:00
|
|
|
formats = media.FilterYoutubeVideo(formats)
|
2021-09-24 05:15:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("In descending order of preference:")
|
|
|
|
for n, f := range formats {
|
2021-11-12 12:41:59 +00:00
|
|
|
fmt.Printf("%d: %s\n", n+1, formatDebugString(&f, verbose))
|
2021-09-24 05:15:40 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-08 14:38:35 +00:00
|
|
|
|
|
|
|
func downloadAll(formats youtubev2.FormatList) {
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
for i := range formats {
|
|
|
|
format := formats[i]
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
start := time.Now()
|
|
|
|
|
|
|
|
outpath := fmt.Sprintf("./debug/%s.%s-itag-%d", strings.ReplaceAll(format.MimeType, "/", "'"), format.Quality, format.ItagNo)
|
|
|
|
output, err := os.Create(outpath)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("error opening output file: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := http.Get(format.URL)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("error fetching media: %v", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
n, err := io.Copy(output, resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("error reading media: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dur := time.Since(start)
|
|
|
|
log.Printf("downloaded itag %d, %d bytes in %v secs", format.ItagNo, n, dur.Seconds())
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
}
|
2021-11-12 12:41:59 +00:00
|
|
|
|
|
|
|
func formatDebugString(format *youtubev2.Format, includeURL bool) string {
|
|
|
|
url := "hidden"
|
|
|
|
if includeURL {
|
|
|
|
url = format.URL
|
|
|
|
}
|
|
|
|
return fmt.Sprintf(
|
|
|
|
"iTag = %d, mime_type = %s, quality = %s, quality_label = %s, bitrate = %d, fps = %d, width = %d, height = %d, content_length = %d, duration = %v, audio_channels = %d, audio_sample_rate = %s, audio_quality = %s, url = %s",
|
|
|
|
format.ItagNo,
|
|
|
|
format.MimeType,
|
|
|
|
format.Quality,
|
|
|
|
format.QualityLabel,
|
|
|
|
format.Bitrate,
|
|
|
|
format.FPS,
|
|
|
|
format.Width,
|
|
|
|
format.Height,
|
|
|
|
format.ContentLength,
|
|
|
|
format.ApproxDurationMs,
|
|
|
|
format.AudioChannels,
|
|
|
|
format.AudioSampleRate,
|
|
|
|
format.AudioQuality,
|
|
|
|
url,
|
|
|
|
)
|
|
|
|
}
|