diff --git a/cmd/download.go b/cmd/download.go index 7a0957d..87edd23 100644 --- a/cmd/download.go +++ b/cmd/download.go @@ -20,9 +20,9 @@ var downloadCmd = &cobra.Command{ func init() { RootCmd.AddCommand(downloadCmd) - downloadCmd.PersistentFlags().StringVarP(&opts.OutputDir, "output", "o", "", "output directory (default is $HOME/Music/GoDeez)") - downloadCmd.PersistentFlags().StringVarP(&opts.Quality, "quality", "q", "", "download quality [mp3_128, mp3_320, flac, best] (default is best)") - downloadCmd.PersistentFlags().DurationVarP(&opts.Timeout, "timeout", "t", 2*time.Minute, "timeout for each download (e.g. 10s, 1m, 2m30s) (default is 2m)") + downloadCmd.PersistentFlags().StringVarP(&opts.OutputDir, "output", "o", "", "output directory (default $HOME/Music/GoDeez)") + downloadCmd.PersistentFlags().StringVarP(&opts.Quality, "quality", "q", "best", "download quality [mp3_128, mp3_320, flac, best]") + downloadCmd.PersistentFlags().DurationVarP(&opts.Timeout, "timeout", "t", 2*time.Minute, "timeout for each download (e.g. 10s, 1m, 2m30s)") downloadCmd.AddCommand( newDownloadCmd("album"), @@ -32,9 +32,9 @@ func init() { func newDownloadCmd(resourceType string) *cobra.Command { cmd := &cobra.Command{ - Use: fmt.Sprintf("%s [%s_id...]", resourceType, resourceType), - Short: fmt.Sprintf("Download songs from one or more %ss", resourceType), - Args: cobra.MinimumNArgs(1), + Use: fmt.Sprintf("%s ", resourceType), + Short: fmt.Sprintf("Download songs from %s", resourceType), + Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { return opts.Validate(appCtx.AppDir) }, @@ -42,7 +42,7 @@ func newDownloadCmd(resourceType string) *cobra.Command { ctx := cmd.Context() dl := downloader.New(appCtx, resourceType) - if err := dl.Run(ctx, opts, args); err != nil { + if err := dl.Run(ctx, opts, args[0]); err != nil { if errors.Is(err, context.Canceled) { return nil } diff --git a/cmd/root.go b/cmd/root.go index 62503c5..b4653b2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -23,5 +23,5 @@ var RootCmd = &cobra.Command{ } func init() { - RootCmd.PersistentFlags().StringVar(&cfgPath, "config", "", "config file (default is $HOME/.godeez)") + RootCmd.PersistentFlags().StringVar(&cfgPath, "config", "", "config file (default $HOME/.godeez)") } diff --git a/internal/downloader/client.go b/internal/downloader/client.go index 8347d01..9905393 100644 --- a/internal/downloader/client.go +++ b/internal/downloader/client.go @@ -40,95 +40,89 @@ func New(appCtx *app.Context, resourceType string) *Client { } } -func (c *Client) Run(ctx context.Context, opts Options, ids []string) error { +func (c *Client) Run(ctx context.Context, opts Options, id string) error { var err error c.deezerClient, err = deezer.NewClient(ctx, c.appCtx) if err != nil { return err } - for _, id := range ids { + var resource deezer.Resource + switch c.resourceType { + case "album": + resource = &deezer.Album{} + case "playlist": + resource = &deezer.Playlist{} + default: + return fmt.Errorf("unsupported resource type: %s", c.resourceType) + } + + if err := c.deezerClient.FetchResource(ctx, resource, id); err != nil { + return fmt.Errorf("failed to fetch resource: %w", err) + } + + songs := resource.GetSongs() + if len(songs) == 0 { + return fmt.Errorf("%s has no songs", c.resourceType) + } + + outputDir := resource.GetOutputDir(opts.OutputDir) + if err := fileutil.EnsureDir(outputDir); err != nil { + return fmt.Errorf("failed to create output directory: %w", err) + } + + startTime := time.Now() + fmt.Printf("%s\n\nStarting download...\n\n", resource) + + downloaded := 0 + skipped := 0 + failed := 0 + + for i, song := range songs { if ctx.Err() != nil { return ctx.Err() } - downloaded := 0 - skipped := 0 - failed := 0 + trackProgress := fmt.Sprintf("[%d/%d]", i+1, len(songs)) - var resource deezer.Resource + sp := spinner.New(spinner.CharSets[14], 100*time.Millisecond) + sp.Writer = os.Stdout + sp.Prefix = trackProgress + " " + sp.Suffix = fmt.Sprintf(" Downloading: %s - %s", song.Artist, song.Title) + sp.Start() - switch c.resourceType { - case "album": - resource = &deezer.Album{} - case "playlist": - resource = &deezer.Playlist{} - default: - return fmt.Errorf("unsupported resource type: %s", c.resourceType) - } + warnings, err := c.downloadSong(ctx, resource, song, opts, outputDir) + sp.Stop() - if err := c.deezerClient.FetchResource(ctx, resource, id); err != nil { - return fmt.Errorf("failed to fetch resource: %w", err) - } - - songs := resource.GetSongs() - if len(songs) == 0 { - return fmt.Errorf("%s has no songs", c.resourceType) - } - - outputDir := resource.GetOutputDir(opts.OutputDir) - if err := fileutil.EnsureDir(outputDir); err != nil { - return fmt.Errorf("failed to create output directory: %w", err) - } - - startTime := time.Now() - fmt.Printf("%s\n\nStarting download...\n\n", resource) - - for i, song := range songs { - if ctx.Err() != nil { - return ctx.Err() + if err != nil { + if errors.Is(err, context.Canceled) { + return err } - trackProgress := fmt.Sprintf("[%d/%d]", i+1, len(songs)) - - sp := spinner.New(spinner.CharSets[14], 100*time.Millisecond) - sp.Writer = os.Stdout - sp.Prefix = trackProgress + " " - sp.Suffix = fmt.Sprintf(" Downloading: %s - %s", song.Artist, song.Title) - sp.Start() - - warnings, err := c.downloadSong(ctx, resource, song, opts, outputDir) - sp.Stop() - - if err != nil { - if errors.Is(err, context.Canceled) { - return err - } - - if path, ok := IsSkipError(err); ok { - skipped++ - fmt.Printf("%s ↷ Skipped: %s - %s\n Already exists at: %s\n", trackProgress, song.Artist, song.Title, path) - continue - } - - failed++ - fmt.Printf("%s ✖ Failed: %s - %s:\n Error: %v\n", trackProgress, song.Artist, song.Title, err) + if path, ok := IsSkipError(err); ok { + skipped++ + fmt.Printf("%s ↷ Skipped: %s - %s\n Already exists at: %s\n", trackProgress, song.Artist, song.Title, path) continue } - symbol := "✔" - if len(warnings) > 0 { - symbol = "⚠" - } - - downloaded++ - fmt.Printf("%s %s Downloaded: %s - %s\n", trackProgress, symbol, song.Artist, song.Title) - for _, w := range warnings { - fmt.Printf(" Warning: %s\n", w) - } + failed++ + fmt.Printf("%s ✖ Failed: %s - %s:\n Error: %v\n", trackProgress, song.Artist, song.Title, err) + continue } - fmt.Printf(` + symbol := "✔" + if len(warnings) > 0 { + symbol = "⚠" + } + + downloaded++ + fmt.Printf("%s %s Downloaded: %s - %s\n", trackProgress, symbol, song.Artist, song.Title) + for _, w := range warnings { + fmt.Printf(" Warning: %s\n", w) + } + } + + fmt.Printf(` ================== [ Summary ] ================== Downloaded: %d Skipped: %d @@ -137,14 +131,12 @@ Elapsed time: %s Files saved to: %s ================================================= `, - downloaded, - skipped, - failed, - time.Since(startTime).Round(time.Second), - outputDir, - ) - - } + downloaded, + skipped, + failed, + time.Since(startTime).Round(time.Second), + outputDir, + ) return nil }