/* Copyright © 2024 Evan Fiordeliso */ package cmd import ( "errors" "fmt" "log/slog" "os" "github.com/dgraph-io/badger/v2" "github.com/spf13/cobra" "github.com/spf13/viper" "go.fifitido.net/ytdl-web/config" "go.fifitido.net/ytdl-web/pkg/server" "go.fifitido.net/ytdl-web/pkg/utils" "go.fifitido.net/ytdl-web/pkg/ytdl" "go.fifitido.net/ytdl-web/pkg/ytdl/cache" ) var ( cfgFile string cfg *config.Config rootCmd = &cobra.Command{ Use: "ytdl-web", Short: "A web frontend for yt-dlp", Long: `YTDL Web is a web application that grabs the links to videos from over a thousand websites using the yt-dlp project under the hood.`, RunE: func(cmd *cobra.Command, args []string) error { initLogging() logger := slog.Default() db, err := badger.Open( badger. DefaultOptions(cfg.Cache.DirPath). WithLogger(utils.NewBadgerLogger(logger.With("module", "badger"))), ) if err != nil { return err } defer db.Close() cache := cache.NewDefaultMetadataCache(db) yt := ytdl.NewYtdl(cfg, slog.Default(), cache) ytdl.SetDefault(yt) return server.ListenAndServe( server.WithListenAddr(viper.GetString("http.listen")), server.WithListenPort(viper.GetInt("http.port")), server.WithLogger(logger.With("module", "server")), ) }, } ) func Execute() error { return rootCmd.Execute() } func init() { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $XDG_CONFIG_HOME/ytdl-web/config.yml)") rootCmd.PersistentFlags().IntP("port", "p", 8080, "port to listen on") rootCmd.PersistentFlags().StringP("listen", "l", "", "address to listen on") rootCmd.PersistentFlags().StringP("base-path", "b", "", "the base path, used when behind reverse proxy") rootCmd.PersistentFlags().StringP("ytdlp-path", "y", "", "the path to the yt-dlp binary, used when it is not in $PATH") rootCmd.PersistentFlags().BoolP("cookies-enabled", "C", false, "whether cookies are enabled") rootCmd.PersistentFlags().StringP("cookies", "c", "", "the path to the cookies file") viper.BindPFlag("http.port", rootCmd.PersistentFlags().Lookup("port")) viper.BindPFlag("http.listen", rootCmd.PersistentFlags().Lookup("listen")) viper.BindPFlag("http.basePath", rootCmd.PersistentFlags().Lookup("base-path")) viper.BindPFlag("ytdlp.binaryPath", rootCmd.PersistentFlags().Lookup("ytdlp-path")) viper.BindPFlag("cookies.enabled", rootCmd.PersistentFlags().Lookup("cookies-enabled")) viper.BindPFlag("cookies.filePath", rootCmd.PersistentFlags().Lookup("cookies")) } func initConfig() { var err error if cfgFile != "" { cfg, err = config.LoadConfig(cfgFile) } else { cfg, err = config.LoadConfig() } notFound := &viper.ConfigFileNotFoundError{} switch { case err != nil && !errors.As(err, notFound): cobra.CheckErr(err) case err != nil && errors.As(err, notFound): // The config file is optional, we shouldn't exit when the config is not found break default: fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) } } func initLogging() { if cfg.IsProduction() { slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelInfo, }))) } else { slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelDebug, }))) } }