From 44994bd14e5662af466c31668ebbd63e4a5d5890 Mon Sep 17 00:00:00 2001 From: Logan G Date: Tue, 31 Dec 2024 16:59:03 -0700 Subject: [PATCH] Prototype --- config.toml | 11 +++-- download.go | 113 ++++++++++++++++++++++++------------------------ ffmpeg.go | 4 +- substring.go | 42 ++++++++++++++++++ youtube.go | 113 ------------------------------------------------ ytva.go | 118 +++++++++++++++++++++++++-------------------------- 6 files changed, 166 insertions(+), 235 deletions(-) create mode 100644 substring.go delete mode 100644 youtube.go diff --git a/config.toml b/config.toml index ee17a60..5d73b2d 100644 --- a/config.toml +++ b/config.toml @@ -1,6 +1,11 @@ -OutputDir = "out/" -Channels = [ "https://www.youtube.com/@BudgetBuildsOfficial", "https://www.youtube.com/@RandomGaminginHD" ] +TempDir = "/tmp/" -[YouTube] +[Group.PCBuilders] +URL = [ "https://www.youtube.com/@BudgetBuildsOfficial", "https://www.youtube.com/@RandomGaminginHD" ] +OutputDir = "out/%Channel%/%Playlist%/" +FileName = "%Timestamp% - %Title%.mkv" +CFormat = "matroska" VideoFormat = "bestvideo[height<=720]" AudioFormat = "bestaudio" +NumVideos = 2 +SizeLimit = 4294967296 diff --git a/download.go b/download.go index c296826..3f4852f 100644 --- a/download.go +++ b/download.go @@ -7,7 +7,7 @@ import ( "io" "errors" "net/http" - "strconv" + //"strconv" "github.com/charmbracelet/log" @@ -33,7 +33,7 @@ func checkURL(input string) (error) { return nil } -func downloadVideo(url string, videoFormat string, audioFormat string, outDir string, sizeLimit float64) (err error) { +func downloadVideo(url string, path string, group GroupConfig) (err error) { goutubedl.Path = "yt-dlp" if err := checkURL(url); err != nil { @@ -47,67 +47,68 @@ func downloadVideo(url string, videoFormat string, audioFormat string, outDir st return err } - path := outDir+strconv.Itoa(int(video.Info.Timestamp))+" - "+video.Info.Title + if video.Info.IsLive { + log.Info("Video is a live stream. Skipping.") + return nil + } - if ! video.Info.IsLive { - /* VIDEO */ - { - log.Debugf("Downloading video \"%s\" with format \"%s\"", video.Info.ID, videoFormat) + /* VIDEO */ + { + log.Debugf("Downloading video \"%s\" with format \"%s\"", video.Info.ID, group.VideoFormat) - videoDLResult, err := video.Download(context.Background(), videoFormat) - if err != nil { - return err - } - - defer videoDLResult.Close() - - file, err := os.OpenFile(path+"-vid", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - defer file.Close() - - io.Copy(file, videoDLResult) - - videoDLResult.Close() - file.Close() - } - - /* AUDIO */ - { - log.Debugf("Downloading audio \"%s\" with format \"%s\"", video.Info.ID, audioFormat) - - audioDLResult, err := video.Download(context.Background(), audioFormat) - if err != nil { - return err - } - - defer audioDLResult.Close() - - file, err := os.OpenFile(path+"-audio", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - defer file.Close() - - io.Copy(file, audioDLResult) - - audioDLResult.Close() - file.Close() - } - - mergeStreams(path+"-vid",path+"-audio",path+".mkv") - - if err := os.Remove(path+"-vid"); err != nil { - log.Warnf("Could not remove file \"%s\"", path+"-vid") - } - if err := os.Remove(path+"-audio"); err != nil { - log.Warnf("Could not remove file \"%s\"", path+"-audio") - } - - /* - extension, err := getExtension(path) + videoDLResult, err := video.Download(context.Background(), group.VideoFormat) if err != nil { return err } - os.Rename(path, path+"."+extension) - */ + defer videoDLResult.Close() + + file, err := os.OpenFile(MainConfig.TempDir+"/"+video.Info.ID+"-vid", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + defer file.Close() + + io.Copy(file, videoDLResult) + + videoDLResult.Close() + file.Close() } + /* AUDIO */ + { + log.Debugf("Downloading audio \"%s\" with format \"%s\"", video.Info.ID, group.AudioFormat) + + audioDLResult, err := video.Download(context.Background(), group.AudioFormat) + if err != nil { + return err + } + + defer audioDLResult.Close() + + file, err := os.OpenFile(MainConfig.TempDir+"/"+video.Info.ID+"-audio", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + defer file.Close() + + io.Copy(file, audioDLResult) + + audioDLResult.Close() + file.Close() + } + + mergeStreams(MainConfig.TempDir+"/"+video.Info.ID+"-vid",MainConfig.TempDir+"/"+video.Info.ID+"-audio", group.CFormat, path) + + if err := os.Remove(MainConfig.TempDir+"/"+video.Info.ID+"-vid"); err != nil { + log.Warnf("Could not remove file \"%s\"", MainConfig.TempDir+"/"+video.Info.ID+"-vid") + } + if err := os.Remove(MainConfig.TempDir+"/"+video.Info.ID+"-audio"); err != nil { + log.Warnf("Could not remove file \"%s\"", MainConfig.TempDir+"/"+video.Info.ID+"-audio") + } + + /* + extension, err := getExtension(path) + if err != nil { + return err + } + + os.Rename(path, path+"."+extension) + */ + return nil } diff --git a/ffmpeg.go b/ffmpeg.go index b8a170b..35f42e8 100644 --- a/ffmpeg.go +++ b/ffmpeg.go @@ -4,10 +4,10 @@ import ( ffmpeg "github.com/u2takey/ffmpeg-go" ) -func mergeStreams(path1 string, path2 string, output string) (err error) { +func mergeStreams(path1 string, path2 string, format string, output string) (err error) { input := []*ffmpeg.Stream{ffmpeg.Input(path1), ffmpeg.Input(path2)} - defaultArgs := ffmpeg.KwArgs{"c:v": "copy", "c:a": "copy", "format": "matroska"} + defaultArgs := ffmpeg.KwArgs{"c:v": "copy", "c:a": "copy", "format": format} var ffmpegLogLevel ffmpeg.KwArgs var silent bool diff --git a/substring.go b/substring.go new file mode 100644 index 0000000..111ed14 --- /dev/null +++ b/substring.go @@ -0,0 +1,42 @@ +package main + +import ( + "regexp" + "reflect" + "fmt" + + //"github.com/charmbracelet/log" + "github.com/wader/goutubedl" +) + + +func fillPlaceholders(input string, videoInfo goutubedl.Info) (output string) { + + re := regexp.MustCompile(`%([^%]+)%`) + + return re.ReplaceAllStringFunc(input, func(match string) string { + groups := re.FindStringSubmatch(match) + if len(groups) < 1 { + // No capture group found; return original match + return match + } + key := groups[1] // the captured part + + v := reflect.ValueOf(videoInfo) + if v.Kind() == reflect.Struct { + fieldVal := v.FieldByName(key) + if fieldVal.IsValid() && fieldVal.CanInterface() { + // Haha cheating :) + if fieldVal.Kind() == reflect.Float64 { + return fmt.Sprintf("%v", int(fieldVal.Float())) + } else { + return fmt.Sprintf("%v", fieldVal.Interface()) + } + } + } + + // Value not found; return original match + return match + }) + +} diff --git a/youtube.go b/youtube.go deleted file mode 100644 index c296826..0000000 --- a/youtube.go +++ /dev/null @@ -1,113 +0,0 @@ -package main - -import ( - "context" - "fmt" - "os" - "io" - "errors" - "net/http" - "strconv" - - - "github.com/charmbracelet/log" - "github.com/wader/goutubedl" -) - -func checkURL(input string) (error) { - request, err := http.NewRequest("GET", input, nil) - if err != nil { - return err - } - - client := &http.Client{} - resp, err := client.Do(request) - if err != nil { - return err - } - - if resp.StatusCode == http.StatusNotFound { - return errors.New(fmt.Sprintf("Video does not exist!")) - } - - return nil -} - -func downloadVideo(url string, videoFormat string, audioFormat string, outDir string, sizeLimit float64) (err error) { - goutubedl.Path = "yt-dlp" - - if err := checkURL(url); err != nil { - return err - } - - log.Debugf("URL \"%s\" exists.", url) - - video, err := goutubedl.New(context.Background(), url, goutubedl.Options{}) - if err != nil { - return err - } - - path := outDir+strconv.Itoa(int(video.Info.Timestamp))+" - "+video.Info.Title - - if ! video.Info.IsLive { - /* VIDEO */ - { - log.Debugf("Downloading video \"%s\" with format \"%s\"", video.Info.ID, videoFormat) - - videoDLResult, err := video.Download(context.Background(), videoFormat) - if err != nil { - return err - } - - defer videoDLResult.Close() - - file, err := os.OpenFile(path+"-vid", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - defer file.Close() - - io.Copy(file, videoDLResult) - - videoDLResult.Close() - file.Close() - } - - /* AUDIO */ - { - log.Debugf("Downloading audio \"%s\" with format \"%s\"", video.Info.ID, audioFormat) - - audioDLResult, err := video.Download(context.Background(), audioFormat) - if err != nil { - return err - } - - defer audioDLResult.Close() - - file, err := os.OpenFile(path+"-audio", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) - defer file.Close() - - io.Copy(file, audioDLResult) - - audioDLResult.Close() - file.Close() - } - - mergeStreams(path+"-vid",path+"-audio",path+".mkv") - - if err := os.Remove(path+"-vid"); err != nil { - log.Warnf("Could not remove file \"%s\"", path+"-vid") - } - if err := os.Remove(path+"-audio"); err != nil { - log.Warnf("Could not remove file \"%s\"", path+"-audio") - } - - /* - extension, err := getExtension(path) - if err != nil { - return err - } - - os.Rename(path, path+"."+extension) - */ - } - - return nil -} diff --git a/ytva.go b/ytva.go index 55cda00..19f86f3 100644 --- a/ytva.go +++ b/ytva.go @@ -32,18 +32,23 @@ var Flags _flags -type ConfigFile struct { - OutputDir string - Channels []string - YouTube struct { - VideoFormat string - AudioFormat string - } +type _config struct { + TempDir string + Group map[string]GroupConfig } -var MainConfig ConfigFile - +type GroupConfig struct { + URL []string + OutputDir string + FileName string + CFormat string + VideoFormat string + AudioFormat string + NumVideos uint + SizeLimit float64 +} +var MainConfig _config func main() { _ = arg.MustParse(&Flags) @@ -71,67 +76,58 @@ func main() { goutubedl.Path = "yt-dlp" - if _, err := os.Stat(MainConfig.OutputDir); err != nil { - if os.IsNotExist(err) { - if err := os.Mkdir(MainConfig.OutputDir, 0755); err != nil { - log.Fatal(err) + for name, group := range MainConfig.Group { + log.Info("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") + log.Infof("Started group \"%s\"", name) + + for _, channel := range group.URL { + log.Info("================================================================") + log.Infof("Channel URL: \"%s\"", channel) + + optType := goutubedl.TypeFromString["channel"] + channel, err := goutubedl.New(context.Background(), channel, + goutubedl.Options{ + Type: optType, + PlaylistEnd: group.NumVideos, + }, + ) + if err != nil { + log.Fatalf("Error creating YouTube service: %v", err) } - } else { - log.Fatal(err) - } - } - for _, channel := range MainConfig.Channels { - log.Info("==========================================================================================================") - log.Infof("Channel URL: \"%s\"", channel) + log.Infof("Channel Name: %s", channel.Info.Channel) - optType := goutubedl.TypeFromString["channel"] - channel, err := goutubedl.New(context.Background(), channel, - goutubedl.Options{ - Type: optType, - PlaylistEnd: 2, - }, - ) - if err != nil { - log.Fatalf("Error creating YouTube service: %v", err) - } + for _, v := range channel.Info.Entries { + log.Debugf("-------------------------------------") + log.Debugf("Title: %s", v.Title) + log.Debugf("URL: %s", v.WebpageURL) + log.Debugf("Playlist: %s", v.Playlist) + log.Debugf("PublishedAt: %s", v.UploadDate) + log.Debugf("IsLive: %t", v.IsLive) + //log.Debugf("Description: %s\n", item.Snippet.Description) - log.Infof("Channel Name: %s", channel.Info.Channel) + dirPath := fillPlaceholders(group.OutputDir, v) + fileName := fillPlaceholders(group.FileName, v) + path := dirPath+"/"+fileName - basePath := MainConfig.OutputDir+"/"+channel.Info.Channel+"/" + log.Debugf("Output: %s", path) - if _, err := os.Stat(basePath); err != nil { - if os.IsNotExist(err) { - if err := os.Mkdir(basePath, 0755); err != nil { - log.Fatal(err) - } - } else { - log.Fatal(err) - } - } - - for _, v := range channel.Info.Entries { - log.Debugf("-----------------------------------------------------") - log.Debugf("Title: %s", v.Title) - log.Debugf("URL: %s", v.WebpageURL) - log.Debugf("Playlist: %s", v.Playlist) - log.Debugf("PublishedAt: %s", v.UploadDate) - log.Debugf("IsLive: %t", v.IsLive) - //log.Debugf("Description: %s\n", item.Snippet.Description) - - dirPath := basePath+"/"+v.Playlist+"/" - if _, err := os.Stat(dirPath); err != nil { - if os.IsNotExist(err) { - if err := os.Mkdir(dirPath, 0755); err != nil { - log.Fatal(err) + if _, err := os.Stat(dirPath); err != nil { + if os.IsNotExist(err) { + log.Debugf("Directory \"%s\" does not exist, creating.", dirPath) + if err := os.MkdirAll(dirPath, 0755); err != nil { + log.Error(err) + continue + } + } else { + log.Error(err) + continue } - } else { - log.Fatal(err) } - } - log.Infof("Downloading video: %s", v.Title) - downloadVideo(v.WebpageURL, MainConfig.YouTube.VideoFormat, MainConfig.YouTube.AudioFormat, dirPath, 2^32) + log.Infof("Downloading video: %s", v.Title) + downloadVideo(v.WebpageURL, path, group) + } } } }