package spt import ( "context" "errors" "github.com/zmb3/spotify/v2" ) var ( topTracksLimit = 15 albumtypes = []spotify.AlbumType{ spotify.AlbumTypeAlbum, spotify.AlbumTypeSingle, spotify.AlbumTypeAppearsOn, spotify.AlbumTypeCompilation, } ) type Playlist struct { SnapshotID string ID spotify.ID Tracks *[]spotify.PlaylistTrack } type Album struct { spotify.FullAlbum Tracks *[]spotify.SimpleTrack } type SavedAlbums []spotify.SavedAlbum type FollowedArtists []spotify.FullArtist type UserPlaylists []spotify.SimplePlaylist type LikedSongs []spotify.SavedTrack type Playable interface { Type() string Uri() } var ( ctx = context.Background() client *spotify.Client playlistCache map[spotify.ID]*Playlist = make(map[spotify.ID]*Playlist) albumCache map[spotify.ID]*Album = make(map[spotify.ID]*Album) PageContinue = errors.New("CONTINUE") ) func GetPlaylist(playlistId spotify.ID) (*Playlist, chan error) { c := make(chan error) if fp, err := client.GetPlaylist(ctx, playlistId); err != nil { go func() { c <- err }() return nil, c } else { if _, ok := playlistCache[fp.ID]; !ok || playlistCache[fp.ID].SnapshotID != fp.SnapshotID { tracks := &[]spotify.PlaylistTrack{} addTracks := func() { *tracks = append(*tracks, fp.Tracks.Tracks...) } addTracks() go func() { for page := 1; ; page++ { if perr := client.NextPage(ctx, &fp.Tracks); perr == spotify.ErrNoMorePages { c <- nil break } else if perr != nil { c <- perr return } addTracks() } }() p := &Playlist{ fp.SnapshotID, fp.ID, tracks, } playlistCache[fp.ID] = p return p, c } go func() { c <- nil }() return playlistCache[fp.ID], c } } func GetAlbum(albumID spotify.ID) (*Album, chan error) { c := make(chan error) if _, ok := albumCache[albumID]; !ok { fa, err := client.GetAlbum(ctx, albumID) if err != nil { go func() { c <- err }() return nil, c } tracks := &[]spotify.SimpleTrack{} addTracks := func() { *tracks = append(*tracks, fa.Tracks.Tracks...) } addTracks() go func() { for page := 1; ; page++ { if perr := client.NextPage(ctx, &fa.Tracks); perr == spotify.ErrNoMorePages { c <- nil break } else if perr != nil { c <- perr return } addTracks() } }() p := &Album{ *fa, tracks, } albumCache[fa.ID] = p return p, c } else { go func() { c <- nil }() return albumCache[albumID], c } } func CurrentUserSavedAlbums() (*SavedAlbums, chan error) { c := make(chan error) _a := make(SavedAlbums, 0) albums := &_a if sp, err := client.CurrentUsersAlbums(ctx); err != nil { go func() { c <- err }() return nil, c } else { addAlbums := func() { _a = append(_a, sp.Albums...) } addAlbums() go func() { for page := 1; ; page++ { if perr := client.NextPage(ctx, sp); perr == spotify.ErrNoMorePages { c <- nil break } else if perr != nil { c <- perr return } addAlbums() } }() return albums, c } } func CurrentUserPlaylists() (*UserPlaylists, chan error) { c := make(chan error) _p := make(UserPlaylists, 0) playlists := &_p if spp, err := client.CurrentUsersPlaylists(ctx); err != nil { go func() { c <- err }() return nil, c } else { addPlaylists := func() { _p = append(_p, spp.Playlists...) } addPlaylists() go func() { for page := 1; ; page++ { if perr := client.NextPage(ctx, spp); perr == spotify.ErrNoMorePages { c <- nil break } else if perr != nil { c <- perr return } addPlaylists() } }() return playlists, c } } func CurrentUserSavedTracks() (*LikedSongs, chan error) { c := make(chan error) _p := make(LikedSongs, 0) playlists := &_p if ls, err := client.CurrentUsersTracks(ctx); err != nil { go func() { c <- err }() return nil, c } else { addTracks := func() { _p = append(_p, ls.Tracks...) } addTracks() go func() { for page := 1; ; page++ { if perr := client.NextPage(ctx, ls); perr == spotify.ErrNoMorePages { c <- nil break } else if perr != nil { c <- perr return } addTracks() } }() return playlists, c } } func CurrentUserFollowedArtists() (*FollowedArtists, chan error) { c := make(chan error) // TODO: Check if this is the proper implementation _a := make(FollowedArtists, 0) artists := &_a if ar, err := client.CurrentUsersFollowedArtists(ctx); err != nil { go func() { c <- err }() return nil, c } else { ap := spotify.ID("") addArtists := func() { _a = append(_a, ar.Artists...) ap = _a[len(_a)-1].ID } addArtists() go func() { for { if len(ar.Artists) == 0 { c <- nil } if ar, err = client.CurrentUsersFollowedArtists(ctx, spotify.After(string(ap))); err != nil { if err == spotify.ErrNoMorePages { c <- nil break } else { c <- err break } } else { addArtists() } } }() return artists, c } } func RecentlyPlayed() ([]spotify.RecentlyPlayedItem, error) { return client.PlayerRecentlyPlayedOpt(ctx, &spotify.RecentlyPlayedOptions{Limit: 50}) } func GetPlayerState() (*spotify.PlayerState, error) { return client.PlayerState(ctx) } func GetTopTracks() ([]spotify.FullTrack, error) { c, err := client.CurrentUsersTopTracks(ctx, spotify.Limit(topTracksLimit)) if c != nil { return c.Tracks, err } else { return []spotify.FullTrack{}, errors.New("No Top Tracks Found!") } } func GetTopArtists() ([]spotify.FullArtist, error) { c, err := client.CurrentUsersTopArtists(ctx, spotify.Limit(topTracksLimit)) if c != nil { return c.Artists, err } else { return []spotify.FullArtist{}, errors.New("No Top Artists Found!") } } func GetArtistTopTracks(artistID spotify.ID) ([]spotify.FullTrack, error) { c, err := client.CurrentUser(ctx) if err != nil { return []spotify.FullTrack{}, err } return client.GetArtistsTopTracks(ctx, artistID, c.Country) } func GetArtistAlbums(artistID spotify.ID) ([]spotify.SimpleAlbum, error) { c, err := client.GetArtistAlbums(ctx, artistID, albumtypes) if err != nil { return []spotify.SimpleAlbum{}, err } return c.Albums, nil } func Search(s string) (*spotify.SearchResult, error) { return client.Search(ctx, s, spotify.SearchTypePlaylist| spotify.SearchTypeAlbum| spotify.SearchTypeTrack| spotify.SearchTypeArtist) } func UserDevices() ([]spotify.PlayerDevice, error) { return client.PlayerDevices(ctx) } func TransferPlayback(deviceId spotify.ID) error { s, err := GetPlayerState() if err != nil { return errors.New("Unable to get Current Player State!") } err = client.PauseOpt(ctx, &spotify.PlayOptions{DeviceID: &s.Device.ID}) return client.TransferPlayback(ctx, deviceId, true) } func GetFullPlaylist(id spotify.ID) (*spotify.FullPlaylist, error) { return client.GetPlaylist(ctx, id) }