From 2733406c9c8c85ccbc4329dcebb3330a1ec779d2 Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Sun, 1 Aug 2021 18:41:54 -0600 Subject: [PATCH] fix playlist bug --- go.mod | 2 + internal/back/back.go | 5 ++- internal/back/back_test.go | 8 ++-- internal/common.go | 46 ++++++++++++++++++--- internal/errors.go | 23 ++++++----- internal/login/login.go | 1 + internal/mock_api.go | 56 ++++++++++++++------------ internal/next/next.go | 3 +- internal/next/next_test.go | 6 ++- internal/p/p.go | 32 ++++++--------- internal/p/p_test.go | 28 +++++++------ internal/pause/pause.go | 13 ++---- internal/pause/pause_test.go | 19 ++------- internal/play/play.go | 73 +++++++++++----------------------- internal/play/play_test.go | 33 ++++++--------- internal/playlist/show.go | 24 +++-------- internal/queue/queue.go | 2 +- internal/save/save_test.go | 15 +------ internal/status/status_test.go | 14 +++---- internal/unsave/unsave_test.go | 6 +-- 20 files changed, 187 insertions(+), 222 deletions(-) diff --git a/go.mod b/go.mod index 3d8ef2b..80f3d75 100644 --- a/go.mod +++ b/go.mod @@ -12,3 +12,5 @@ require ( github.com/spf13/viper v1.8.1 github.com/stretchr/testify v1.7.0 ) + +replace github.com/brianstrauch/spotify => /Users/brianstrauch/Documents/go/src/github.com/brianstrauch/spotify \ No newline at end of file diff --git a/internal/back/back.go b/internal/back/back.go index 8c96db0..79043fc 100644 --- a/internal/back/back.go +++ b/internal/back/back.go @@ -42,15 +42,16 @@ func back(api internal.APIInterface) (string, error) { } id := playback.Item.ID + progressMs := playback.ProgressMs if err := api.SkipToPreviousTrack(); err != nil { - if err.Error() == internal.ErrRestrictionViolated { + if err.Error() == "Player command failed: Restriction violated" { return "", errors.New(internal.ErrNoPrevious) } } playback, err = internal.WaitForUpdatedPlayback(api, func(playback *spotify.Playback) bool { - return playback.Item.ID != id + return playback.Item.ID != id || playback.ProgressMs < progressMs }) if err != nil { return "", err diff --git a/internal/back/back_test.go b/internal/back/back_test.go index ef8b28c..bb6d300 100644 --- a/internal/back/back_test.go +++ b/internal/back/back_test.go @@ -18,12 +18,14 @@ func TestBack(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ - Meta: spotify.Meta{ID: "1"}, + Meta: spotify.Meta{ + ID: "1", + Type: "track", + }, Name: "Track", Artists: []spotify.Artist{{Name: "Artist"}}, Duration: &spotify.Duration{Duration: time.Second}, }, - Type: "track", }, } @@ -43,7 +45,7 @@ func TestBack(t *testing.T) { func TestBack_ErrNoPrevious(t *testing.T) { api := new(internal.MockAPI) api.On("GetPlayback").Return(new(spotify.Playback), nil) - api.On("SkipToPreviousTrack").Return(errors.New(internal.ErrRestrictionViolated)) + api.On("SkipToPreviousTrack").Return(errors.New("Player command failed: Restriction violated")) _, err := back(api) require.Error(t, err) diff --git a/internal/common.go b/internal/common.go index a3b53c2..fde4cc7 100644 --- a/internal/common.go +++ b/internal/common.go @@ -2,6 +2,7 @@ package internal import ( "errors" + "strings" "time" "github.com/brianstrauch/spotify" @@ -46,14 +47,14 @@ func SaveToken(token *spotify.Token) error { return viper.WriteConfig() } -func WaitForUpdatedPlayback(api APIInterface, isUpdated func(playback *spotify.Playback) bool) (*spotify.Playback, error) { +func WaitForUpdatedPlayback(api APIInterface, isUpdated func(*spotify.Playback) bool) (*spotify.Playback, error) { timeout := time.After(time.Second) tick := time.NewTicker(100 * time.Millisecond) for { select { case <-timeout: - return nil, errors.New("request timed out") + return nil, errors.New(ErrRequestTimedOut) case <-tick.C: playback, err := api.GetPlayback() if err != nil { @@ -67,10 +68,45 @@ func WaitForUpdatedPlayback(api APIInterface, isUpdated func(playback *spotify.P } } -func Search(api APIInterface, query, searchType string) (*spotify.Track, error) { - page, err := api.Search(query, searchType, 1) +func SearchTrack(api APIInterface, query string) (*spotify.Track, error) { + paging, err := api.Search(query, "track", 1) if err != nil { return nil, err } - return page.Tracks.Items[0], nil + + tracks := paging.Tracks.Items + if len(tracks) == 0 { + return nil, errors.New(ErrTrackNotFound) + } + + return paging.Tracks.Items[0], nil +} + +func SearchAlbum(api APIInterface, query string) (*spotify.Album, error) { + paging, err := api.Search(query, "album", 1) + if err != nil { + return nil, err + } + + albums := paging.Albums.Items + if len(albums) == 0 { + return nil, errors.New(ErrAlbumNotFound) + } + + return albums[0], nil +} + +func SearchPlaylist(api APIInterface, query string) (*spotify.Playlist, error) { + playlists, err := api.GetPlaylists() + if err != nil { + return nil, err + } + + for _, playlist := range playlists { + if strings.EqualFold(playlist.Name, query) { + return playlist, nil + } + } + + return nil, errors.New(ErrPlaylistNotFound) } diff --git a/internal/errors.go b/internal/errors.go index 83be7df..7879ebc 100644 --- a/internal/errors.go +++ b/internal/errors.go @@ -1,14 +1,17 @@ package internal const ( - ErrAlreadyUpToDate = "Already up to date" - ErrLoginFailed = "Login failed" - ErrNoActiveDevice = "Player command failed: No active device found" - ErrNoAlbums = "No albums found" - ErrNoDevices = "No devices found" - ErrNoPlaylists = "No playlists found" - ErrNoPrevious = "No track before this one" - ErrNotLoggedIn = `You are not logged in: Run "spotify login"` - ErrRestrictionViolated = "Player command failed: Restriction violated" - ErrSavePodcast = "Saving podcasts is not allowed" + ErrAlbumNotFound = "album not found" + ErrAlreadyUpToDate = "already up to date" + ErrInvalidPlayArgs = "you may only pass args, --album, or --playlist" + ErrLoginFailed = "login failed" + ErrNoActiveDevice = "no active spotify session found" + ErrNoDevices = "no devices found" + ErrNoPlaylists = "no playlists found" + ErrNoPrevious = "no track before this one" + ErrNotLoggedIn = `you are not logged in, run "spotify login"` + ErrPlaylistNotFound = "playlist not found" + ErrRequestTimedOut = "request timed out" + ErrSavePodcast = "saving podcasts is not allowed" + ErrTrackNotFound = "track not found" ) diff --git a/internal/login/login.go b/internal/login/login.go index 4664540..daf2023 100644 --- a/internal/login/login.go +++ b/internal/login/login.go @@ -68,6 +68,7 @@ func login() (*spotify.Token, error) { } scopes := []string{ + spotify.ScopePlaylistReadPrivate, spotify.ScopeUserLibraryModify, spotify.ScopeUserModifyPlaybackState, spotify.ScopeUserReadPlaybackState, diff --git a/internal/mock_api.go b/internal/mock_api.go index 0b38676..2aa62cc 100644 --- a/internal/mock_api.go +++ b/internal/mock_api.go @@ -11,14 +11,16 @@ type APIInterface interface { GetPlayback() (*spotify.Playback, error) GetDevices() ([]*spotify.Device, error) - Play(deviceID, contextURI string, uris ...string) error - Pause(deviceID string) error + Play(contextURI string, uris ...string) error + Pause() error SkipToNextTrack() error SkipToPreviousTrack() error Repeat(state string) error Shuffle(state bool) error Queue(uri string) error + GetPlaylists() ([]*spotify.Playlist, error) + Search(q, searchType string, limit int) (*spotify.Paging, error) } @@ -27,13 +29,11 @@ type MockAPI struct { } func (m *MockAPI) SaveTracks(ids ...string) error { - args := m.Called(ids) - return args.Error(0) + return m.Called(ids).Error(0) } func (m *MockAPI) RemoveSavedTracks(ids ...string) error { - args := m.Called(ids) - return args.Error(0) + return m.Called(ids).Error(0) } func (m *MockAPI) GetPlayback() (*spotify.Playback, error) { @@ -58,50 +58,56 @@ func (m *MockAPI) GetDevices() ([]*spotify.Device, error) { return devices.([]*spotify.Device), err } -func (m *MockAPI) Play(deviceID, contextURI string, uris ...string) error { - args := m.Called(deviceID, contextURI, uris) - return args.Error(0) +func (m *MockAPI) Play(contextURI string, uris ...string) error { + return m.Called(contextURI, uris).Error(0) } -func (m *MockAPI) Pause(deviceID string) error { - args := m.Called(deviceID) - return args.Error(0) +func (m *MockAPI) Pause() error { + return m.Called().Error(0) } func (m *MockAPI) SkipToNextTrack() error { - args := m.Called() - return args.Error(0) + return m.Called().Error(0) } func (m *MockAPI) SkipToPreviousTrack() error { - args := m.Called() - return args.Error(0) + return m.Called().Error(0) } func (m *MockAPI) Repeat(state string) error { - args := m.Called(state) - return args.Error(0) + return m.Called(state).Error(0) } func (m *MockAPI) Shuffle(state bool) error { - args := m.Called(state) - return args.Error(0) + return m.Called(state).Error(0) } func (m *MockAPI) Queue(uri string) error { - args := m.Called(uri) - return args.Error(0) + return m.Called(uri).Error(0) +} + +func (m *MockAPI) GetPlaylists() ([]*spotify.Playlist, error) { + args := m.Called() + + playlists := args.Get(0) + err := args.Error(1) + + if playlists == nil { + return nil, err + } + + return playlists.([]*spotify.Playlist), err } func (m *MockAPI) Search(q, searchType string, limit int) (*spotify.Paging, error) { args := m.Called(q, searchType, limit) - page := args.Get(0) + paging := args.Get(0) err := args.Error(1) - if page == nil { + if paging == nil { return nil, err } - return page.(*spotify.Paging), err + return paging.(*spotify.Paging), err } diff --git a/internal/next/next.go b/internal/next/next.go index 72789b1..f348564 100644 --- a/internal/next/next.go +++ b/internal/next/next.go @@ -41,15 +41,14 @@ func next(api internal.APIInterface) (string, error) { return "", errors.New(internal.ErrNoActiveDevice) } - progressMs := playback.ProgressMs id := playback.Item.ID + progressMs := playback.ProgressMs if err := api.SkipToNextTrack(); err != nil { return "", err } playback, err = internal.WaitForUpdatedPlayback(api, func(playback *spotify.Playback) bool { - // TODO: Handle edge case where the next song is the same and progressMs = 0 return playback.Item.ID != id || playback.ProgressMs < progressMs }) if err != nil { diff --git a/internal/next/next_test.go b/internal/next/next_test.go index bbceffb..6f757a5 100644 --- a/internal/next/next_test.go +++ b/internal/next/next_test.go @@ -17,12 +17,14 @@ func TestNext(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ - Meta: spotify.Meta{ID: "0"}, + Meta: spotify.Meta{ + ID: "0", + Type: "track", + }, Name: "Track", Artists: []spotify.Artist{{Name: "Artist"}}, Duration: &spotify.Duration{Duration: time.Second}, }, - Type: "track", }, } diff --git a/internal/p/p.go b/internal/p/p.go index 8d7a8bd..6777b4c 100644 --- a/internal/p/p.go +++ b/internal/p/p.go @@ -19,30 +19,24 @@ func NewCommand() *cobra.Command { if err != nil { return err } - query := strings.Join(args, " ") - queryType := "track" - deviceID, err := cmd.Flags().GetString("device-id") + track := strings.Join(args, " ") + + playlist, err := cmd.Flags().GetString("playlist") if err != nil { return err } - contextQuery, err := cmd.Flags().GetString("playlist") + album, err := cmd.Flags().GetString("album") if err != nil { return err } - if contextQuery == "" { - contextQuery, err = cmd.Flags().GetString("album") - if err != nil { - return err - } - queryType = "album" - } else { - queryType = "playlist" + if track != "" && playlist != "" || track != "" && album != "" || playlist != "" && album != "" { + return errors.New(internal.ErrInvalidPlayArgs) } - status, err := p(api, query, contextQuery, queryType, deviceID) + status, err := p(api, track, playlist, album) if err != nil { return err } @@ -52,30 +46,28 @@ func NewCommand() *cobra.Command { }, } - cmd.Flags().String("device-id", "", "device ID from 'spotify device list'") cmd.Flags().String("playlist", "", "playlist name from 'spotify playlist list'") cmd.Flags().String("album", "", "album name") return cmd } -func p(api internal.APIInterface, query, contextQuery, queryType, deviceID string) (string, error) { - if len(query) > 0 || len(contextQuery) > 0 { - return play.Play(api, query, contextQuery, queryType, deviceID) +func p(api internal.APIInterface, track, playlist, album string) (string, error) { + if track != "" || playlist != "" || album != "" { + return play.Play(api, track, playlist, album) } playback, err := api.GetPlayback() if err != nil { return "", err } - if playback == nil { return "", errors.New(internal.ErrNoActiveDevice) } if playback.IsPlaying { - return pause.Pause(api, deviceID) + return pause.Pause(api) } else { - return play.Play(api, query, contextQuery, queryType, deviceID) + return play.Play(api, "", "", "") } } diff --git a/internal/p/p_test.go b/internal/p/p_test.go index ad45596..085846b 100644 --- a/internal/p/p_test.go +++ b/internal/p/p_test.go @@ -17,12 +17,14 @@ func TestP_Play(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ - Meta: spotify.Meta{ID: "0"}, + Meta: spotify.Meta{ + ID: "0", + Type: "track", + }, Name: "Track", Artists: []spotify.Artist{{Name: "Artist"}}, Duration: &spotify.Duration{Duration: time.Second}, }, - Type: "track", }, } @@ -32,9 +34,9 @@ func TestP_Play(t *testing.T) { api.On("GetPlayback").Return(playback1, nil).Twice() api.On("GetPlayback").Return(playback2, nil).Once() - api.On("Play", "", "", "", []string(nil)).Return(nil) + api.On("Play", "", []string(nil)).Return(nil) - status, err := p(api, "", "", "", "") + status, err := p(api, "", "", "") require.NoError(t, err) require.Equal(t, " Track\r🎵\n Artist\r🎤\n 0:00 [ ] 0:01\r▶️\n", status) } @@ -63,23 +65,25 @@ func TestP_Play_WithArgs(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ - Meta: spotify.Meta{ID: "0"}, + Meta: spotify.Meta{ + ID: "0", + Type: "track", + }, Name: name, Artists: []spotify.Artist{{Name: "Artist"}}, Duration: &spotify.Duration{Duration: time.Second}, }, - Type: "track", }, } query := "track" api.On("Search", query, "track", 1).Return(paging, nil) - api.On("Play", "", "", "", []string{uri}).Return(nil) + api.On("Play", "", []string{uri}).Return(nil) api.On("GetPlayback").Return(playback1, nil).Twice() api.On("GetPlayback").Return(playback2, nil).Once() - status, err := p(api, query, "", "", "") + status, err := p(api, query, "", "") require.NoError(t, err) require.Equal(t, " Track\r🎵\n Artist\r🎤\n 0:00 [ ] 0:01\r▶️\n", status) } @@ -92,11 +96,11 @@ func TestP_Pause(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ + Meta: spotify.Meta{Type: "track"}, Name: "Track", Artists: []spotify.Artist{{Name: "Artist"}}, Duration: &spotify.Duration{Duration: time.Second}, }, - Type: "track", }, } @@ -106,9 +110,9 @@ func TestP_Pause(t *testing.T) { api.On("GetPlayback").Return(playback1, nil).Twice() api.On("GetPlayback").Return(playback2, nil).Once() - api.On("Pause", "").Return(nil) + api.On("Pause").Return(nil) - status, err := p(api, "", "", "", "") + status, err := p(api, "", "", "") require.NoError(t, err) require.Equal(t, " Track\r🎵\n Artist\r🎤\n 0:00 [ ] 0:01\r⏸\n", status) } @@ -117,7 +121,7 @@ func TestP_ErrNoActiveDevice(t *testing.T) { api := new(internal.MockAPI) api.On("GetPlayback").Return(nil, nil) - _, err := p(api, "", "", "", "") + _, err := p(api, "", "", "") require.Error(t, err) require.Equal(t, internal.ErrNoActiveDevice, err.Error()) } diff --git a/internal/pause/pause.go b/internal/pause/pause.go index f11ceec..99a616b 100644 --- a/internal/pause/pause.go +++ b/internal/pause/pause.go @@ -19,12 +19,7 @@ func NewCommand() *cobra.Command { return err } - deviceID, err := cmd.Flags().GetString("device-id") - if err != nil { - return err - } - - status, err := Pause(api, deviceID) + status, err := Pause(api) if err != nil { return err } @@ -34,12 +29,10 @@ func NewCommand() *cobra.Command { }, } - cmd.Flags().String("device-id", "", "device ID from 'spotify device list'") - return cmd } -func Pause(api internal.APIInterface, deviceID string) (string, error) { +func Pause(api internal.APIInterface) (string, error) { playback, err := api.GetPlayback() if err != nil { return "", err @@ -49,7 +42,7 @@ func Pause(api internal.APIInterface, deviceID string) (string, error) { return "", errors.New(internal.ErrNoActiveDevice) } - if err := api.Pause(deviceID); err != nil { + if err := api.Pause(); err != nil { return "", err } diff --git a/internal/pause/pause_test.go b/internal/pause/pause_test.go index 6677e67..edea92b 100644 --- a/internal/pause/pause_test.go +++ b/internal/pause/pause_test.go @@ -1,7 +1,6 @@ package pause import ( - "errors" "spotify/internal" "testing" "time" @@ -18,11 +17,11 @@ func TestPause(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ + Meta: spotify.Meta{Type: "track"}, Name: "Track", Artists: []spotify.Artist{{Name: "Artist"}}, Duration: &spotify.Duration{Duration: time.Second}, }, - Type: "track", }, } @@ -32,28 +31,18 @@ func TestPause(t *testing.T) { api.On("GetPlayback").Return(playback1, nil).Once() api.On("GetPlayback").Return(playback2, nil).Once() - api.On("Pause", "").Return(nil) + api.On("Pause").Return(nil) - status, err := Pause(api, "") + status, err := Pause(api) require.NoError(t, err) require.Equal(t, " Track\r🎵\n Artist\r🎤\n 0:00 [ ] 0:01\r⏸\n", status) } -func TestPause_ErrAlreadyPaused(t *testing.T) { - api := new(internal.MockAPI) - api.On("GetPlayback").Return(new(spotify.Playback), nil) - api.On("Pause", "").Return(errors.New(internal.ErrRestrictionViolated)) - - _, err := Pause(api, "") - require.Error(t, err) - require.Equal(t, internal.ErrRestrictionViolated, err.Error()) -} - func TestPause_ErrNoActiveDevice(t *testing.T) { api := new(internal.MockAPI) api.On("GetPlayback").Return(nil, nil) - _, err := Pause(api, "") + _, err := Pause(api) require.Error(t, err) require.Equal(t, internal.ErrNoActiveDevice, err.Error()) } diff --git a/internal/play/play.go b/internal/play/play.go index decf6fc..a2cedcb 100644 --- a/internal/play/play.go +++ b/internal/play/play.go @@ -20,30 +20,23 @@ func NewCommand() *cobra.Command { return err } - query := strings.Join(args, " ") - queryType := "track" + track := strings.Join(args, " ") - deviceID, err := cmd.Flags().GetString("device-id") + playlist, err := cmd.Flags().GetString("playlist") if err != nil { return err } - contextQuery, err := cmd.Flags().GetString("playlist") + album, err := cmd.Flags().GetString("album") if err != nil { return err } - if contextQuery == "" { - contextQuery, err = cmd.Flags().GetString("album") - if err != nil { - return err - } - queryType = "album" - } else { - queryType = "playlist" + if track != "" && playlist != "" || track != "" && album != "" || playlist != "" && album != "" { + return errors.New(internal.ErrInvalidPlayArgs) } - status, err := Play(api, query, contextQuery, queryType, deviceID) + status, err := Play(api, track, playlist, album) if err != nil { return err } @@ -53,85 +46,65 @@ func NewCommand() *cobra.Command { }, } - cmd.Flags().String("device-id", "", "device ID from 'spotify device list'") cmd.Flags().String("playlist", "", "playlist name from 'spotify playlist list'") cmd.Flags().String("album", "", "album name that you wish to play") return cmd } -func Play(api internal.APIInterface, query, contextQuery, queryType, deviceID string) (string, error) { +func Play(api internal.APIInterface, track, playlist, album string) (string, error) { playback, err := api.GetPlayback() if err != nil { return "", err } - if playback == nil { return "", errors.New(internal.ErrNoActiveDevice) } - switch queryType { - case "album": + isPlaying := playback.IsPlaying + id := playback.Item.ID + progressMs := playback.ProgressMs - api, err := internal.Authenticate() + if track != "" { + track, err := internal.SearchTrack(api, track) if err != nil { return "", err } - paging, err := api.Search(contextQuery, "album", 1) - if err != nil { - return "", err - } - - albums := paging.Albums.Items - if len(albums) == 0 { - return "", errors.New(internal.ErrNoAlbums) - } - - if err := api.Play(deviceID, albums[0].URI); err != nil { + if err := api.Play("", track.URI); err != nil { return "", err } + } - case "playlist": - // Return a different API interface required for the playlist commands? - api, err := internal.Authenticate() + if album != "" { + album, err := internal.SearchAlbum(api, album) if err != nil { return "", err } - playlists, err := api.GetPlaylists() - if err != nil { + if err := api.Play(album.URI); err != nil { return "", err } + } - for _, playlist := range playlists { - if strings.EqualFold(playlist.Name, contextQuery) { - if err := api.Play(deviceID, playlist.URI); err != nil { - return "", err - } - break - } - } - - default: - track, err := internal.Search(api, query, "track") + if playlist != "" { + playlist, err := internal.SearchPlaylist(api, playlist) if err != nil { return "", err } - if err := api.Play(deviceID, "", track.URI); err != nil { + if err := api.Play(playlist.URI); err != nil { return "", err } } playback, err = internal.WaitForUpdatedPlayback(api, func(playback *spotify.Playback) bool { - // The first check safeguards against empty playback objects - return len(playback.Item.ID) > 0 && playback.IsPlaying + hasChanged := len(playback.Item.ID) > 0 && (playback.Item.ID != id || playback.ProgressMs < progressMs) + return !isPlaying && playback.IsPlaying || hasChanged }) if err != nil { return "", err } return status.Show(playback), nil - } diff --git a/internal/play/play_test.go b/internal/play/play_test.go index 82d4fec..1f75115 100644 --- a/internal/play/play_test.go +++ b/internal/play/play_test.go @@ -1,7 +1,6 @@ package play import ( - "errors" "spotify/internal" "testing" "time" @@ -18,12 +17,14 @@ func TestPlay(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ - Meta: spotify.Meta{ID: "0"}, + Meta: spotify.Meta{ + ID: "0", + Type: "track", + }, Name: "Track", Artists: []spotify.Artist{{Name: "Artist"}}, Duration: &spotify.Duration{Duration: time.Second}, }, - Type: "track", }, } @@ -33,9 +34,9 @@ func TestPlay(t *testing.T) { api.On("GetPlayback").Return(playback1, nil).Once() api.On("GetPlayback").Return(playback2, nil).Once() - api.On("Play", "", "", "", []string(nil)).Return(nil) + api.On("Play", "", []string(nil)).Return(nil) - status, err := Play(api, "", "", "", "") + status, err := Play(api, "", "", "") require.NoError(t, err) require.Equal(t, " Track\r🎵\n Artist\r🎤\n 0:00 [ ] 0:01\r▶️\n", status) } @@ -64,42 +65,34 @@ func TestPlay_WithArgs(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ - Meta: spotify.Meta{ID: "0"}, + Meta: spotify.Meta{ + ID: "0", + Type: "track", + }, Name: name, Artists: []spotify.Artist{{Name: "Artist"}}, Duration: &spotify.Duration{Duration: time.Second}, }, - Type: "track", }, } query := "track" api.On("Search", query, "track", 1).Return(paging, nil) - api.On("Play", "", "", "", []string{uri}).Return(nil) + api.On("Play", "", []string{uri}).Return(nil) api.On("GetPlayback").Return(playback1, nil).Twice() api.On("GetPlayback").Return(playback2, nil).Once() - status, err := Play(api, query, "", "", "") + status, err := Play(api, query, "", "") require.NoError(t, err) require.Equal(t, " Track\r🎵\n Artist\r🎤\n 0:00 [ ] 0:01\r▶️\n", status) } -func TestPlay_ErrAlreadyPlaying(t *testing.T) { - api := new(internal.MockAPI) - api.On("GetPlayback").Return(new(spotify.Playback), nil) - api.On("Play", "", "", "", []string(nil)).Return(errors.New(internal.ErrRestrictionViolated)) - - _, err := Play(api, "", "", "", "") - require.Error(t, err) - require.Equal(t, internal.ErrRestrictionViolated, err.Error()) -} - func TestPlay_ErrNoActiveDevice(t *testing.T) { api := new(internal.MockAPI) api.On("GetPlayback").Return(nil, nil) - _, err := Play(api, "", "", "", "") + _, err := Play(api, "", "", "") require.Error(t, err) require.Equal(t, internal.ErrNoActiveDevice, err.Error()) } diff --git a/internal/playlist/show.go b/internal/playlist/show.go index 1c21da4..99fb0c1 100644 --- a/internal/playlist/show.go +++ b/internal/playlist/show.go @@ -1,7 +1,6 @@ package playlist import ( - "errors" "fmt" "spotify/internal" "strconv" @@ -14,7 +13,7 @@ import ( func NewShowCommand() *cobra.Command { return &cobra.Command{ - Use: "show", + Use: "show [playlist]", Short: "show artist and songs", Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -23,31 +22,20 @@ func NewShowCommand() *cobra.Command { return err } - name := strings.Join(args, " ") + playlist := strings.Join(args, " ") - return Show(api, name) + return show(api, playlist) }, } } -func Show(api *spotify.API, name string) error { - playlists, err := api.GetPlaylists() +func show(api *spotify.API, name string) error { + playlist, err := internal.SearchPlaylist(api, name) if err != nil { return err } - id := "" - for _, playlist := range playlists { - if strings.EqualFold(playlist.Name, name) { - id = playlist.ID - } - } - if id == "" { - return errors.New("no such playlist") - } - - playlist, err := api.GetPlaylist(id) - if err != nil { + if err := playlist.HREF.Get(api, playlist); err != nil { return err } diff --git a/internal/queue/queue.go b/internal/queue/queue.go index 18af142..7aeafa1 100644 --- a/internal/queue/queue.go +++ b/internal/queue/queue.go @@ -35,7 +35,7 @@ func NewCommand() *cobra.Command { } func Queue(api internal.APIInterface, query string) (string, error) { - track, err := internal.Search(api, query, "track") + track, err := internal.SearchTrack(api, query) if err != nil { return "", err } diff --git a/internal/save/save_test.go b/internal/save/save_test.go index 536780f..0baea6b 100644 --- a/internal/save/save_test.go +++ b/internal/save/save_test.go @@ -12,14 +12,7 @@ func TestSave(t *testing.T) { api := new(internal.MockAPI) var id string - - playback := &spotify.Playback{ - Item: spotify.Item{ - Track: spotify.Track{ - Meta: spotify.Meta{ID: id}, - }, - }, - } + playback := &spotify.Playback{Item: spotify.Item{Track: spotify.Track{Meta: spotify.Meta{ID: id}}}} api.On("GetPlayback").Return(playback, nil) api.On("SaveTracks", []string{id}).Return(nil) @@ -31,11 +24,7 @@ func TestSave(t *testing.T) { func TestSave_ErrSavePodcast(t *testing.T) { api := new(internal.MockAPI) - playback := &spotify.Playback{ - Item: spotify.Item{ - Type: "episode", - }, - } + playback := &spotify.Playback{Item: spotify.Item{Track: spotify.Track{Meta: spotify.Meta{Type: "episode"}}}} api.On("GetPlayback").Return(playback, nil) diff --git a/internal/status/status_test.go b/internal/status/status_test.go index 0526ec1..fd4a190 100644 --- a/internal/status/status_test.go +++ b/internal/status/status_test.go @@ -17,13 +17,11 @@ func TestStatus_Track(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ - Name: "Track", - Artists: []spotify.Artist{ - {Name: "Artist"}, - }, + Meta: spotify.Meta{Type: "track"}, + Name: "Track", + Artists: []spotify.Artist{{Name: "Artist"}}, Duration: &spotify.Duration{Duration: time.Second}, }, - Type: "track", }, } @@ -42,13 +40,11 @@ func TestStatus_Podcast(t *testing.T) { ProgressMs: 0, Item: spotify.Item{ Track: spotify.Track{ + Meta: spotify.Meta{Type: "episode"}, Name: "Episode", Duration: &spotify.Duration{Duration: time.Second}, }, - Show: spotify.Show{ - Name: "Podcast", - }, - Type: "episode", + Show: spotify.Show{Name: "Podcast"}, }, } diff --git a/internal/unsave/unsave_test.go b/internal/unsave/unsave_test.go index bbacc23..3388777 100644 --- a/internal/unsave/unsave_test.go +++ b/internal/unsave/unsave_test.go @@ -31,11 +31,7 @@ func TestUnsave(t *testing.T) { func TestUnsave_ErrSavePodcast(t *testing.T) { api := new(internal.MockAPI) - playback := &spotify.Playback{ - Item: spotify.Item{ - Type: "episode", - }, - } + playback := &spotify.Playback{Item: spotify.Item{Track: spotify.Track{Meta: spotify.Meta{Type: "episode"}}}} api.On("GetPlayback").Return(playback, nil)