package main import ( //"flag" "context" "fmt" "os" //"io" //"reflect" "regexp" "path/filepath" //"google.golang.org/api/option" //"google.golang.org/api/youtube/v3" "github.com/wader/goutubedl" "github.com/BurntSushi/toml" "github.com/charmbracelet/log" "github.com/alexflint/go-arg" ) type _flags struct { Quiet bool `arg:"-q,--quiet" help:"Output only warnings and errors."` Verbose bool `arg:"-v,--verbose" help:"Enables debug output."` Version bool `arg:"--version" help:"Prints version of ytva and exits."` ConfigFile string `arg:"-c,--config,env:YTVA_CONFIG" help:"Specifies the path to a TOML formatted configuration file." default:"config.toml"` } func (_flags) Description() string { return "YouTube Video Archive - Downloads videos in bulk automatically" } var Flags _flags type _config struct { TempDir string Group map[string]GroupConfig } type GroupConfig struct { URL []string OutputDir string FileName string CFormat string VideoFormat string AudioFormat string NumVideos uint SizeLimit float64 Filters []struct { Name string Input string Pattern string AllowDeny bool } } var MainConfig _config var gitCommit string var buildDate string func main() { _ = arg.MustParse(&Flags) if Flags.Verbose { log.SetReportCaller(true) log.SetLevel(log.DebugLevel) if Flags.Quiet { log.Warn("Verbose and quiet specified. Defaulting to verbose.") } } else if Flags.Quiet { log.SetLevel(log.WarnLevel) } if Flags.Version { if gitCommit == "" { gitCommit = "unknown" } if buildDate == "" { buildDate = "unknown" } fmt.Printf("Commit %s built on %s\n", gitCommit, buildDate) os.Exit(0) } confFile, err := os.ReadFile(Flags.ConfigFile) if(err != nil) { log.Fatal(err) } confContent := string(confFile) _, err = toml.Decode(confContent, &MainConfig) log.Debug("", "config", MainConfig) goutubedl.Path = "yt-dlp" 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) } log.Infof("Channel Name: %s", channel.Info.Channel) log.Debugf("Got %d entries", len(channel.Info.Entries)) video: for _, v := range channel.Info.Entries { log.Debugf("-------------------------------------") log.Debugf("Title: %s", v.Title) log.Debugf("AltTitle: %s", v.AltTitle) log.Debugf("URL: %s", v.WebpageURL) log.Debugf("URL: %s", v.URL) 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) if len(group.Filters) > 0 { for _, filter := range group.Filters { testStr := fillPlaceholders(filter.Input, v) matched, err := regexp.MatchString(filter.Pattern, testStr) if err != nil { log.Error(err) continue } if (matched && !filter.AllowDeny) || (!matched && filter.AllowDeny) { log.Infof("Skipping \"%s\" due to filter rule \"%s\".", v.Title, filter.Name) continue video } } } if v.IsLive { log.Infof("Skipping live stream \"%s\".", v.Title) continue } dirPath := fillPlaceholders(group.OutputDir, v) fileName := fillPlaceholders(group.FileName, v) path := filepath.Join(dirPath, filepath.Clean(sanitizeFilename(replaceDelimiters(fileName)))) log.Debugf("Output: \"%s\"", path) 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 } } if _, err := os.Stat(path); err != nil { if os.IsNotExist(err) { log.Infof("Downloading video: %s", v.Title) if err := downloadVideo(v.WebpageURL, path, group); err != nil { log.Error(err) } } else { log.Error(err) continue } } else { log.Infof("Skipping existing video \"%s\".", v.Title) continue } } } } }