package cmd import ( "context" "errors" "fmt" "strings" "time" "github.com/mathismqn/godeez/internal/config" "github.com/mathismqn/godeez/internal/deezer" "github.com/mathismqn/godeez/internal/downloader" "github.com/mathismqn/godeez/internal/store" "github.com/spf13/cobra" ) func newDownloadCmd() *cobra.Command { cmd := &cobra.Command{ Use: "download", Short: "Download tracks from Deezer", Annotations: map[string]string{updateNoticeAnnotation: "true"}, } opts := &downloader.Options{} cmd.PersistentFlags().StringVarP(&opts.Quality, "quality", "q", "mp3_320", "download quality [mp3_128, mp3_320, flac]") cmd.PersistentFlags().DurationVarP(&opts.Timeout, "timeout", "t", 2*time.Minute, "timeout for each download (e.g. 10s, 1m, 2m30s)") cmd.PersistentFlags().BoolVar(&opts.BPM, "bpm", false, "fetch BPM/key and add to file tags") cmd.PersistentFlags().BoolVar(&opts.Genre, "genre", false, "fetch genre and add to file tags") cmd.PersistentFlags().BoolVar(&opts.Strict, "strict", false, "fail the download if the requested quality is unavailable") // Every subcommand shares opts: registering the artist-only --limit flag // stores its default in the shared struct, which Validate requires for all kinds. cmd.AddCommand( newDownloadSubCmd(deezer.KindAlbum, opts), newDownloadSubCmd(deezer.KindPlaylist, opts), newDownloadSubCmd(deezer.KindArtist, opts), newDownloadSubCmd(deezer.KindTrack, opts), ) return cmd } func newDownloadSubCmd(kind deezer.Kind, opts *downloader.Options) *cobra.Command { cmd := &cobra.Command{ Use: fmt.Sprintf("%s <%s_id>", kind, kind), Short: downloadShort(kind), Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { opts.Quality = strings.ToLower(opts.Quality) return opts.Validate() }, RunE: func(cmd *cobra.Command, args []string) error { cfg, err := config.Load() if err != nil { return err } config.MigrateLegacy(cfg.OutputDir) st, err := store.Open(cfg.OutputDir) if err != nil { return err } defer st.Close() err = downloader.New(cfg, st, kind).Run(cmd.Context(), *opts, args[0]) if errors.Is(err, context.Canceled) { return nil } return err }, } if kind == deezer.KindArtist { cmd.Flags().IntVarP(&opts.Limit, "limit", "l", 10, "number of tracks to download") } return cmd } func downloadShort(kind deezer.Kind) string { switch kind { case deezer.KindArtist: return "Download an artist's top tracks" case deezer.KindTrack: return "Download a single track" case deezer.KindAlbum: return "Download tracks from an album" default: return fmt.Sprintf("Download tracks from a %s", kind) } }