public void ArtistUri_ArtistUriWhitespaceString_ThrowsArgumentException()
        {
            // arrange
            const string uri = " ";

            // act
            SpotifyUriHelper.ArtistUri(uri);
        }
예제 #2
0
 public async Task <T> GetPlaylist <T>(string playlistId, string accessToken = null)
 {
     if (string.IsNullOrEmpty(playlistId))
     {
         throw new ArgumentNullException(nameof(playlistId));
     }
     return(await GetModel <T>($"{BaseUrl}/playlists/{SpotifyUriHelper.PlaylistId(playlistId)}", accessToken));
 }
        public void ArtistUri_ArtistUriNull_ThrowsArgumentException()
        {
            // arrange
            const string uri = null;

            // act
            SpotifyUriHelper.ArtistUri(uri);
        }
        public void ArtistUri_ArtistUriLeadingWhitespace_ThrowsArgumentException()
        {
            // arrange
            const string uri = " spotify:album:0TnOYISbd1XYRBk9myaseg";

            // act
            SpotifyUriHelper.ArtistUri(uri);
        }
예제 #5
0
        /// <summary>
        /// BETA. Play an Artist on the user’s active device.
        /// </summary>
        /// <param name="artistId">Spotify Album Id to play</param>
        /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service.
        /// The access token must have been issued on behalf of a user. The access token must have the
        /// `user-modify-playback-state` scope authorized in order to control playback. <seealso cref="UserAccountsService"/>
        /// </param>
        /// <param name="deviceId">Optional. The id of the device this command is targeting. If not supplied, the user’s
        /// currently active device is the target.</param>
        /// <param name="positionMs">Optional. Indicates from what position to start playback. Must be a positive number.
        /// Passing in a position that is greater than the length of the track will cause the player to start playing the
        /// next song.</param>
        /// <remarks>
        /// https://developer.spotify.com/documentation/web-api/reference/player/start-a-users-playback/
        /// </remarks>
        public async Task PlayArtist(string artistId, string accessToken = null, string deviceId = null, long positionMs = 0)
        {
            if (string.IsNullOrEmpty(artistId))
            {
                throw new ArgumentNullException(nameof(artistId));
            }
            dynamic data = JObject.FromObject(new { context_uri = SpotifyUriHelper.ArtistUri(artistId) });

            await Play(data, accessToken, deviceId, positionMs);
        }
예제 #6
0
        /// <summary>
        /// Get Spotify catalog information for a single album.
        /// </summary>
        /// <param name="albumId">The Spotify ID for the album.</param>
        /// <param name="market">Optional. An ISO 3166-1 alpha-2 country code or the string `from_token`
        /// (See <see cref="SpotifyCountryCodes"/>). Provide this parameter if you want to apply Track Relinking.</param>
        /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service.</param>
        /// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
        /// <returns>A Task that, once successfully completed, returns a Model of T.</returns>
        /// <remarks>https://developer.spotify.com/documentation/web-api/reference/albums/get-album/</remarks>
        public async Task <T> GetAlbum <T>(string albumId, string market = null, string accessToken = null)
        {
            string url = $"{BaseUrl}/albums/{SpotifyUriHelper.AlbumId(albumId)}";

            if (!string.IsNullOrEmpty(market))
            {
                url += $"?market={market}";
            }
            return(await GetModel <T>(url, accessToken));
        }
        public void PlaylistUri_ValidPlaylistUri_ReturnsPlaylistUri()
        {
            // arrange
            const string uri = "spotify:playlist:0TnOYISbd1XYRBk9myaseg";

            // act
            string result = SpotifyUriHelper.PlaylistUri(uri);

            // assert
            Assert.AreSame(uri, result);
        }
        public void ArtistUri_UserCollectionArtist_ReturnsCollectionUri()
        {
            // arrange
            const string collectionUri = "spotify:user:daniellarsennz:collection:artist:65XA3lk0aG9XejO8y37jjD";

            // act
            string uri = SpotifyUriHelper.ArtistUri(collectionUri);

            // assert
            Assert.AreEqual(collectionUri, uri);
        }
        public void PlaylistUri_NumericUsername_ExtractsValidUri()
        {
            // arrange
            const string fullUri = "spotify:user:1298341199:playlist:6RTNx0BJWjbmJuEfvMau3r";

            // act
            string uri = SpotifyUriHelper.PlaylistUri(fullUri);

            // assert
            Assert.AreEqual(fullUri, uri);
        }
        public void TrackUri_ValidTrackUri_ReturnsTrackUri()
        {
            // arrange
            const string uri = "spotify:track:0TnOYISbd1XYRBk9myaseg";

            // act
            string result = SpotifyUriHelper.TrackUri(uri);

            // assert
            Assert.AreSame(uri, result);
        }
        public void PlaylistUri_NumericUserIdInUri_ReturnsPlaylistUri()
        {
            // arrange
            const string uri = "spotify:user:122740800:playlist:6CI5ScAEKJdHMmQsfjHOmY";

            // act
            string result = SpotifyUriHelper.PlaylistUri(uri);

            // assert
            Assert.AreSame(uri, result);
        }
        public void PlaylistUri_DashInUserUri_ReturnsPlaylistUri()
        {
            // arrange
            const string uri = "spotify:user:trojan-records:playlist:0HHuexBIs4gdPZ2WeNGDt3";

            // act
            string result = SpotifyUriHelper.PlaylistUri(uri);

            // assert
            Assert.AreSame(uri, result);
        }
        public void PlaylistUri_UnderscoreInUri_ReturnsPlaylistUri()
        {
            // arrange
            const string uri = "spotify:user:catstevens_islanduk:playlist:4DgyX0TYmDJvjKlAVtkRxo";
            //spotify:user:122740800:playlist:6CI5ScAEKJdHMmQsfjHOmY
            // act
            string result = SpotifyUriHelper.PlaylistUri(uri);

            // assert
            Assert.AreSame(uri, result);
        }
        public void ArtistId_UserCollectionArtistUri_ReturnsArtistId()
        {
            // arrange
            const string artistId      = "65XA3lk0aG9XejO8y37jjD";
            string       collectionUri = $"spotify:user:daniellarsennz:collection:artist:{artistId}";

            // act
            string id = SpotifyUriHelper.ArtistId(collectionUri);

            // assert
            Assert.AreEqual(artistId, id);
        }
        public void PlaylistUri_PlaylistUriMoreThan3Parts_ExtractsValidUri()
        {
            // arrange
            const string playlistUri = "spotify:playlist:0TnOYISbd1XYRBk9myaseg";
            string       fullUri     = $"spotify:user:{playlistUri}";

            // act
            string uri = SpotifyUriHelper.PlaylistUri(fullUri);

            // assert
            Assert.AreEqual(playlistUri, uri);
        }
예제 #16
0
        /// <summary>
        /// Get Spotify catalog information about an artist’s top tracks by country.
        /// </summary>
        /// <param name="artistId">The Spotify ID for the artist.</param>
        /// <param name="market">Required. An ISO 3166-1 alpha-2 country code (<see cref="SpotifyCountryCodes"/>)
        /// or the string `from_token`.</param>
        /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
        /// used for this call only. See constructors for more ways to provide access tokens.</param>
        /// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
        /// <returns>Task of T. The Spotify response is deserialised as T.</returns>
        public async Task <T> GetArtistsTopTracks <T>(string artistId, string market, string accessToken = null)
        {
            if (string.IsNullOrWhiteSpace(artistId))
            {
                throw new ArgumentNullException("artistId");
            }
            if (string.IsNullOrWhiteSpace(market))
            {
                throw new ArgumentNullException("market");
            }

            return(await GetModelFromProperty <T>($"{BaseUrl}/artists/{SpotifyUriHelper.ArtistId(artistId)}/top-tracks?country={market}", "tracks", accessToken));
        }
예제 #17
0
        /// <summary>
        /// BETA. Play a Playlist on the user’s active device.
        /// </summary>
        /// <param name="playlistId">Spotify Playlist Id to play</param>
        /// <param name="offsetPosition">From where in the Playlist playback should start, i.e. Track number</param>
        /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service.
        /// The access token must have been issued on behalf of a user. The access token must have the
        /// `user-modify-playback-state` scope authorized in order to control playback. <seealso cref="UserAccountsService"/>
        /// </param>
        /// <param name="deviceId">Optional. The id of the device this command is targeting. If not supplied, the user’s
        /// currently active device is the target.</param>
        /// <param name="positionMs">Optional. Indicates from what position to start playback. Must be a positive number.
        /// Passing in a position that is greater than the length of the track will cause the player to start playing the
        /// next song.</param>
        /// <remarks>
        /// https://developer.spotify.com/documentation/web-api/reference/player/start-a-users-playback/
        /// </remarks>
        public async Task PlayPlaylistOffset(
            string playlistId,
            int offsetPosition,
            string accessToken = null,
            string deviceId    = null,
            long positionMs    = 0)
        {
            dynamic data = JObject.FromObject(new { context_uri = SpotifyUriHelper.PlaylistUri(playlistId) });

            if (offsetPosition > 0)
            {
                data.offset = JObject.FromObject(new { position = offsetPosition });
            }
            await Play(data, accessToken, deviceId, positionMs);
        }
예제 #18
0
        /// <summary>
        /// BETA. Play an Album from a Track offset on the user’s active device.
        /// </summary>
        /// <param name="albumId">Spotify Album Id to play</param>
        /// <param name="offsetTrackId">Id of the Track to start at</param>
        /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service.
        /// The access token must have been issued on behalf of a user. The access token must have the
        /// `user-modify-playback-state` scope authorized in order to control playback. <seealso cref="UserAccountsService"/>
        /// </param>
        /// <param name="deviceId">Optional. The id of the device this command is targeting. If not supplied, the user’s
        /// currently active device is the target.</param>
        /// <param name="positionMs">Optional. Indicates from what position to start playback. Must be a positive number.
        /// Passing in a position that is greater than the length of the track will cause the player to start playing the
        /// next song.</param>
        /// <remarks>
        /// https://developer.spotify.com/documentation/web-api/reference/player/start-a-users-playback/
        /// </remarks>
        public async Task PlayAlbumOffset(
            string albumId,
            string offsetTrackId,
            string accessToken = null,
            string deviceId    = null,
            long positionMs    = 0)
        {
            dynamic data = JObject.FromObject(new { context_uri = SpotifyUriHelper.AlbumUri(albumId) });

            if (offsetTrackId != null)
            {
                data.offset = JObject.FromObject(new { uri = SpotifyUriHelper.TrackUri(offsetTrackId) });
            }
            await Play(data, accessToken, deviceId, positionMs);
        }
예제 #19
0
        /// <summary>
        /// Get a playlist owned by a Spotify user.
        /// </summary>
        /// <param name="playlistId">The Spotify ID for the playlist.</param>
        /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service.
        /// <param name="fields">Optional. Filters for the query: a comma-separated list of the fields to return. If omitted, all fields are returned. See docs for examples.</param>
        /// <param name="additionalTypes">Optional. A comma-separated list of item types that your
        /// client supports besides the default track type. Valid types are: `track` and `episode`.
        /// Note: This parameter was introduced to allow existing clients to maintain their current
        /// behaviour and might be deprecated in the future. In addition to providing this parameter,
        /// make sure that your client properly handles cases of new types in the future by checking
        /// against the type field of each object.</param>
        /// <param name="market">Optional. An <see cref="SpotifyCountryCodes"/> or the string <see cref="SpotifyCountryCodes._From_Token"/>.
        /// Provide this parameter if you want to apply Track Relinking.</param>
        /// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
        /// <returns>Task of T</returns>
        public async Task <T> GetPlaylist <T>(
            string playlistId,
            string accessToken       = null,
            string fields            = null,
            string[] additionalTypes = null,
            string market            = null)
        {
            if (string.IsNullOrEmpty(playlistId))
            {
                throw new ArgumentNullException(nameof(playlistId));
            }

            var builder = new UriBuilder($"{BaseUrl}/playlists/{SpotifyUriHelper.PlaylistId(playlistId)}");

            builder.AppendToQueryIfValueNotNullOrWhiteSpace("fields", fields);
            builder.AppendToQueryAsCsv("additional_types", additionalTypes);
            builder.AppendToQueryIfValueNotNullOrWhiteSpace("market", market);

            return(await GetModel <T>(builder.Uri, accessToken : accessToken));
        }
예제 #20
0
        /// <summary>
        /// Get full details of the tracks of a playlist owned by a Spotify user.
        /// </summary>
        /// <param name="playlistId">The Spotify ID for the playlist.</param>
        /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service.
        /// <param name="fields">Optional. Filters for the query: a comma-separated list of the fields to return. If omitted, all fields are returned. See docs for examples.</param>
        /// <param name="limit">Optional. The maximum number of tracks to return. Default: 100. Minimum: 1. Maximum: 100.</param>
        /// <param name="offset">Optional. The index of the first track to return. Default: 0 (the first object).</param>
        /// <param name="market">Optional. An <see cref="SpotifyCountryCodes"/> or the string <see cref="SpotifyCountryCodes._From_Token"/>.
        /// Provide this parameter if you want to apply Track Relinking.</param>
        /// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
        /// <returns>Task of T</returns>
        /// <remarks>
        /// https://developer.spotify.com/documentation/web-api/reference/playlists/get-playlists-tracks/
        /// </remarks>
        public async Task <T> GetTracks <T>(
            string playlistId,
            string accessToken = null,
            string fields      = null,
            int?limit          = null,
            int offset         = 0,
            string market      = null)
        {
            if (string.IsNullOrEmpty(playlistId))
            {
                throw new ArgumentNullException(nameof(playlistId));
            }
            string url = $"{BaseUrl}/playlists/{SpotifyUriHelper.PlaylistId(playlistId)}/tracks";

            if (!string.IsNullOrEmpty(fields) || (limit ?? 0) > 0 || offset > 0 || !string.IsNullOrEmpty(market))
            {
                url += "?";

                if (!string.IsNullOrEmpty(fields))
                {
                    url += $"fields={fields}&";
                }
                if ((limit ?? 0) > 0)
                {
                    url += $"limit={limit.Value}&";
                }
                if (offset > 0)
                {
                    url += $"offset={offset}&";
                }
                if (!string.IsNullOrEmpty(market))
                {
                    url += $"market={market}";
                }
            }

            return(await GetModel <T>(url, accessToken));
        }
예제 #21
0
        public async Task <T> NewGetAlbumTracks <T>(
            string albumId,
            int?limit          = null,
            int offset         = 0,
            string market      = null,
            string accessToken = null)
        {
            var url = "https://api.spotify.com/v1/albums/" + SpotifyUriHelper.AlbumId(albumId) + "/tracks";

            if (limit.HasValue || !string.IsNullOrEmpty(market))
            {
                url += "?";
            }
            if (limit.HasValue)
            {
                url += $"limit={limit.Value}&offset={offset}&";
            }
            if (!string.IsNullOrEmpty(market))
            {
                url = url + "market=" + market;
            }
            return(await GetModelFromProperty <T>(url, "items", accessToken));
        }
예제 #22
0
        /// <summary>
        /// Get Spotify catalog information about an album’s tracks. Optional parameters can be used to limit the number of tracks returned.
        /// </summary>
        /// <param name="albumId">The Spotify ID for the album.</param>
        /// <param name="limit">Optional. The maximum number of tracks to return. Default: 20. Minimum: 1. Maximum: 50.</param>
        /// <param name="offset">Optional. The index of the first track to return. Default: 0 (the first
        /// object). Use with limit to get the next set of tracks.</param>
        /// <param name="market">Optional. An ISO 3166-1 alpha-2 country code or the string `from_token`
        /// (See <see cref="SpotifyCountryCodes"/>). Provide this parameter if you want to apply Track Relinking.</param>
        /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service.</param>
        /// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
        /// <returns>A Task that, once successfully completed, returns a Model of T.</returns>
        /// <remarks>https://developer.spotify.com/documentation/web-api/reference/albums/get-albums-tracks/</remarks>
        public async Task <T> GetAlbumTracks <T>(
            string albumId,
            int?limit          = null,
            int offset         = 0,
            string market      = null,
            string accessToken = null)
        {
            string url = $"{BaseUrl}/albums/{SpotifyUriHelper.AlbumId(albumId)}/tracks";

            if (limit.HasValue || !string.IsNullOrEmpty(market))
            {
                url += "?";
            }
            if (limit.HasValue)
            {
                url += $"limit={limit.Value}&offset={offset}&";
            }
            if (!string.IsNullOrEmpty(market))
            {
                url += $"market={market}";
            }
            return(await GetModel <T>(url, accessToken));
        }
예제 #23
0
 /// <summary>
 /// Get Spotify catalog information about artists similar to a given artist. Similarity is
 /// based on analysis of the Spotify community’s listening history.
 /// </summary>
 /// <param name="artistId">The Spotify ID for the artist.</param>
 /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
 /// used for this call only. See constructors for more ways to provide access tokens.</param>
 /// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
 /// <returns>Task of T. The Spotify response is deserialised as T.</returns>
 public async Task <T> GetRelatedArtists <T>(string artistId, string accessToken = null)
 => await GetModel <T>($"{BaseUrl}/artists/{SpotifyUriHelper.ArtistId(artistId)}/related-artists", accessToken);
예제 #24
0
 /// <summary>
 /// Get Spotify catalog information for a single artist identified by their unique Spotify ID.
 /// </summary>
 /// <param name="artistId">The Spotify ID for the artist.</param>
 /// <param name="accessToken">Optional. A valid access token from the Spotify Accounts service,
 /// used for this call only. See constructors for more ways to provide access tokens.</param>
 /// <typeparam name="T">Optionally provide your own type to deserialise Spotify's response to.</typeparam>
 /// <returns>Task of T. The Spotify response is deserialised as T.</returns>
 public async Task <T> GetArtist <T>(string artistId, string accessToken = null)
 => await GetModel <T>(new Uri($"{BaseUrl}/artists/{SpotifyUriHelper.ArtistId(artistId)}"), accessToken);
예제 #25
0
        public async Task <bool> JoinPlaylist(
            string query,
            string token,
            Station station,
            string stationToken,
            CancellationToken cancellationToken)
        {
            // is the station playing?
            // default the position to what was returned by get info
            var info = await GetUserNowPlaying(stationToken);

            if (
                info == null ||
                !info.IsPlaying ||
                info.Context == null ||
                SpotifyUriHelper.NormalizeUri(info.Context.Uri) != SpotifyUriHelper.NormalizeUri(station.SpotifyUri))
            {
                _logger.LogInformation($"JoinPlaylist: No longer playing station {station}");
                _logger.LogDebug($"JoinPlaylist: station.SpotifyUri = {station.SpotifyUri}");
                _logger.LogDebug($"JoinPlaylist: info = {JsonConvert.SerializeObject(info)}");
                return(false);
            }

            (string itemId, (long positionMs, DateTime atUtc)position)itemPosition = (info.Item?.Id, (info.ProgressMs ?? 0, DateTime.UtcNow));


            if (!SupportedSpotifyItemTypes.Contains(station.SpotifyContextType))
            {
                throw new NotSupportedException($"\"{station.SpotifyContextType}\" is not a supported Spotify context type");
            }

            var offset = await GetOffset(stationToken);

            if (offset.success)
            {
                // reset position to Station position
                itemPosition.itemId   = offset.itemId;
                itemPosition.position = offset.position;
            }

            await TurnOffShuffleRepeat(token, info);

            try
            {
                // mute joining player
                await Volume(token, 0, info.Device.Id);

                // play from offset
                switch (station.SpotifyContextType)
                {
                case "album":
                    await RetryHelper.RetryAsync(
                        () => _player.PlayAlbumOffset(
                            info.Context.Uri,
                            info.Item.Id,
                            accessToken: token,
                            positionMs: PositionMsNow(itemPosition.position).positionMs),
                        logger : _logger,
                        cancellationToken : cancellationToken);

                    break;

                case "playlist":
                    await RetryHelper.RetryAsync(
                        () => _player.PlayPlaylistOffset(
                            info.Context.Uri,
                            info.Item.Id,
                            accessToken: token,
                            positionMs: PositionMsNow(itemPosition.position).positionMs),
                        logger : _logger,
                        cancellationToken : cancellationToken);

                    break;
                }

                if (offset.success)
                {
                    await SyncJoiningPlayer(stationToken : stationToken, joiningToken : token);
                }
            }
            finally
            {
                // unmute joining player
                await Volume(token, (int)info.Device.VolumePercent, info.Device.Id);
            }

            return(true);
        }