diff --git a/app/application/spotlike/get_all_albums_by_artist_id_usecase.go b/app/application/spotlike/get_all_albums_by_artist_id_usecase.go index 0d21259..543a031 100644 --- a/app/application/spotlike/get_all_albums_by_artist_id_usecase.go +++ b/app/application/spotlike/get_all_albums_by_artist_id_usecase.go @@ -8,20 +8,20 @@ import ( "github.com/zmb3/spotify/v2" ) -// getAllAlbumsUseCase is a struct that contains the use case of getting for an artist. -type getAllAlbumsUseCase struct { +// getAllAlbumsByArtistIdUseCase is a struct that contains the use case of getting for an artist. +type getAllAlbumsByArtistIdUseCase struct { Client *spotify.Client } -// NewgetAllAlbumsUseCase returns a new instance of the getAllAlbumsUseCase struct. -func NewgetAllAlbumsUseCase(client *spotify.Client) *getAllAlbumsUseCase { - return &getAllAlbumsUseCase{ +// NewGetAllAlbumsByArtistIdUseCase returns a new instance of the getAllAlbumsUseCase struct. +func NewGetAllAlbumsByArtistIdUseCase(client *spotify.Client) *getAllAlbumsByArtistIdUseCase { + return &getAllAlbumsByArtistIdUseCase{ Client: client, } } -// GetAllAlbumsUseCaseOutputDto is a DTO struct that contains the output data of the getAllAlbumsUseCase. -type GetAllAlbumsUseCaseOutputDto struct { +// GetAllAlbumsByArtistIdUseCaseOutputDto is a DTO struct that contains the output data of the getAllAlbumsUseCase. +type GetAllAlbumsByArtistIdUseCaseOutputDto struct { ID string Artists string Name string @@ -29,7 +29,7 @@ type GetAllAlbumsUseCaseOutputDto struct { } // Run returns the get result of the artist. -func (uc *getAllAlbumsUseCase) Run(id string) ([]*GetAllAlbumsUseCaseOutputDto, error) { +func (uc *getAllAlbumsByArtistIdUseCase) Run(id string) ([]*GetAllAlbumsByArtistIdUseCaseOutputDto, error) { allAlbums, err := uc.Client.GetArtistAlbums(context.Background(), spotify.ID(id), nil) if err != nil { return nil, err @@ -38,21 +38,21 @@ func (uc *getAllAlbumsUseCase) Run(id string) ([]*GetAllAlbumsUseCaseOutputDto, return allAlbums.Albums[i].ReleaseDateTime().Before(allAlbums.Albums[j].ReleaseDateTime()) }) - var getAllAlbumsUseCaseOutputDtos []*GetAllAlbumsUseCaseOutputDto + var getAllAlbumsByArtistIdUseCaseOutputDtos []*GetAllAlbumsByArtistIdUseCaseOutputDto for _, album := range allAlbums.Albums { - getAllAlbumsUseCaseOutputDto := &GetAllAlbumsUseCaseOutputDto{ + getAllAlbumsUseCaseOutputDto := &GetAllAlbumsByArtistIdUseCaseOutputDto{ ID: string(album.ID), Artists: uc.combineArtistNames(album.Artists), Name: album.Name, ReleaseDate: album.ReleaseDateTime(), } - getAllAlbumsUseCaseOutputDtos = append(getAllAlbumsUseCaseOutputDtos, getAllAlbumsUseCaseOutputDto) + getAllAlbumsByArtistIdUseCaseOutputDtos = append(getAllAlbumsByArtistIdUseCaseOutputDtos, getAllAlbumsUseCaseOutputDto) } - return getAllAlbumsUseCaseOutputDtos, nil + return getAllAlbumsByArtistIdUseCaseOutputDtos, nil } -func (uc *getAllAlbumsUseCase) combineArtistNames(artists []spotify.SimpleArtist) string { +func (uc *getAllAlbumsByArtistIdUseCase) combineArtistNames(artists []spotify.SimpleArtist) string { var artistNames string for index, artist := range artists { artistNames += artist.Name diff --git a/app/application/spotlike/get_all_tracks_by_album_id_usecase.go b/app/application/spotlike/get_all_tracks_by_album_id_usecase.go new file mode 100644 index 0000000..c09bf0f --- /dev/null +++ b/app/application/spotlike/get_all_tracks_by_album_id_usecase.go @@ -0,0 +1,71 @@ +package spotlike + +import ( + "context" + "sort" + "strconv" + "time" + + "github.com/zmb3/spotify/v2" +) + +// getAllTracksByAlbumIdUseCase is a struct that contains the use case of getting for an artist. +type getAllTracksByAlbumIdUseCase struct { + Client *spotify.Client +} + +// NewGetAllTracksByAlbumIdUseCase returns a new instance of the getAllTracksUseCase struct. +func NewGetAllTracksByAlbumIdUseCase(client *spotify.Client) *getAllTracksByAlbumIdUseCase { + return &getAllTracksByAlbumIdUseCase{ + Client: client, + } +} + +// GetAllTracksByAlbumIdUseCaseOutputDto is a DTO struct that contains the output data of the getAllTracksUseCase. +type GetAllTracksByAlbumIdUseCaseOutputDto struct { + ID string + Artists string + Album string + Name string + TrackNumber string + ReleaseDate time.Time +} + +// Run returns the get result of the artist. +func (uc *getAllTracksByAlbumIdUseCase) Run(id string) ([]*GetAllTracksByAlbumIdUseCaseOutputDto, error) { + var getAllTracksByAlbumIdUseCaseOutputDtos []*GetAllTracksByAlbumIdUseCaseOutputDto + allTracks, err := uc.Client.GetAlbumTracks(context.Background(), spotify.ID(id), nil) + if err != nil { + return nil, err + } + sort.Slice(allTracks.Tracks, func(i, j int) bool { + return allTracks.Tracks[i].TrackNumber < allTracks.Tracks[j].TrackNumber + }) + + for _, track := range allTracks.Tracks { + getAllTracksByAlbumIdUseCaseOutputDto := &GetAllTracksByAlbumIdUseCaseOutputDto{ + ID: string(track.ID), + Artists: uc.combineArtistNames(track.Artists), + Album: track.Album.Name, + Name: track.Name, + TrackNumber: strconv.Itoa(track.TrackNumber), + ReleaseDate: track.Album.ReleaseDateTime(), + } + getAllTracksByAlbumIdUseCaseOutputDtos = append(getAllTracksByAlbumIdUseCaseOutputDtos, getAllTracksByAlbumIdUseCaseOutputDto) + } + + return getAllTracksByAlbumIdUseCaseOutputDtos, nil +} + +func (uc *getAllTracksByAlbumIdUseCase) combineArtistNames(artists []spotify.SimpleArtist) string { + var artistNames string + for index, artist := range artists { + artistNames += artist.Name + if index+1 != len(artists) { + // if the arg is not the last one, add a comma and a space + artistNames += ", " + } + } + + return artistNames +} diff --git a/app/application/spotlike/get_all_tracks_by_artist_id_usecase.go b/app/application/spotlike/get_all_tracks_by_artist_id_usecase.go new file mode 100644 index 0000000..8e6e118 --- /dev/null +++ b/app/application/spotlike/get_all_tracks_by_artist_id_usecase.go @@ -0,0 +1,81 @@ +package spotlike + +import ( + "context" + "sort" + "strconv" + "time" + + "github.com/zmb3/spotify/v2" +) + +// getAllTracksByArtistIdUseCase is a struct that contains the use case of getting for an artist. +type getAllTracksByArtistIdUseCase struct { + Client *spotify.Client +} + +// NewGetAllTracksByArtistIdUseCase returns a new instance of the getAllTracksUseCase struct. +func NewGetAllTracksByArtistIdUseCase(client *spotify.Client) *getAllTracksByArtistIdUseCase { + return &getAllTracksByArtistIdUseCase{ + Client: client, + } +} + +// GetAllTracksByArtistIdUseCaseOutputDto is a DTO struct that contains the output data of the getAllTracksUseCase. +type GetAllTracksByArtistIdUseCaseOutputDto struct { + ID string + Artists string + Album string + Name string + TrackNumber string + ReleaseDate time.Time +} + +// Run returns the get result of the artist. +func (uc *getAllTracksByArtistIdUseCase) Run(id string) ([]*GetAllTracksByAlbumIdUseCaseOutputDto, error) { + allAlbums, err := uc.Client.GetArtistAlbums(context.Background(), spotify.ID(id), nil) + if err != nil { + return nil, err + } + sort.Slice(allAlbums.Albums, func(i, j int) bool { + return allAlbums.Albums[i].ReleaseDateTime().Before(allAlbums.Albums[j].ReleaseDateTime()) + }) + + var getAllTracksByArtistIdUseCaseOutputDtos []*GetAllTracksByAlbumIdUseCaseOutputDto + for _, album := range allAlbums.Albums { + allTracks, err := uc.Client.GetAlbumTracks(context.Background(), album.ID) + if err != nil { + return nil, err + } + sort.Slice(allTracks.Tracks, func(i, j int) bool { + return allTracks.Tracks[i].TrackNumber < allTracks.Tracks[j].TrackNumber + }) + + for _, track := range allTracks.Tracks { + getAllTracksByArtistIdUseCaseOutputDto := &GetAllTracksByAlbumIdUseCaseOutputDto{ + ID: string(track.ID), + Artists: uc.combineArtistNames(track.Artists), + Album: album.Name, + Name: track.Name, + TrackNumber: strconv.Itoa(track.TrackNumber), + ReleaseDate: track.Album.ReleaseDateTime(), + } + getAllTracksByArtistIdUseCaseOutputDtos = append(getAllTracksByArtistIdUseCaseOutputDtos, getAllTracksByArtistIdUseCaseOutputDto) + } + } + + return getAllTracksByArtistIdUseCaseOutputDtos, nil +} + +func (uc *getAllTracksByArtistIdUseCase) combineArtistNames(artists []spotify.SimpleArtist) string { + var artistNames string + for index, artist := range artists { + artistNames += artist.Name + if index+1 != len(artists) { + // if the arg is not the last one, add a comma and a space + artistNames += ", " + } + } + + return artistNames +} diff --git a/app/application/spotlike/get_content_type_usecase.go b/app/application/spotlike/get_content_type_usecase.go index e8354f0..e8b67a7 100644 --- a/app/application/spotlike/get_content_type_usecase.go +++ b/app/application/spotlike/get_content_type_usecase.go @@ -3,6 +3,7 @@ package spotlike import ( "context" "errors" + "strconv" "time" "github.com/zmb3/spotify/v2" @@ -13,8 +14,8 @@ type getContentTypeUseCase struct { Client *spotify.Client } -// NewgetContentTypeUseCase returns a new instance of the getContentTypeUseCase struct. -func NewgetContentTypeUseCase(client *spotify.Client) *getContentTypeUseCase { +// NewGetContentTypeUseCase returns a new instance of the getContentTypeUseCase struct. +func NewGetContentTypeUseCase(client *spotify.Client) *getContentTypeUseCase { return &getContentTypeUseCase{ Client: client, } @@ -27,6 +28,7 @@ type GetContentTypeUseCaseOutputDto struct { Name string Artists string Album string + TrackNumber string ReleaseDate time.Time } @@ -77,6 +79,7 @@ func (uc *getContentTypeUseCase) Run(id string) (*GetContentTypeUseCaseOutputDto Name: resultTrack.Name, Artists: uc.combineArtistNames(resultTrack.Artists), Album: resultTrack.Album.Name, + TrackNumber: strconv.Itoa(resultTrack.TrackNumber), ReleaseDate: resultTrack.Album.ReleaseDateTime(), }, nil } diff --git a/app/application/spotlike/like_track_usecase.go b/app/application/spotlike/like_track_usecase.go new file mode 100644 index 0000000..c1dc5d4 --- /dev/null +++ b/app/application/spotlike/like_track_usecase.go @@ -0,0 +1,24 @@ +package spotlike + +import ( + "context" + + "github.com/zmb3/spotify/v2" +) + +// likeTrackUseCase is a struct that contains the use case of likeing for an artist. +type likeTrackUseCase struct { + Client *spotify.Client +} + +// NewLikeTrackUseCase returns a new instance of the likeTrackUseCase struct. +func NewLikeTrackUseCase(client *spotify.Client) *likeTrackUseCase { + return &likeTrackUseCase{ + Client: client, + } +} + +// Run returns the like result of the artist. +func (uc *likeTrackUseCase) Run(id string) error { + return uc.Client.AddTracksToLibrary(context.Background(), spotify.ID(id)) +} diff --git a/app/application/spotlike/search_track_usecase.go b/app/application/spotlike/search_track_usecase.go index 7fa2646..872b6b7 100644 --- a/app/application/spotlike/search_track_usecase.go +++ b/app/application/spotlike/search_track_usecase.go @@ -2,6 +2,7 @@ package spotlike import ( "context" + "strconv" "time" "github.com/zmb3/spotify/v2" @@ -25,6 +26,7 @@ type SearchTrackUseCaseOutputDto struct { Artists string Album string Name string + TrackNumber string ReleaseDate time.Time } @@ -44,6 +46,7 @@ func (uc *searchTrackUseCase) Run(keywords []string, max int) ([]*SearchTrackUse Artists: uc.combineArtistNames(t.Artists), Album: t.Album.Name, Name: t.Name, + TrackNumber: strconv.Itoa(t.TrackNumber), ReleaseDate: t.Album.ReleaseDateTime(), }) } diff --git a/app/presentation/cli/spotlike/command/spotlike/completion/completion.go b/app/presentation/cli/spotlike/command/spotlike/completion/completion.go index 66ff36d..67bfabc 100644 --- a/app/presentation/cli/spotlike/command/spotlike/completion/completion.go +++ b/app/presentation/cli/spotlike/command/spotlike/completion/completion.go @@ -44,5 +44,7 @@ Available Subommands: Flags: -h, --help 🤝 help for completion + +Use "spotlike completion [command] --help" for more information about a command. ` ) diff --git a/app/presentation/cli/spotlike/command/spotlike/like/album.go b/app/presentation/cli/spotlike/command/spotlike/like/album.go index c938224..cdeea4d 100644 --- a/app/presentation/cli/spotlike/command/spotlike/like/album.go +++ b/app/presentation/cli/spotlike/command/spotlike/like/album.go @@ -42,7 +42,7 @@ func NewAlbumCommand( cmd.Flags().StringVarP( &LikeAlbumOps.Artist, "artist", - "a", + "A", "", "🆔 an ID of the artist to like all albums released by the artist", ) @@ -51,7 +51,7 @@ func NewAlbumCommand( "no-confirm", "", false, - "🚫 do not confirm before liking the artist", + "🚫 do not confirm before liking the album", ) cmd.Flags().StringVarP( &LikeAlbumOps.Format, @@ -82,7 +82,7 @@ func runAlbum(output *string, args []string, conf *config.SpotlikeCliConfig) err return err } - gctuc := spotlikeApp.NewgetContentTypeUseCase(client) + gctuc := spotlikeApp.NewGetContentTypeUseCase(client) var albums []*spotlikeApp.GetContentTypeUseCaseOutputDto if LikeAlbumOps.Artist != "" { gctucDto, err := gctuc.Run(LikeAlbumOps.Artist) @@ -96,7 +96,7 @@ func runAlbum(output *string, args []string, conf *config.SpotlikeCliConfig) err return nil } - gaauc := spotlikeApp.NewgetAllAlbumsUseCase(client) + gaauc := spotlikeApp.NewGetAllAlbumsByArtistIdUseCase(client) allAlbums, err := gaauc.Run(LikeAlbumOps.Artist) if err != nil { return err @@ -138,7 +138,7 @@ func runAlbum(output *string, args []string, conf *config.SpotlikeCliConfig) err return err } if alreadyLiked { - presenter.Print(os.Stdout, formatter.Yellow("⚡ Album "+album.Name+" ("+album.ID+") "+"is already liked. skipping...")) + presenter.Print(os.Stdout, formatter.Blue("⏩ Album "+album.Name+" ("+album.ID+")"+" by "+album.Artists+" is already liked. skipping...")) continue } @@ -176,14 +176,14 @@ func runAlbum(output *string, args []string, conf *config.SpotlikeCliConfig) err } const ( - likeAlbumHelpTemplate = `🤍📀 Like albums on Spotify by ID. + likeAlbumHelpTemplate = `🤍💿 Like albums on Spotify by ID. -You can like albums on Spotify by ID. +You can like tracks on Spotify by ID. Before using this command, -you need to get the ID of the album you want to like by using the search command. +you need to get the ID of the track you want to like by using the search command. -Also, you can like all albums of the artist by specifying the ID of the artist with artist flag. +Also, you can like all albums released by the artist with specifying the ID of the artist with artist flag. If you specify artist flag, the arguments would be ignored. ` + likeAlbumUsageTemplate @@ -193,8 +193,8 @@ If you specify artist flag, the arguments would be ignored. spotlike like a [flags] [arguments] Flags: - -a, --artist 🆔 an ID of the artist to like all albums released by the artist - --no-confirm 🚫 do not confirm before liking the artist + -A, --artist 🆔 an ID of the artist to like all albums released by the artist + --no-confirm 🚫 do not confirm before liking the album -f, --format 📝 format of the output (default "table", e.g: "plain") Arguments: diff --git a/app/presentation/cli/spotlike/command/spotlike/like/artist.go b/app/presentation/cli/spotlike/command/spotlike/like/artist.go index 39423e4..10aa0f6 100644 --- a/app/presentation/cli/spotlike/command/spotlike/like/artist.go +++ b/app/presentation/cli/spotlike/command/spotlike/like/artist.go @@ -74,7 +74,7 @@ func runArtist(output *string, args []string, conf *config.SpotlikeCliConfig) er } var artists []*spotlikeApp.GetContentTypeUseCaseOutputDto - gctuc := spotlikeApp.NewgetContentTypeUseCase(client) + gctuc := spotlikeApp.NewGetContentTypeUseCase(client) for _, id := range args { gctucDto, err := gctuc.Run(id) if err != nil { @@ -99,7 +99,7 @@ func runArtist(output *string, args []string, conf *config.SpotlikeCliConfig) er return err } if alreadyLiked { - presenter.Print(os.Stdout, formatter.Yellow("⚡ Artist "+artist.Name+" ("+artist.ID+") "+"is already liked. skipping...")) + presenter.Print(os.Stdout, formatter.Blue("⏩ Artist "+artist.Name+" ("+artist.ID+") "+"is already liked. skipping...")) continue } diff --git a/app/presentation/cli/spotlike/command/spotlike/like/like.go b/app/presentation/cli/spotlike/command/spotlike/like/like.go index 5eab4e1..c5f2994 100644 --- a/app/presentation/cli/spotlike/command/spotlike/like/like.go +++ b/app/presentation/cli/spotlike/command/spotlike/like/like.go @@ -32,6 +32,11 @@ func NewLikeCommand( conf, output, ), + NewTrackCommand( + cobra, + conf, + output, + ), ) cmd.SetRunE( diff --git a/app/presentation/cli/spotlike/command/spotlike/like/track.go b/app/presentation/cli/spotlike/command/spotlike/like/track.go index b887788..d93500a 100644 --- a/app/presentation/cli/spotlike/command/spotlike/like/track.go +++ b/app/presentation/cli/spotlike/command/spotlike/like/track.go @@ -1 +1,254 @@ package like + +import ( + "os" + + c "github.com/spf13/cobra" + + spotlikeApp "github.com/yanosea/spotlike/app/application/spotlike" + "github.com/yanosea/spotlike/app/presentation/cli/spotlike/command/spotlike" + "github.com/yanosea/spotlike/app/presentation/cli/spotlike/config" + "github.com/yanosea/spotlike/app/presentation/cli/spotlike/formatter" + "github.com/yanosea/spotlike/app/presentation/cli/spotlike/presenter" + + "github.com/yanosea/spotlike/pkg/proxy" +) + +type LikeTrackOptions struct { + Artist string + Album string + NoConfirm bool + Format string +} + +var ( + LikeTrackOps = LikeTrackOptions{ + Artist: "", + Album: "", + NoConfirm: false, + Format: "table", + } +) + +func NewTrackCommand( + cobra proxy.Cobra, + conf *config.SpotlikeCliConfig, + output *string, +) proxy.Command { + cmd := cobra.NewCommand() + cmd.SetUse("track") + cmd.SetAliases([]string{"tr", "t"}) + cmd.SetUsageTemplate(likeTrackUsageTemplate) + cmd.SetHelpTemplate(likeTrackHelpTemplate) + cmd.SetSilenceErrors(true) + cmd.Flags().StringVarP( + &LikeTrackOps.Artist, + "artist", + "A", + "", + "🆔 an ID of the artist to like all albums released by the artist", + ) + cmd.Flags().StringVarP( + &LikeTrackOps.Album, + "album", + "a", + "", + "🆔 an ID of the album to like all tracks in the album", + ) + cmd.Flags().BoolVarP( + &LikeTrackOps.NoConfirm, + "no-confirm", + "", + false, + "🚫 do not confirm before liking the track", + ) + cmd.Flags().StringVarP( + &LikeTrackOps.Format, + "format", + "f", + "table", + "📝 format of the output (default \"table\", e.g: \"plain\")", + ) + + cmd.SetRunE( + func(_ *c.Command, args []string) error { + return runTrack(output, args, conf) + }, + ) + + return cmd +} + +func runTrack(output *string, args []string, conf *config.SpotlikeCliConfig) error { + if LikeTrackOps.Artist != "" && LikeTrackOps.Album != "" { + o := formatter.Yellow("⚡ Both artist and album flags can not be specified at the same time...") + *output = o + return nil + } + + if LikeTrackOps.Artist == "" && LikeTrackOps.Album == "" && len(args) == 0 { + o := formatter.Yellow("⚡ No ID arguments specified...") + *output = o + return nil + } + + client, err := spotlike.Auth(conf, output, false) + if err != nil { + return err + } + + gctuc := spotlikeApp.NewGetContentTypeUseCase(client) + var tracks []*spotlikeApp.GetContentTypeUseCaseOutputDto + if LikeTrackOps.Artist != "" { + gctucDto, err := gctuc.Run(LikeTrackOps.Artist) + if err != nil { + return err + } + + if gctucDto.Type != spotlikeApp.Artist { + o := formatter.Yellow("⚡ The id " + gctucDto.Name + " is not an artist. skipping...") + *output = o + return nil + } + + gatAuc := spotlikeApp.NewGetAllTracksByArtistIdUseCase(client) + allTracks, err := gatAuc.Run(LikeTrackOps.Artist) + if err != nil { + return err + } + + for _, track := range allTracks { + album := &spotlikeApp.GetContentTypeUseCaseOutputDto{ + ID: track.ID, + Type: spotlikeApp.Track, + Name: track.Name, + Artists: track.Artists, + Album: track.Album, + TrackNumber: track.TrackNumber, + ReleaseDate: track.ReleaseDate, + } + tracks = append(tracks, album) + } + } else if LikeTrackOps.Album != "" { + gctucDto, err := gctuc.Run(LikeTrackOps.Album) + if err != nil { + return err + } + + if gctucDto.Type != spotlikeApp.Album { + o := formatter.Yellow("⚡ The id " + gctucDto.Name + " is not an album. skipping...") + *output = o + return nil + } + + gatauc := spotlikeApp.NewGetAllTracksByAlbumIdUseCase(client) + allTracks, err := gatauc.Run(LikeTrackOps.Album) + if err != nil { + return err + } + + for _, track := range allTracks { + album := &spotlikeApp.GetContentTypeUseCaseOutputDto{ + ID: track.ID, + Type: spotlikeApp.Track, + Name: track.Name, + Artists: track.Artists, + Album: track.Album, + TrackNumber: track.TrackNumber, + ReleaseDate: track.ReleaseDate, + } + tracks = append(tracks, album) + } + } else { + for _, id := range args { + gctucDto, err := gctuc.Run(id) + if err != nil { + return err + } + + if gctucDto.Type != spotlikeApp.Track { + o := formatter.Yellow("⚡ The id " + gctucDto.Name + " is not a track. skipping...") + *output = o + continue + } + + tracks = append(tracks, gctucDto) + } + } + + cfuc := spotlikeApp.NewCheckLikeUseCase(client) + lauc := spotlikeApp.NewLikeTrackUseCase(client) + var likeExecutedTracks []*spotlikeApp.GetContentTypeUseCaseOutputDto + for _, track := range tracks { + alreadyLiked, err := cfuc.Run(track.ID, spotlikeApp.Track) + if err != nil { + return err + } + if alreadyLiked { + presenter.Print(os.Stdout, formatter.Blue("⏩ Track #"+track.TrackNumber+" "+track.Name+" ("+track.ID+")"+" on "+track.Album+" by "+track.Artists+" is already liked. skipping...")) + continue + } + + if !LikeTrackOps.NoConfirm { + if answer, err := presenter.RunPrompt( + "Proceed with liking " + track.Name + " (" + track.ID + ") ? [y/N]", + ); err != nil { + return err + } else if answer != "y" && answer != "Y" { + o := formatter.Yellow("🚫 Cancelled liking album " + track.Name + " (" + track.ID + ") ...") + *output = o + continue + } + } + + if err := lauc.Run(track.ID); err != nil { + return err + } + + likeExecutedTracks = append(likeExecutedTracks, track) + } + + if len(likeExecutedTracks) != 0 { + f, err := formatter.NewFormatter(LikeTrackOps.Format) + if err != nil { + o := formatter.Red("❌ Failed to create a formatter...") + *output = o + return err + } + o := "\n" + f.Format(likeExecutedTracks) + *output = o + } + + return nil +} + +const ( + likeTrackHelpTemplate = `🤍🎵 Like tracks on Spotify by ID. + +You can like tracks on Spotify by ID. + +Before using this command, +you need to get the ID of the album you want to like by using the search command. + +Also, you can like all tracks released by the artist with specifying the ID of the artist with artist flag. +If you specify artist flag, the arguments would be ignored. +Also, you can like all tracks in the album with specifying the ID of the album with album flag. +If you specify album flag, the arguments would be ignored. +Both artist and album flags can not be specified at the same time. + +` + likeTrackUsageTemplate + likeTrackUsageTemplate = `Usage: + spotlike like track [flags] [arguments] + spotlike like tr [flags] [arguments] + spotlike like t [flags] [arguments] + +Flags: + -A, --artist 🆔 an ID of the artist to like all albums released by the artist + -a, --album 🆔 an ID of the album to like all tracks in the album + --no-confirm 🚫 do not confirm before liking the track + -f, --format 📝 format of the output (default "table", e.g: "plain") + +Arguments: + ID 🆔 ID of the tracks (e.g: " ") +` +) diff --git a/app/presentation/cli/spotlike/formatter/plain.go b/app/presentation/cli/spotlike/formatter/plain.go index e7a41eb..13a2826 100644 --- a/app/presentation/cli/spotlike/formatter/plain.go +++ b/app/presentation/cli/spotlike/formatter/plain.go @@ -68,8 +68,10 @@ func (f *PlainFormatter) Format(result interface{}) string { } case spotlikeApp.Track: for i, item := range v { - formatted += "Liked track : " + item.Name + formatted += "Liked track : #" + item.TrackNumber + formatted += "\t" + item.Name formatted += "\t" + item.ID + formatted += "\t on album " + item.Album formatted += "\t released at" + item.ReleaseDate.Format("2006-01-02") formatted += "\t by " + item.Artists if i < len(v)-1 { diff --git a/app/presentation/cli/spotlike/formatter/table.go b/app/presentation/cli/spotlike/formatter/table.go index f2d4eca..ff510f0 100644 --- a/app/presentation/cli/spotlike/formatter/table.go +++ b/app/presentation/cli/spotlike/formatter/table.go @@ -66,7 +66,7 @@ func (f *TableFormatter) formatSearchArtist(items []*spotlikeApp.SearchArtistUse } func (f *TableFormatter) formatSearchAlbum(items []*spotlikeApp.SearchAlbumUseCaseOutputDto) tableData { - header := []string{"🆔 ID", "📀 Album", "🎤 Artists", "📅 Release Date"} + header := []string{"🆔 ID", "💿 Album", "🎤 Artists", "📅 Release Date"} var rows [][]string for _, album := range items { @@ -84,12 +84,13 @@ func (f *TableFormatter) formatSearchAlbum(items []*spotlikeApp.SearchAlbumUseCa } func (f *TableFormatter) formatSearchTrack(items []*spotlikeApp.SearchTrackUseCaseOutputDto) tableData { - header := []string{"🆔 ID", "🎵 Track", "📀 Album", "🎤 Artists", "📅 Release Date"} + header := []string{"🆔 ID", "🔢 Number", "🎵 Track", "💿 Album", "🎤 Artists", "📅 Release Date"} var rows [][]string for _, track := range items { rows = append(rows, []string{ track.ID, + track.TrackNumber, track.Name, track.Album, track.Artists, @@ -116,7 +117,7 @@ func (f *TableFormatter) formatLikedResult(items []*spotlikeApp.GetContentTypeUs } rows = f.addTotalRow(rows, "artists like succeeded") case spotlikeApp.Album: - header = []string{"🆔 ID", "📀 Album", "🎤 Artists", "📅 Release Date"} + header = []string{"🆔 ID", "💿 Album", "🎤 Artists", "📅 Release Date"} for _, item := range items { rows = append(rows, []string{ item.ID, @@ -127,10 +128,11 @@ func (f *TableFormatter) formatLikedResult(items []*spotlikeApp.GetContentTypeUs } rows = f.addTotalRow(rows, "albums like succeeded") case spotlikeApp.Track: - header = []string{"🆔 ID", "🎵 Track", "📀 Album", "🎤 Artists", "📅 Release Date"} + header = []string{"🆔 ID", "🔢 Number", "🎵 Track", "💿 Album", "🎤 Artists", "📅 Release Date"} for _, item := range items { rows = append(rows, []string{ item.ID, + item.TrackNumber, item.Name, item.Album, item.Artists,