using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SpotifyAPI.Web.Enums; using SpotifyAPI.Web.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace SpotifyAPI.Web { // ReSharper disable once InconsistentNaming public sealed class SpotifyWebAPI : IDisposable { private readonly SpotifyWebBuilder _builder; public SpotifyWebAPI() : this(null) { } public SpotifyWebAPI(ProxyConfig proxyConfig) { _builder = new SpotifyWebBuilder(); UseAuth = true; WebClient = new SpotifyWebClient(proxyConfig) { JsonSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, TypeNameHandling = TypeNameHandling.All } }; } public void Dispose() { WebClient.Dispose(); GC.SuppressFinalize(this); } #region Configuration /// /// The type of the /// public string TokenType { get; set; } /// /// A valid token issued by spotify. Used only when is true /// public string AccessToken { get; set; } /// /// If true, an authorization header based on and will be used /// public bool UseAuth { get; set; } /// /// A custom WebClient, used for Unit-Testing /// public IClient WebClient { get; set; } /// /// Specifies after how many miliseconds should a failed request be retried. /// public int RetryAfter { get; set; } = 50; /// /// Should a failed request (specified by be automatically retried or not. /// public bool UseAutoRetry { get; set; } = false; /// /// Maximum number of tries for one failed request. /// public int RetryTimes { get; set; } = 10; /// /// Whether a failure of type "Too Many Requests" should use up one of the allocated retry attempts. /// public bool TooManyRequestsConsumesARetry { get; set; } = false; /// /// Error codes that will trigger auto-retry if is enabled. /// public IEnumerable RetryErrorCodes { get; set; } = new[] { 500, 502, 503 }; #endregion Configuration #region Search /// /// Get Spotify catalog information about artists, albums, tracks or playlists that match a keyword string. /// /// The search query's keywords (and optional field filters and operators), for example q=roadhouse+blues. /// A list of item types to search across. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first result to return. Default: 0 /// An ISO 3166-1 alpha-2 country code or the string from_token. /// public SearchItem SearchItems(string q, SearchType type, int limit = 20, int offset = 0, string market = "") { return DownloadData(_builder.SearchItems(q, type, limit, offset, market)); } /// /// Get Spotify catalog information about artists, albums, tracks or playlists that match a keyword string asynchronously. /// /// The search query's keywords (and optional field filters and operators), for example q=roadhouse+blues. /// A list of item types to search across. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first result to return. Default: 0 /// An ISO 3166-1 alpha-2 country code or the string from_token. /// public Task SearchItemsAsync(string q, SearchType type, int limit = 20, int offset = 0, string market = "") { return DownloadDataAsync(_builder.SearchItems(q, type, limit, offset, market)); } #endregion Search #region Albums /// /// Get Spotify catalog information about an album’s tracks. Optional parameters can be used to limit the number of /// tracks returned. /// /// The Spotify ID for the album. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first track to return. Default: 0 (the first object). /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public Paging GetAlbumTracks(string id, int limit = 20, int offset = 0, string market = "") { return DownloadData>(_builder.GetAlbumTracks(id, limit, offset, market)); } /// /// Get Spotify catalog information about an album’s tracks asynchronously. Optional parameters can be used to limit the number of /// tracks returned. /// /// The Spotify ID for the album. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first track to return. Default: 0 (the first object). /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public Task> GetAlbumTracksAsync(string id, int limit = 20, int offset = 0, string market = "") { return DownloadDataAsync>(_builder.GetAlbumTracks(id, limit, offset, market)); } /// /// Get Spotify catalog information for a single album. /// /// The Spotify ID for the album. /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public FullAlbum GetAlbum(string id, string market = "") { return DownloadData(_builder.GetAlbum(id, market)); } /// /// Get Spotify catalog information for a single album asynchronously. /// /// The Spotify ID for the album. /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public Task GetAlbumAsync(string id, string market = "") { return DownloadDataAsync(_builder.GetAlbum(id, market)); } /// /// Get Spotify catalog information for multiple albums identified by their Spotify IDs. /// /// A list of the Spotify IDs for the albums. Maximum: 20 IDs. /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public SeveralAlbums GetSeveralAlbums(List ids, string market = "") { return DownloadData(_builder.GetSeveralAlbums(ids, market)); } /// /// Get Spotify catalog information for multiple albums identified by their Spotify IDs asynchrously. /// /// A list of the Spotify IDs for the albums. Maximum: 20 IDs. /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public Task GetSeveralAlbumsAsync(List ids, string market = "") { return DownloadDataAsync(_builder.GetSeveralAlbums(ids, market)); } #endregion Albums #region Artists /// /// Get Spotify catalog information for a single artist identified by their unique Spotify ID. /// /// The Spotify ID for the artist. /// public FullArtist GetArtist(string id) { return DownloadData(_builder.GetArtist(id)); } /// /// Get Spotify catalog information for a single artist identified by their unique Spotify ID asynchronously. /// /// The Spotify ID for the artist. /// public Task GetArtistAsync(string id) { return DownloadDataAsync(_builder.GetArtist(id)); } /// /// Get Spotify catalog information about artists similar to a given artist. Similarity is based on analysis of the /// Spotify community’s listening history. /// /// The Spotify ID for the artist. /// public SeveralArtists GetRelatedArtists(string id) { return DownloadData(_builder.GetRelatedArtists(id)); } /// /// Get Spotify catalog information about artists similar to a given artist asynchronously. Similarity is based on analysis of the /// Spotify community’s listening history. /// /// The Spotify ID for the artist. /// public Task GetRelatedArtistsAsync(string id) { return DownloadDataAsync(_builder.GetRelatedArtists(id)); } /// /// Get Spotify catalog information about an artist’s top tracks by country. /// /// The Spotify ID for the artist. /// The country: an ISO 3166-1 alpha-2 country code. /// public SeveralTracks GetArtistsTopTracks(string id, string country) { return DownloadData(_builder.GetArtistsTopTracks(id, country)); } /// /// Get Spotify catalog information about an artist’s top tracks by country asynchronously. /// /// The Spotify ID for the artist. /// The country: an ISO 3166-1 alpha-2 country code. /// public Task GetArtistsTopTracksAsync(string id, string country) { return DownloadDataAsync(_builder.GetArtistsTopTracks(id, country)); } /// /// Get Spotify catalog information about an artist’s albums. Optional parameters can be specified in the query string /// to filter and sort the response. /// /// The Spotify ID for the artist. /// /// A list of keywords that will be used to filter the response. If not supplied, all album types will /// be returned /// /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first album to return. Default: 0 /// /// An ISO 3166-1 alpha-2 country code. Supply this parameter to limit the response to one particular /// geographical market /// /// public Paging GetArtistsAlbums(string id, AlbumType type = AlbumType.All, int limit = 20, int offset = 0, string market = "") { return DownloadData>(_builder.GetArtistsAlbums(id, type, limit, offset, market)); } /// /// Get Spotify catalog information about an artist’s albums asynchronously. Optional parameters can be specified in the query string /// to filter and sort the response. /// /// The Spotify ID for the artist. /// /// A list of keywords that will be used to filter the response. If not supplied, all album types will /// be returned /// /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first album to return. Default: 0 /// /// An ISO 3166-1 alpha-2 country code. Supply this parameter to limit the response to one particular /// geographical market /// /// public Task> GetArtistsAlbumsAsync(string id, AlbumType type = AlbumType.All, int limit = 20, int offset = 0, string market = "") { return DownloadDataAsync>(_builder.GetArtistsAlbums(id, type, limit, offset, market)); } /// /// Get Spotify catalog information for several artists based on their Spotify IDs. /// /// A list of the Spotify IDs for the artists. Maximum: 50 IDs. /// public SeveralArtists GetSeveralArtists(List ids) { return DownloadData(_builder.GetSeveralArtists(ids)); } /// /// Get Spotify catalog information for several artists based on their Spotify IDs asynchronously. /// /// A list of the Spotify IDs for the artists. Maximum: 50 IDs. /// public Task GetSeveralArtistsAsync(List ids) { return DownloadDataAsync(_builder.GetSeveralArtists(ids)); } #endregion Artists #region Browse /// /// Get a list of Spotify featured playlists (shown, for example, on a Spotify player’s “Browse” tab). /// /// /// The desired language, consisting of a lowercase ISO 639 language code and an uppercase ISO 3166-1 /// alpha-2 country code, joined by an underscore. /// /// A country: an ISO 3166-1 alpha-2 country code. /// A timestamp in ISO 8601 format /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first item to return. Default: 0 /// AUTH NEEDED public FeaturedPlaylists GetFeaturedPlaylists(string locale = "", string country = "", DateTime timestamp = default(DateTime), int limit = 20, int offset = 0) { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetFeaturedPlaylists"); return DownloadData(_builder.GetFeaturedPlaylists(locale, country, timestamp, limit, offset)); } /// /// Get a list of Spotify featured playlists asynchronously (shown, for example, on a Spotify player’s “Browse” tab). /// /// /// The desired language, consisting of a lowercase ISO 639 language code and an uppercase ISO 3166-1 /// alpha-2 country code, joined by an underscore. /// /// A country: an ISO 3166-1 alpha-2 country code. /// A timestamp in ISO 8601 format /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first item to return. Default: 0 /// AUTH NEEDED public Task GetFeaturedPlaylistsAsync(string locale = "", string country = "", DateTime timestamp = default(DateTime), int limit = 20, int offset = 0) { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetFeaturedPlaylists"); return DownloadDataAsync(_builder.GetFeaturedPlaylists(locale, country, timestamp, limit, offset)); } /// /// Get a list of new album releases featured in Spotify (shown, for example, on a Spotify player’s “Browse” tab). /// /// A country: an ISO 3166-1 alpha-2 country code. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first item to return. Default: 0 /// /// AUTH NEEDED public NewAlbumReleases GetNewAlbumReleases(string country = "", int limit = 20, int offset = 0) { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetNewAlbumReleases"); return DownloadData(_builder.GetNewAlbumReleases(country, limit, offset)); } /// /// Get a list of new album releases featured in Spotify asynchronously (shown, for example, on a Spotify player’s “Browse” tab). /// /// A country: an ISO 3166-1 alpha-2 country code. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first item to return. Default: 0 /// /// AUTH NEEDED public Task GetNewAlbumReleasesAsync(string country = "", int limit = 20, int offset = 0) { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetNewAlbumReleases"); return DownloadDataAsync(_builder.GetNewAlbumReleases(country, limit, offset)); } /// /// Get a list of categories used to tag items in Spotify (on, for example, the Spotify player’s “Browse” tab). /// /// /// A country: an ISO 3166-1 alpha-2 country code. Provide this parameter if you want to narrow the /// list of returned categories to those relevant to a particular country /// /// /// The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2 country /// code, joined by an underscore /// /// The maximum number of categories to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first item to return. Default: 0 (the first object). /// /// AUTH NEEDED public CategoryList GetCategories(string country = "", string locale = "", int limit = 20, int offset = 0) { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetCategories"); return DownloadData(_builder.GetCategories(country, locale, limit, offset)); } /// /// Get a list of categories used to tag items in Spotify asynchronously (on, for example, the Spotify player’s “Browse” tab). /// /// /// A country: an ISO 3166-1 alpha-2 country code. Provide this parameter if you want to narrow the /// list of returned categories to those relevant to a particular country /// /// /// The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2 country /// code, joined by an underscore /// /// The maximum number of categories to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first item to return. Default: 0 (the first object). /// /// AUTH NEEDED public Task GetCategoriesAsync(string country = "", string locale = "", int limit = 20, int offset = 0) { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetCategories"); return DownloadDataAsync(_builder.GetCategories(country, locale, limit, offset)); } /// /// Get a single category used to tag items in Spotify (on, for example, the Spotify player’s “Browse” tab). /// /// The Spotify category ID for the category. /// /// A country: an ISO 3166-1 alpha-2 country code. Provide this parameter to ensure that the category /// exists for a particular country. /// /// /// The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2 country /// code, joined by an underscore /// /// /// AUTH NEEDED public Category GetCategory(string categoryId, string country = "", string locale = "") { return DownloadData(_builder.GetCategory(categoryId, country, locale)); } /// /// Get a single category used to tag items in Spotify asynchronously (on, for example, the Spotify player’s “Browse” tab). /// /// The Spotify category ID for the category. /// /// A country: an ISO 3166-1 alpha-2 country code. Provide this parameter to ensure that the category /// exists for a particular country. /// /// /// The desired language, consisting of an ISO 639 language code and an ISO 3166-1 alpha-2 country /// code, joined by an underscore /// /// /// AUTH NEEDED public Task GetCategoryAsync(string categoryId, string country = "", string locale = "") { return DownloadDataAsync(_builder.GetCategory(categoryId, country, locale)); } /// /// Get a list of Spotify playlists tagged with a particular category. /// /// The Spotify category ID for the category. /// A country: an ISO 3166-1 alpha-2 country code. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first item to return. Default: 0 /// /// AUTH NEEDED public CategoryPlaylist GetCategoryPlaylists(string categoryId, string country = "", int limit = 20, int offset = 0) { return DownloadData(_builder.GetCategoryPlaylists(categoryId, country, limit, offset)); } /// /// Get a list of Spotify playlists tagged with a particular category asynchronously. /// /// The Spotify category ID for the category. /// A country: an ISO 3166-1 alpha-2 country code. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first item to return. Default: 0 /// /// AUTH NEEDED public Task GetCategoryPlaylistsAsync(string categoryId, string country = "", int limit = 20, int offset = 0) { return DownloadDataAsync(_builder.GetCategoryPlaylists(categoryId, country, limit, offset)); } /// /// Create a playlist-style listening experience based on seed artists, tracks and genres. /// /// A comma separated list of Spotify IDs for seed artists. /// Up to 5 seed values may be provided in any combination of seed_artists, seed_tracks and seed_genres. /// /// A comma separated list of any genres in the set of available genre seeds. /// Up to 5 seed values may be provided in any combination of seed_artists, seed_tracks and seed_genres. /// /// A comma separated list of Spotify IDs for a seed track. /// Up to 5 seed values may be provided in any combination of seed_artists, seed_tracks and seed_genres. /// /// Tracks with the attribute values nearest to the target values will be preferred. /// For each tunable track attribute, a hard floor on the selected track attribute’s value can be provided /// For each tunable track attribute, a hard ceiling on the selected track attribute’s value can be provided /// The target size of the list of recommended tracks. Default: 20. Minimum: 1. Maximum: 100. /// For seeds with unusually small pools or when highly restrictive filtering is applied, it may be impossible to generate the requested number of recommended tracks. /// /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// Because min_*, max_* and target_* are applied to pools before relinking, the generated results may not precisely match the filters applied. /// /// AUTH NEEDED public Recommendations GetRecommendations(List artistSeed = null, List genreSeed = null, List trackSeed = null, TuneableTrack target = null, TuneableTrack min = null, TuneableTrack max = null, int limit = 20, string market = "") { return DownloadData(_builder.GetRecommendations(artistSeed, genreSeed, trackSeed, target, min, max, limit, market)); } /// /// Create a playlist-style listening experience based on seed artists, tracks and genres asynchronously. /// /// A comma separated list of Spotify IDs for seed artists. /// Up to 5 seed values may be provided in any combination of seed_artists, seed_tracks and seed_genres. /// /// A comma separated list of any genres in the set of available genre seeds. /// Up to 5 seed values may be provided in any combination of seed_artists, seed_tracks and seed_genres. /// /// A comma separated list of Spotify IDs for a seed track. /// Up to 5 seed values may be provided in any combination of seed_artists, seed_tracks and seed_genres. /// /// Tracks with the attribute values nearest to the target values will be preferred. /// For each tunable track attribute, a hard floor on the selected track attribute’s value can be provided /// For each tunable track attribute, a hard ceiling on the selected track attribute’s value can be provided /// The target size of the list of recommended tracks. Default: 20. Minimum: 1. Maximum: 100. /// For seeds with unusually small pools or when highly restrictive filtering is applied, it may be impossible to generate the requested number of recommended tracks. /// /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// Because min_*, max_* and target_* are applied to pools before relinking, the generated results may not precisely match the filters applied. /// /// AUTH NEEDED public Task GetRecommendationsAsync(List artistSeed = null, List genreSeed = null, List trackSeed = null, TuneableTrack target = null, TuneableTrack min = null, TuneableTrack max = null, int limit = 20, string market = "") { return DownloadDataAsync(_builder.GetRecommendations(artistSeed, genreSeed, trackSeed, target, min, max, limit, market)); } /// /// Retrieve a list of available genres seed parameter values for recommendations. /// /// /// AUTH NEEDED public RecommendationSeedGenres GetRecommendationSeedsGenres() { return DownloadData(_builder.GetRecommendationSeedsGenres()); } /// /// Retrieve a list of available genres seed parameter values for recommendations asynchronously. /// /// /// AUTH NEEDED public Task GetRecommendationSeedsGenresAsync() { return DownloadDataAsync(_builder.GetRecommendationSeedsGenres()); } #endregion Browse #region Follow /// /// Get the current user’s followed artists. /// /// The ID type: currently only artist is supported. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The last artist ID retrieved from the previous request. /// /// AUTH NEEDED public FollowedArtists GetFollowedArtists(FollowType followType, int limit = 20, string after = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetFollowedArtists"); return DownloadData(_builder.GetFollowedArtists(limit, after)); } /// /// Get the current user’s followed artists asynchronously. /// /// The ID type: currently only artist is supported. /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// The last artist ID retrieved from the previous request. /// /// AUTH NEEDED public Task GetFollowedArtistsAsync(FollowType followType, int limit = 20, string after = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetFollowedArtists"); return DownloadDataAsync(_builder.GetFollowedArtists(limit, after)); } /// /// Add the current user as a follower of one or more artists or other Spotify users. /// /// The ID type: either artist or user. /// A list of the artist or the user Spotify IDs /// /// AUTH NEEDED public ErrorResponse Follow(FollowType followType, List ids) { JObject ob = new JObject { {"ids", new JArray(ids)} }; return UploadData(_builder.Follow(followType), ob.ToString(Formatting.None), "PUT") ?? new ErrorResponse(); } /// /// Add the current user as a follower of one or more artists or other Spotify users asynchronously. /// /// The ID type: either artist or user. /// A list of the artist or the user Spotify IDs /// /// AUTH NEEDED public async Task FollowAsync(FollowType followType, List ids) { JObject ob = new JObject { {"ids", new JArray(ids)} }; return (await UploadDataAsync(_builder.Follow(followType), ob.ToString(Formatting.None), "PUT").ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Add the current user as a follower of one or more artists or other Spotify users. /// /// The ID type: either artist or user. /// Artists or the Users Spotify ID /// /// AUTH NEEDED public ErrorResponse Follow(FollowType followType, string id) { return Follow(followType, new List { id }); } /// /// Add the current user as a follower of one or more artists or other Spotify users asynchronously. /// /// The ID type: either artist or user. /// Artists or the Users Spotify ID /// /// AUTH NEEDED public Task FollowAsync(FollowType followType, string id) { return FollowAsync(followType, new List { id }); } /// /// Remove the current user as a follower of one or more artists or other Spotify users. /// /// The ID type: either artist or user. /// A list of the artist or the user Spotify IDs /// /// AUTH NEEDED public ErrorResponse Unfollow(FollowType followType, List ids) { JObject ob = new JObject { {"ids", new JArray(ids)} }; return UploadData(_builder.Unfollow(followType), ob.ToString(Formatting.None), "DELETE") ?? new ErrorResponse(); } /// /// Remove the current user as a follower of one or more artists or other Spotify users asynchronously. /// /// The ID type: either artist or user. /// A list of the artist or the user Spotify IDs /// /// AUTH NEEDED public async Task UnfollowAsync(FollowType followType, List ids) { JObject ob = new JObject { {"ids", new JArray(ids)} }; return (await UploadDataAsync(_builder.Unfollow(followType), ob.ToString(Formatting.None), "DELETE").ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Remove the current user as a follower of one or more artists or other Spotify users. /// /// The ID type: either artist or user. /// Artists or the Users Spotify ID /// /// AUTH NEEDED public ErrorResponse Unfollow(FollowType followType, string id) { return Unfollow(followType, new List { id }); } /// /// Remove the current user as a follower of one or more artists or other Spotify users asynchronously. /// /// The ID type: either artist or user. /// Artists or the Users Spotify ID /// /// AUTH NEEDED public Task UnfollowAsync(FollowType followType, string id) { return UnfollowAsync(followType, new List { id }); } /// /// Check to see if the current user is following one or more artists or other Spotify users. /// /// The ID type: either artist or user. /// A list of the artist or the user Spotify IDs to check /// /// AUTH NEEDED public ListResponse IsFollowing(FollowType followType, List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for IsFollowing"); var url = _builder.IsFollowing(followType, ids); return DownloadList(url); } /// /// Check to see if the current user is following one or more artists or other Spotify users asynchronously. /// /// The ID type: either artist or user. /// A list of the artist or the user Spotify IDs to check /// /// AUTH NEEDED public Task> IsFollowingAsync(FollowType followType, List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for IsFollowing"); var url = _builder.IsFollowing(followType, ids); return DownloadListAsync(url); } /// /// Check to see if the current user is following one artist or another Spotify user. /// /// The ID type: either artist or user. /// Artists or the Users Spotify ID /// /// AUTH NEEDED public ListResponse IsFollowing(FollowType followType, string id) { return IsFollowing(followType, new List { id }); } /// /// Check to see if the current user is following one artist or another Spotify user asynchronously. /// /// The ID type: either artist or user. /// Artists or the Users Spotify ID /// /// AUTH NEEDED public Task> IsFollowingAsync(FollowType followType, string id) { return IsFollowingAsync(followType, new List { id }); } /// /// Add the current user as a follower of a playlist. /// /// The Spotify user ID of the person who owns the playlist. /// /// The Spotify ID of the playlist. Any playlist can be followed, regardless of its public/private /// status, as long as you know its playlist ID. /// /// /// If true the playlist will be included in user's public playlists, if false it will remain /// private. /// /// /// AUTH NEEDED public ErrorResponse FollowPlaylist(string ownerId, string playlistId, bool showPublic = true) { JObject body = new JObject { {"public", showPublic} }; return UploadData(_builder.FollowPlaylist(ownerId, playlistId, showPublic), body.ToString(Formatting.None), "PUT"); } /// /// Add the current user as a follower of a playlist asynchronously. /// /// The Spotify user ID of the person who owns the playlist. /// /// The Spotify ID of the playlist. Any playlist can be followed, regardless of its public/private /// status, as long as you know its playlist ID. /// /// /// If true the playlist will be included in user's public playlists, if false it will remain /// private. /// /// /// AUTH NEEDED public Task FollowPlaylistAsync(string ownerId, string playlistId, bool showPublic = true) { JObject body = new JObject { {"public", showPublic} }; return UploadDataAsync(_builder.FollowPlaylist(ownerId, playlistId, showPublic), body.ToString(Formatting.None), "PUT"); } /// /// Remove the current user as a follower of a playlist. /// /// The Spotify user ID of the person who owns the playlist. /// The Spotify ID of the playlist that is to be no longer followed. /// /// AUTH NEEDED public ErrorResponse UnfollowPlaylist(string ownerId, string playlistId) { return UploadData(_builder.UnfollowPlaylist(ownerId, playlistId), "", "DELETE"); } /// /// Remove the current user as a follower of a playlist asynchronously. /// /// The Spotify user ID of the person who owns the playlist. /// The Spotify ID of the playlist that is to be no longer followed. /// /// AUTH NEEDED public Task UnfollowPlaylistAsync(string ownerId, string playlistId) { return UploadDataAsync(_builder.UnfollowPlaylist(ownerId, playlistId), "", "DELETE"); } /// /// Check to see if one or more Spotify users are following a specified playlist. /// /// The Spotify user ID of the person who owns the playlist. /// The Spotify ID of the playlist. /// A list of Spotify User IDs /// /// AUTH NEEDED public ListResponse IsFollowingPlaylist(string ownerId, string playlistId, List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for IsFollowingPlaylist"); var url = _builder.IsFollowingPlaylist(ownerId, playlistId, ids); return DownloadList(url); } /// /// Check to see if one or more Spotify users are following a specified playlist asynchronously. /// /// The Spotify user ID of the person who owns the playlist. /// The Spotify ID of the playlist. /// A list of Spotify User IDs /// /// AUTH NEEDED public Task> IsFollowingPlaylistAsync(string ownerId, string playlistId, List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for IsFollowingPlaylist"); var url = _builder.IsFollowingPlaylist(ownerId, playlistId, ids); return DownloadListAsync(url); } /// /// Check to see if one or more Spotify users are following a specified playlist. /// /// The Spotify user ID of the person who owns the playlist. /// The Spotify ID of the playlist. /// A Spotify User ID /// /// AUTH NEEDED public ListResponse IsFollowingPlaylist(string ownerId, string playlistId, string id) { return IsFollowingPlaylist(ownerId, playlistId, new List { id }); } /// /// Check to see if one or more Spotify users are following a specified playlist asynchronously. /// /// The Spotify user ID of the person who owns the playlist. /// The Spotify ID of the playlist. /// A Spotify User ID /// /// AUTH NEEDED public Task> IsFollowingPlaylistAsync(string ownerId, string playlistId, string id) { return IsFollowingPlaylistAsync(ownerId, playlistId, new List { id }); } #endregion Follow #region Library /// /// Save one or more tracks to the current user’s “Your Music” library. /// /// A list of the Spotify IDs /// /// AUTH NEEDED public ErrorResponse SaveTracks(List ids) { JArray array = new JArray(ids); return UploadData(_builder.SaveTracks(), array.ToString(Formatting.None), "PUT") ?? new ErrorResponse(); } /// /// Save one or more tracks to the current user’s “Your Music” library asynchronously. /// /// A list of the Spotify IDs /// /// AUTH NEEDED public async Task SaveTracksAsync(List ids) { JArray array = new JArray(ids); return (await UploadDataAsync(_builder.SaveTracks(), array.ToString(Formatting.None), "PUT").ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Save one track to the current user’s “Your Music” library. /// /// A Spotify ID /// /// AUTH NEEDED public ErrorResponse SaveTrack(string id) { return SaveTracks(new List { id }); } /// /// Save one track to the current user’s “Your Music” library asynchronously. /// /// A Spotify ID /// /// AUTH NEEDED public Task SaveTrackAsync(string id) { return SaveTracksAsync(new List { id }); } /// /// Get a list of the songs saved in the current Spotify user’s “Your Music” library. /// /// The maximum number of objects to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first object to return. Default: 0 (i.e., the first object) /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// /// AUTH NEEDED public Paging GetSavedTracks(int limit = 20, int offset = 0, string market = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetSavedTracks"); return DownloadData>(_builder.GetSavedTracks(limit, offset, market)); } /// /// Get a list of the songs saved in the current Spotify user’s “Your Music” library asynchronously. /// /// The maximum number of objects to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first object to return. Default: 0 (i.e., the first object) /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// /// AUTH NEEDED public Task> GetSavedTracksAsync(int limit = 20, int offset = 0, string market = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetSavedTracks"); return DownloadDataAsync>(_builder.GetSavedTracks(limit, offset, market)); } /// /// Remove one or more tracks from the current user’s “Your Music” library. /// /// A list of the Spotify IDs. /// /// AUTH NEEDED public ErrorResponse RemoveSavedTracks(List ids) { JArray array = new JArray(ids); return UploadData(_builder.RemoveSavedTracks(), array.ToString(Formatting.None), "DELETE") ?? new ErrorResponse(); } /// /// Remove one or more tracks from the current user’s “Your Music” library asynchronously. /// /// A list of the Spotify IDs. /// /// AUTH NEEDED public async Task RemoveSavedTracksAsync(List ids) { JArray array = new JArray(ids); return (await UploadDataAsync(_builder.RemoveSavedTracks(), array.ToString(Formatting.None), "DELETE").ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Check if one or more tracks is already saved in the current Spotify user’s “Your Music” library. /// /// A list of the Spotify IDs. /// /// AUTH NEEDED public ListResponse CheckSavedTracks(List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for CheckSavedTracks"); var url = _builder.CheckSavedTracks(ids); return DownloadList(url); } /// /// Check if one or more tracks is already saved in the current Spotify user’s “Your Music” library asynchronously. /// /// A list of the Spotify IDs. /// /// AUTH NEEDED public Task> CheckSavedTracksAsync(List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for CheckSavedTracks"); var url = _builder.CheckSavedTracks(ids); return DownloadListAsync(url); } /// /// Save one or more albums to the current user’s “Your Music” library. /// /// A list of the Spotify IDs /// /// AUTH NEEDED public ErrorResponse SaveAlbums(List ids) { JArray array = new JArray(ids); return UploadData(_builder.SaveAlbums(), array.ToString(Formatting.None), "PUT") ?? new ErrorResponse(); } /// /// Save one or more albums to the current user’s “Your Music” library asynchronously. /// /// A list of the Spotify IDs /// /// AUTH NEEDED public async Task SaveAlbumsAsync(List ids) { JArray array = new JArray(ids); return (await UploadDataAsync(_builder.SaveAlbums(), array.ToString(Formatting.None), "PUT").ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Save one album to the current user’s “Your Music” library. /// /// A Spotify ID /// /// AUTH NEEDED public ErrorResponse SaveAlbum(string id) { return SaveAlbums(new List { id }); } /// /// Save one album to the current user’s “Your Music” library asynchronously. /// /// A Spotify ID /// /// AUTH NEEDED public Task SaveAlbumAsync(string id) { return SaveAlbumsAsync(new List { id }); } /// /// Get a list of the albums saved in the current Spotify user’s “Your Music” library. /// /// The maximum number of objects to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first object to return. Default: 0 (i.e., the first object) /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// /// AUTH NEEDED public Paging GetSavedAlbums(int limit = 20, int offset = 0, string market = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetSavedAlbums"); return DownloadData>(_builder.GetSavedAlbums(limit, offset, market)); } /// /// Get a list of the albums saved in the current Spotify user’s “Your Music” library asynchronously. /// /// The maximum number of objects to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first object to return. Default: 0 (i.e., the first object) /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// /// AUTH NEEDED public Task> GetSavedAlbumsAsync(int limit = 20, int offset = 0, string market = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetSavedAlbumsAsync"); return DownloadDataAsync>(_builder.GetSavedAlbums(limit, offset, market)); } /// /// Remove one or more albums from the current user’s “Your Music” library. /// /// A list of the Spotify IDs. /// /// AUTH NEEDED public ErrorResponse RemoveSavedAlbums(List ids) { JArray array = new JArray(ids); return UploadData(_builder.RemoveSavedAlbums(), array.ToString(Formatting.None), "DELETE") ?? new ErrorResponse(); } /// /// Remove one or more albums from the current user’s “Your Music” library asynchronously. /// /// A list of the Spotify IDs. /// /// AUTH NEEDED public async Task RemoveSavedAlbumsAsync(List ids) { JArray array = new JArray(ids); return (await UploadDataAsync(_builder.RemoveSavedAlbums(), array.ToString(Formatting.None), "DELETE").ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Check if one or more albums is already saved in the current Spotify user’s “Your Music” library. /// /// A list of the Spotify IDs. /// /// AUTH NEEDED public ListResponse CheckSavedAlbums(List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for CheckSavedTracks"); var url = _builder.CheckSavedAlbums(ids); return DownloadList(url); } /// /// Check if one or more albums is already saved in the current Spotify user’s “Your Music” library asynchronously. /// /// A list of the Spotify IDs. /// /// AUTH NEEDED public Task> CheckSavedAlbumsAsync(List ids) { if (!UseAuth) throw new InvalidOperationException("Auth is required for CheckSavedAlbumsAsync"); var url = _builder.CheckSavedAlbums(ids); return DownloadListAsync(url); } #endregion Library #region Personalization /// /// Get the current user’s top tracks based on calculated affinity. /// /// Over what time frame the affinities are computed. /// Valid values: long_term (calculated from several years of data and including all new data as it becomes available), /// medium_term (approximately last 6 months), short_term (approximately last 4 weeks). /// The number of entities to return. Default: 20. Minimum: 1. Maximum: 50 /// The index of the first entity to return. Default: 0 (i.e., the first track). Use with limit to get the next set of entities. /// /// AUTH NEEDED public Paging GetUsersTopTracks(TimeRangeType timeRange = TimeRangeType.MediumTerm, int limit = 20, int offest = 0) { return DownloadData>(_builder.GetUsersTopTracks(timeRange, limit, offest)); } /// /// Get the current user’s top tracks based on calculated affinity asynchronously. /// /// Over what time frame the affinities are computed. /// Valid values: long_term (calculated from several years of data and including all new data as it becomes available), /// medium_term (approximately last 6 months), short_term (approximately last 4 weeks). /// The number of entities to return. Default: 20. Minimum: 1. Maximum: 50 /// The index of the first entity to return. Default: 0 (i.e., the first track). Use with limit to get the next set of entities. /// /// AUTH NEEDED public Task> GetUsersTopTracksAsync(TimeRangeType timeRange = TimeRangeType.MediumTerm, int limit = 20, int offest = 0) { return DownloadDataAsync>(_builder.GetUsersTopTracks(timeRange, limit, offest)); } /// /// Get the current user’s top artists based on calculated affinity. /// /// Over what time frame the affinities are computed. /// Valid values: long_term (calculated from several years of data and including all new data as it becomes available), /// medium_term (approximately last 6 months), short_term (approximately last 4 weeks). /// The number of entities to return. Default: 20. Minimum: 1. Maximum: 50 /// The index of the first entity to return. Default: 0 (i.e., the first track). Use with limit to get the next set of entities. /// /// AUTH NEEDED public Paging GetUsersTopArtists(TimeRangeType timeRange = TimeRangeType.MediumTerm, int limit = 20, int offest = 0) { return DownloadData>(_builder.GetUsersTopArtists(timeRange, limit, offest)); } /// /// Get the current user’s top artists based on calculated affinity asynchronously. /// /// Over what time frame the affinities are computed. /// Valid values: long_term (calculated from several years of data and including all new data as it becomes available), /// medium_term (approximately last 6 months), short_term (approximately last 4 weeks). /// The number of entities to return. Default: 20. Minimum: 1. Maximum: 50 /// The index of the first entity to return. Default: 0 (i.e., the first track). Use with limit to get the next set of entities. /// /// AUTH NEEDED public Task> GetUsersTopArtistsAsync(TimeRangeType timeRange = TimeRangeType.MediumTerm, int limit = 20, int offest = 0) { return DownloadDataAsync>(_builder.GetUsersTopArtists(timeRange, limit, offest)); } /// /// Get tracks from the current user’s recent play history. /// /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// A Unix timestamp in milliseconds. Returns all items after (but not including) this cursor position. If after is specified, before must not be specified. /// A Unix timestamp in milliseconds. Returns all items before (but not including) this cursor position. If before is specified, after must not be specified. /// /// AUTH NEEDED public CursorPaging GetUsersRecentlyPlayedTracks(int limit = 20, DateTime? after = null, DateTime? before = null) { return DownloadData>(_builder.GetUsersRecentlyPlayedTracks(limit, after, before)); } /// /// Get tracks from the current user’s recent play history asynchronously /// /// The maximum number of items to return. Default: 20. Minimum: 1. Maximum: 50. /// A Unix timestamp in milliseconds. Returns all items after (but not including) this cursor position. If after is specified, before must not be specified. /// A Unix timestamp in milliseconds. Returns all items before (but not including) this cursor position. If before is specified, after must not be specified. /// /// AUTH NEEDED public Task> GetUsersRecentlyPlayedTracksAsync(int limit = 20, DateTime? after = null, DateTime? before = null) { return DownloadDataAsync>(_builder.GetUsersRecentlyPlayedTracks(limit, after, before)); } #endregion #region Playlists /// /// Get a list of the playlists owned or followed by a Spotify user. /// /// The user's Spotify user ID. /// The maximum number of playlists to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first playlist to return. Default: 0 (the first object) /// /// AUTH NEEDED public Paging GetUserPlaylists(string userId, int limit = 20, int offset = 0) { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetUserPlaylists"); return DownloadData>(_builder.GetUserPlaylists(userId, limit, offset)); } /// /// Get a list of the playlists owned or followed by a Spotify user asynchronously. /// /// The user's Spotify user ID. /// The maximum number of playlists to return. Default: 20. Minimum: 1. Maximum: 50. /// The index of the first playlist to return. Default: 0 (the first object) /// /// AUTH NEEDED public Task> GetUserPlaylistsAsync(string userId, int limit = 20, int offset = 0) { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetUserPlaylists"); return DownloadDataAsync>(_builder.GetUserPlaylists(userId, limit, offset)); } /// /// Get a playlist owned by a Spotify user. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// /// Filters for the query: a comma-separated list of the fields to return. If omitted, all fields are /// returned. /// /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// /// AUTH NEEDED public FullPlaylist GetPlaylist(string userId, string playlistId, string fields = "", string market = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetPlaylist"); return DownloadData(_builder.GetPlaylist(userId, playlistId, fields, market)); } /// /// Get a playlist owned by a Spotify user asynchronously. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// /// Filters for the query: a comma-separated list of the fields to return. If omitted, all fields are /// returned. /// /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// /// AUTH NEEDED public Task GetPlaylistAsync(string userId, string playlistId, string fields = "", string market = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetPlaylist"); return DownloadDataAsync(_builder.GetPlaylist(userId, playlistId, fields, market)); } /// /// Get full details of the tracks of a playlist owned by a Spotify user. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// /// Filters for the query: a comma-separated list of the fields to return. If omitted, all fields are /// returned. /// /// The maximum number of tracks to return. Default: 100. Minimum: 1. Maximum: 100. /// The index of the first object to return. Default: 0 (i.e., the first object) /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// /// AUTH NEEDED public Paging GetPlaylistTracks(string userId, string playlistId, string fields = "", int limit = 100, int offset = 0, string market = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetPlaylistTracks"); return DownloadData>(_builder.GetPlaylistTracks(userId, playlistId, fields, limit, offset, market)); } /// /// Get full details of the tracks of a playlist owned by a Spotify user asyncronously. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// /// Filters for the query: a comma-separated list of the fields to return. If omitted, all fields are /// returned. /// /// The maximum number of tracks to return. Default: 100. Minimum: 1. Maximum: 100. /// The index of the first object to return. Default: 0 (i.e., the first object) /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// /// AUTH NEEDED public Task> GetPlaylistTracksAsync(string userId, string playlistId, string fields = "", int limit = 100, int offset = 0, string market = "") { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetPlaylistTracks"); return DownloadDataAsync>(_builder.GetPlaylistTracks(userId, playlistId, fields, limit, offset, market)); } /// /// Create a playlist for a Spotify user. (The playlist will be empty until you add tracks.) /// /// The user's Spotify user ID. /// /// The name for the new playlist, for example "Your Coolest Playlist". This name does not need /// to be unique. /// /// /// default true. If true the playlist will be public, if false it will be private. To be able to /// create private playlists, the user must have granted the playlist-modify-private scope. /// /// If true the playlist will become collaborative and other users will be able to modify the playlist in their Spotify client. /// Note: You can only set collaborative to true on non-public playlists. /// Value for playlist description as displayed in Spotify Clients and in the Web API. /// /// AUTH NEEDED public FullPlaylist CreatePlaylist(string userId, string playlistName, bool isPublic = true, bool isCollaborative = false, string playlistDescription = "") { JObject body = new JObject { {"name", playlistName}, {"public", isPublic}, {"collaborative", isCollaborative}, {"description", playlistDescription} }; return UploadData(_builder.CreatePlaylist(userId, playlistName, isPublic), body.ToString(Formatting.None)); } /// /// Create a playlist for a Spotify user asynchronously. (The playlist will be empty until you add tracks.) /// /// The user's Spotify user ID. /// /// The name for the new playlist, for example "Your Coolest Playlist". This name does not need /// to be unique. /// /// /// default true. If true the playlist will be public, if false it will be private. To be able to /// create private playlists, the user must have granted the playlist-modify-private scope. /// /// If true the playlist will become collaborative and other users will be able to modify the playlist in their Spotify client. /// Note: You can only set collaborative to true on non-public playlists. /// Value for playlist description as displayed in Spotify Clients and in the Web API. /// /// AUTH NEEDED public Task CreatePlaylistAsync(string userId, string playlistName, bool isPublic = true, bool isCollaborative = false, string playlistDescription = "") { JObject body = new JObject { {"name", playlistName}, {"public", isPublic}, {"collaborative", isCollaborative}, {"description", playlistDescription} }; return UploadDataAsync(_builder.CreatePlaylist(userId, playlistName, isPublic), body.ToString(Formatting.None)); } /// /// Change a playlist’s name and public/private state. (The user must, of course, own the playlist.) /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// The new name for the playlist, for example "My New Playlist Title". /// If true the playlist will be public, if false it will be private. /// If true the playlist will become collaborative and other users will be able to modify the playlist in their Spotify client. /// Note: You can only set collaborative to true on non-public playlists. /// Value for playlist description as displayed in Spotify Clients and in the Web API. /// /// AUTH NEEDED public ErrorResponse UpdatePlaylist(string userId, string playlistId, string newName = null, bool? newPublic = null, bool? newCollaborative = null, string newDescription = null) { JObject body = new JObject(); if (newName != null) body.Add("name", newName); if (newPublic != null) body.Add("public", newPublic); if (newCollaborative != null) body.Add("collaborative", newCollaborative); if (newDescription != null) body.Add("description", newDescription); return UploadData(_builder.UpdatePlaylist(userId, playlistId), body.ToString(Formatting.None), "PUT") ?? new ErrorResponse(); } /// /// Change a playlist’s name and public/private state asynchronously. (The user must, of course, own the playlist.) /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// The new name for the playlist, for example "My New Playlist Title". /// If true the playlist will be public, if false it will be private. /// If true the playlist will become collaborative and other users will be able to modify the playlist in their Spotify client. Note: You can only set collaborative to true on non-public playlists. /// Value for playlist description as displayed in Spotify Clients and in the Web API. /// /// AUTH NEEDED public async Task UpdatePlaylistAsync(string userId, string playlistId, string newName = null, bool? newPublic = null, bool? newCollaborative = null, string newDescription = null) { JObject body = new JObject(); if (newName != null) body.Add("name", newName); if (newPublic != null) body.Add("public", newPublic); if (newCollaborative != null) body.Add("collaborative", newCollaborative); if (newDescription != null) body.Add("description", newDescription); return (await UploadDataAsync(_builder.UpdatePlaylist(userId, playlistId), body.ToString(Formatting.None), "PUT").ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Replace all the tracks in a playlist, overwriting its existing tracks. This powerful request can be useful for /// replacing tracks, re-ordering existing tracks, or clearing the playlist. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// A list of Spotify track URIs to set. A maximum of 100 tracks can be set in one request. /// /// AUTH NEEDED public ErrorResponse ReplacePlaylistTracks(string userId, string playlistId, List uris) { JObject body = new JObject { {"uris", new JArray(uris.Take(100))} }; return UploadData(_builder.ReplacePlaylistTracks(userId, playlistId), body.ToString(Formatting.None), "PUT") ?? new ErrorResponse(); } /// /// Replace all the tracks in a playlist asynchronously, overwriting its existing tracks. This powerful request can be useful for /// replacing tracks, re-ordering existing tracks, or clearing the playlist. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// A list of Spotify track URIs to set. A maximum of 100 tracks can be set in one request. /// /// AUTH NEEDED public async Task ReplacePlaylistTracksAsync(string userId, string playlistId, List uris) { JObject body = new JObject { {"uris", new JArray(uris.Take(100))} }; return await (UploadDataAsync(_builder.ReplacePlaylistTracks(userId, playlistId), body.ToString(Formatting.None), "PUT").ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Remove one or more tracks from a user’s playlist. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// /// array of objects containing Spotify URI strings (and their position in the playlist). A maximum of /// 100 objects can be sent at once. /// /// /// AUTH NEEDED public ErrorResponse RemovePlaylistTracks(string userId, string playlistId, List uris) { JObject body = new JObject { {"tracks", JArray.FromObject(uris.Take(100))} }; return UploadData(_builder.RemovePlaylistTracks(userId, playlistId, uris), body.ToString(Formatting.None), "DELETE") ?? new ErrorResponse(); } /// /// Remove one or more tracks from a user’s playlist asynchronously. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// /// array of objects containing Spotify URI strings (and their position in the playlist). A maximum of /// 100 objects can be sent at once. /// /// /// AUTH NEEDED public async Task RemovePlaylistTracksAsync(string userId, string playlistId, List uris) { JObject body = new JObject { {"tracks", JArray.FromObject(uris.Take(100))} }; return await (UploadDataAsync(_builder.RemovePlaylistTracks(userId, playlistId, uris), body.ToString(Formatting.None), "DELETE").ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Remove a track from a user’s playlist. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// Spotify URI /// /// AUTH NEEDED public ErrorResponse RemovePlaylistTrack(string userId, string playlistId, DeleteTrackUri uri) { return RemovePlaylistTracks(userId, playlistId, new List { uri }); } /// /// Remove a track from a user’s playlist asynchronously. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// Spotify URI /// /// AUTH NEEDED public Task RemovePlaylistTrackAsync(string userId, string playlistId, DeleteTrackUri uri) { return RemovePlaylistTracksAsync(userId, playlistId, new List { uri }); } /// /// Add one or more tracks to a user’s playlist. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// A list of Spotify track URIs to add /// The position to insert the tracks, a zero-based index /// /// AUTH NEEDED public ErrorResponse AddPlaylistTracks(string userId, string playlistId, List uris, int? position = null) { JObject body = new JObject { {"uris", JArray.FromObject(uris.Take(100))} }; return UploadData(_builder.AddPlaylistTracks(userId, playlistId, uris, position), body.ToString(Formatting.None)) ?? new ErrorResponse(); } /// /// Add one or more tracks to a user’s playlist asynchronously. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// A list of Spotify track URIs to add /// The position to insert the tracks, a zero-based index /// /// AUTH NEEDED public async Task AddPlaylistTracksAsync(string userId, string playlistId, List uris, int? position = null) { JObject body = new JObject { {"uris", JArray.FromObject(uris.Take(100))} }; return await (UploadDataAsync(_builder.AddPlaylistTracks(userId, playlistId, uris, position), body.ToString(Formatting.None)).ConfigureAwait(false)) ?? new ErrorResponse(); } /// /// Add a track to a user’s playlist. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// A Spotify Track URI /// The position to insert the tracks, a zero-based index /// /// AUTH NEEDED public ErrorResponse AddPlaylistTrack(string userId, string playlistId, string uri, int? position = null) { return AddPlaylistTracks(userId, playlistId, new List { uri }, position); } /// /// Add a track to a user’s playlist asynchronously. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// A Spotify Track URI /// The position to insert the tracks, a zero-based index /// /// AUTH NEEDED public Task AddPlaylistTrackAsync(string userId, string playlistId, string uri, int? position = null) { return AddPlaylistTracksAsync(userId, playlistId, new List { uri }, position); } /// /// Reorder a track or a group of tracks in a playlist. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// The position of the first track to be reordered. /// The position where the tracks should be inserted. /// The amount of tracks to be reordered. Defaults to 1 if not set. /// The playlist's snapshot ID against which you want to make the changes. /// /// AUTH NEEDED public Snapshot ReorderPlaylist(string userId, string playlistId, int rangeStart, int insertBefore, int rangeLength = 1, string snapshotId = "") { JObject body = new JObject { {"range_start", rangeStart}, {"range_length", rangeLength}, {"insert_before", insertBefore} }; if (!string.IsNullOrEmpty(snapshotId)) body.Add("snapshot_id", snapshotId); return UploadData(_builder.ReorderPlaylist(userId, playlistId), body.ToString(Formatting.None), "PUT"); } /// /// Reorder a track or a group of tracks in a playlist asynchronously. /// /// The user's Spotify user ID. /// The Spotify ID for the playlist. /// The position of the first track to be reordered. /// The position where the tracks should be inserted. /// The amount of tracks to be reordered. Defaults to 1 if not set. /// The playlist's snapshot ID against which you want to make the changes. /// /// AUTH NEEDED public Task ReorderPlaylistAsync(string userId, string playlistId, int rangeStart, int insertBefore, int rangeLength = 1, string snapshotId = "") { JObject body = new JObject { {"range_start", rangeStart}, {"range_length", rangeLength}, {"insert_before", insertBefore}, {"snapshot_id", snapshotId} }; if (!string.IsNullOrEmpty(snapshotId)) body.Add("snapshot_id", snapshotId); return UploadDataAsync(_builder.ReorderPlaylist(userId, playlistId), body.ToString(Formatting.None), "PUT"); } #endregion Playlists #region Profiles /// /// Get detailed profile information about the current user (including the current user’s username). /// /// /// AUTH NEEDED public PrivateProfile GetPrivateProfile() { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetPrivateProfile"); return DownloadData(_builder.GetPrivateProfile()); } /// /// Get detailed profile information about the current user asynchronously (including the current user’s username). /// /// /// AUTH NEEDED public Task GetPrivateProfileAsync() { if (!UseAuth) throw new InvalidOperationException("Auth is required for GetPrivateProfile"); return DownloadDataAsync(_builder.GetPrivateProfile()); } /// /// Get public profile information about a Spotify user. /// /// The user's Spotify user ID. /// public PublicProfile GetPublicProfile(string userId) { return DownloadData(_builder.GetPublicProfile(userId)); } /// /// Get public profile information about a Spotify user asynchronously. /// /// The user's Spotify user ID. /// public Task GetPublicProfileAsync(string userId) { return DownloadDataAsync(_builder.GetPublicProfile(userId)); } #endregion Profiles #region Tracks /// /// Get Spotify catalog information for multiple tracks based on their Spotify IDs. /// /// A list of the Spotify IDs for the tracks. Maximum: 50 IDs. /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public SeveralTracks GetSeveralTracks(List ids, string market = "") { return DownloadData(_builder.GetSeveralTracks(ids, market)); } /// /// Get Spotify catalog information for multiple tracks based on their Spotify IDs asynchronously. /// /// A list of the Spotify IDs for the tracks. Maximum: 50 IDs. /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public Task GetSeveralTracksAsync(List ids, string market = "") { return DownloadDataAsync(_builder.GetSeveralTracks(ids, market)); } /// /// Get Spotify catalog information for a single track identified by its unique Spotify ID. /// /// The Spotify ID for the track. /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public FullTrack GetTrack(string id, string market = "") { return DownloadData(_builder.GetTrack(id, market)); } /// /// Get Spotify catalog information for a single track identified by its unique Spotify ID asynchronously. /// /// The Spotify ID for the track. /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public Task GetTrackAsync(string id, string market = "") { return DownloadDataAsync(_builder.GetTrack(id, market)); } /// /// Get a detailed audio analysis for a single track identified by its unique Spotify ID. /// /// The Spotify ID for the track. /// /// AUTH NEEDED public AudioAnalysis GetAudioAnalysis(string id) { return DownloadData(_builder.GetAudioAnalysis(id)); } /// /// Get a detailed audio analysis for a single track identified by its unique Spotify ID asynchronously. /// /// The Spotify ID for the track. /// /// AUTH NEEDED public Task GetAudioAnalysisAsync(string id) { return DownloadDataAsync(_builder.GetAudioAnalysis(id)); } /// /// Get audio feature information for a single track identified by its unique Spotify ID. /// /// The Spotify ID for the track. /// /// AUTH NEEDED public AudioFeatures GetAudioFeatures(string id) { return DownloadData(_builder.GetAudioFeatures(id)); } /// /// Get audio feature information for a single track identified by its unique Spotify ID asynchronously. /// /// The Spotify ID for the track. /// /// AUTH NEEDED public Task GetAudioFeaturesAsync(string id) { return DownloadDataAsync(_builder.GetAudioFeatures(id)); } /// /// Get audio features for multiple tracks based on their Spotify IDs. /// /// A list of Spotify Track-IDs. Maximum: 100 IDs. /// /// AUTH NEEDED public SeveralAudioFeatures GetSeveralAudioFeatures(List ids) { return DownloadData(_builder.GetSeveralAudioFeatures(ids)); } /// /// Get audio features for multiple tracks based on their Spotify IDs asynchronously. /// /// A list of Spotify Track-IDs. Maximum: 100 IDs. /// /// AUTH NEEDED public Task GetSeveralAudioFeaturesAsync(List ids) { return DownloadDataAsync(_builder.GetSeveralAudioFeatures(ids)); } #endregion Tracks #region Player /// /// Get information about a user’s available devices. /// /// public AvailabeDevices GetDevices() { return DownloadData(_builder.GetDevices()); } /// /// Get information about a user’s available devices. /// /// public Task GetDevicesAsync() { return DownloadDataAsync(_builder.GetDevices()); } /// /// Get information about the user’s current playback state, including track, track progress, and active device. /// /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public PlaybackContext GetPlayback(string market = "") { return DownloadData(_builder.GetPlayback(market)); } /// /// Get information about the user’s current playback state, including track, track progress, and active device. /// /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public Task GetPlaybackAsync(string market = "") { return DownloadDataAsync(_builder.GetPlayback(market)); } /// /// Get the object currently being played on the user’s Spotify account. /// /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public PlaybackContext GetPlayingTrack(string market = "") { return DownloadData(_builder.GetPlayingTrack(market)); } /// /// Get the object currently being played on the user’s Spotify account. /// /// An ISO 3166-1 alpha-2 country code. Provide this parameter if you want to apply Track Relinking. /// public Task GetPlayingTrackAsync(string market = "") { return DownloadDataAsync(_builder.GetPlayingTrack(market)); } /// /// Transfer playback to a new device and determine if it should start playing. /// /// ID of the device on which playback should be started/transferred to /// /// true: ensure playback happens on new device. /// false or not provided: keep the current playback state. /// /// public ErrorResponse TransferPlayback(string deviceId, bool play = false) => TransferPlayback( new List { deviceId }, play); /// /// Transfer playback to a new device and determine if it should start playing. /// /// ID of the device on which playback should be started/transferred to /// /// true: ensure playback happens on new device. /// false or not provided: keep the current playback state. /// /// public Task TransferPlaybackAsync(string deviceId, bool play = false) => TransferPlaybackAsync( new List { deviceId }, play); /// /// Transfer playback to a new device and determine if it should start playing. /// NOTE: Although an array is accepted, only a single device_id is currently supported. Supplying more than one will return 400 Bad Request /// /// A array containing the ID of the device on which playback should be started/transferred. /// /// true: ensure playback happens on new device. /// false or not provided: keep the current playback state. /// /// public ErrorResponse TransferPlayback(List deviceIds, bool play = false) { JObject ob = new JObject() { { "play", play }, { "device_ids", new JArray(deviceIds) } }; return UploadData(_builder.TransferPlayback(), ob.ToString(Formatting.None), "PUT"); } /// /// Transfer playback to a new device and determine if it should start playing. /// NOTE: Although an array is accepted, only a single device_id is currently supported. Supplying more than one will return 400 Bad Request /// /// A array containing the ID of the device on which playback should be started/transferred. /// /// true: ensure playback happens on new device. /// false or not provided: keep the current playback state. /// /// public Task TransferPlaybackAsync(List deviceIds, bool play = false) { JObject ob = new JObject() { { "play", play }, { "device_ids", new JArray(deviceIds) } }; return UploadDataAsync(_builder.TransferPlayback(), ob.ToString(Formatting.None), "PUT"); } /// /// Start a new context or resume current playback on the user’s active device. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// Spotify URI of the context to play. /// A JSON array of the Spotify track URIs to play. /// Indicates from where in the context playback should start. /// The starting time to seek the track to /// Only available when context_uri corresponds to an album or playlist object, or when the uris parameter is used. /// public ErrorResponse ResumePlayback(string deviceId = "", string contextUri = "", List uris = null, int? offset = null, int positionMs = 0) { JObject ob = new JObject(); if(!string.IsNullOrEmpty(contextUri)) ob.Add("context_uri", contextUri); if(uris != null) ob.Add("uris", new JArray(uris)); if(offset != null) ob.Add("offset", new JObject { { "position", offset } }); if (positionMs > 0) ob.Add("position_ms", positionMs); return UploadData(_builder.ResumePlayback(deviceId), ob.ToString(Formatting.None), "PUT"); } /// /// Start a new context or resume current playback on the user’s active device. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// Spotify URI of the context to play. /// A JSON array of the Spotify track URIs to play. /// Indicates from where in the context playback should start. /// The starting time to seek the track to /// Only available when context_uri corresponds to an album or playlist object, or when the uris parameter is used. /// public Task ResumePlaybackAsync(string deviceId = "", string contextUri = "", List uris = null, int? offset = null, int positionMs = 0) { JObject ob = new JObject(); if (!string.IsNullOrEmpty(contextUri)) ob.Add("context_uri", contextUri); if (uris != null) ob.Add("uris", new JArray(uris)); if (offset != null) ob.Add("offset", new JObject { { "position", offset } }); if (positionMs > 0) ob.Add("position_ms", positionMs); return UploadDataAsync(_builder.ResumePlayback(deviceId), ob.ToString(Formatting.None), "PUT"); } /// /// Start a new context or resume current playback on the user’s active device. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// Spotify URI of the context to play. /// A JSON array of the Spotify track URIs to play. /// Indicates from where in the context playback should start. /// The starting time to seek the track to /// Only available when context_uri corresponds to an album or playlist object, or when the uris parameter is used. /// public ErrorResponse ResumePlayback(string deviceId = "", string contextUri = "", List uris = null, string offset = "", int positionMs = 0) { JObject ob = new JObject(); if (!string.IsNullOrEmpty(contextUri)) ob.Add("context_uri", contextUri); if (uris != null) ob.Add("uris", new JArray(uris)); if (!string.IsNullOrEmpty(offset)) ob.Add("offset", new JObject {{"uri", offset}}); if (positionMs > 0) ob.Add("position_ms", positionMs); return UploadData(_builder.ResumePlayback(deviceId), ob.ToString(Formatting.None), "PUT"); } /// /// Start a new context or resume current playback on the user’s active device. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// Spotify URI of the context to play. /// A JSON array of the Spotify track URIs to play. /// Indicates from where in the context playback should start. /// The starting time to seek the track to /// Only available when context_uri corresponds to an album or playlist object, or when the uris parameter is used. /// public Task ResumePlaybackAsync(string deviceId = "", string contextUri = "", List uris = null, string offset = "", int positionMs = 0) { JObject ob = new JObject(); if (!string.IsNullOrEmpty(contextUri)) ob.Add("context_uri", contextUri); if (uris != null) ob.Add("uris", new JArray(uris)); if (!string.IsNullOrEmpty(offset)) ob.Add("offset", new JObject { { "uri", offset } }); if (positionMs > 0) ob.Add("position_ms", positionMs); return UploadDataAsync(_builder.ResumePlayback(deviceId), ob.ToString(Formatting.None), "PUT"); } /// /// Pause playback on the user’s account. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public ErrorResponse PausePlayback(string deviceId = "") { return UploadData(_builder.PausePlayback(deviceId), string.Empty, "PUT"); } /// /// Pause playback on the user’s account. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public Task PausePlaybackAsync(string deviceId = "") { return UploadDataAsync(_builder.PausePlayback(deviceId), string.Empty, "PUT"); } /// /// Skips to next track in the user’s queue. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public ErrorResponse SkipPlaybackToNext(string deviceId = "") { return UploadData(_builder.SkipPlaybackToNext(deviceId), string.Empty); } /// /// Skips to next track in the user’s queue. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public Task SkipPlaybackToNextAsync(string deviceId = "") { return UploadDataAsync(_builder.SkipPlaybackToNext(deviceId), string.Empty); } /// /// Skips to previous track in the user’s queue. /// Note that this will ALWAYS skip to the previous track, regardless of the current track’s progress. /// Returning to the start of the current track should be performed using the https://api.spotify.com/v1/me/player/seek endpoint. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public ErrorResponse SkipPlaybackToPrevious(string deviceId = "") { return UploadData(_builder.SkipPlaybackToPrevious(deviceId), string.Empty); } /// /// Skips to previous track in the user’s queue. /// Note that this will ALWAYS skip to the previous track, regardless of the current track’s progress. /// Returning to the start of the current track should be performed using the https://api.spotify.com/v1/me/player/seek endpoint. /// /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public Task SkipPlaybackToPreviousAsync(string deviceId = "") { return UploadDataAsync(_builder.SkipPlaybackToPrevious(deviceId), string.Empty); } /// /// Seeks to the given position in the user’s currently playing track. /// /// The position in milliseconds to seek to. 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. /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public ErrorResponse SeekPlayback(int positionMs, string deviceId = "") { return UploadData(_builder.SeekPlayback(positionMs, deviceId), string.Empty, "PUT"); } /// /// Seeks to the given position in the user’s currently playing track. /// /// The position in milliseconds to seek to. 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. /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public Task SeekPlaybackAsync(int positionMs, string deviceId = "") { return UploadDataAsync(_builder.SeekPlayback(positionMs, deviceId), string.Empty, "PUT"); } /// /// Set the repeat mode for the user’s playback. Options are repeat-track, repeat-context, and off. /// /// track, context or off. /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public ErrorResponse SetRepeatMode(RepeatState state, string deviceId = "") { return UploadData(_builder.SetRepeatMode(state, deviceId), string.Empty, "PUT"); } /// /// Set the repeat mode for the user’s playback. Options are repeat-track, repeat-context, and off. /// /// track, context or off. /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public Task SetRepeatModeAsync(RepeatState state, string deviceId = "") { return UploadDataAsync(_builder.SetRepeatMode(state, deviceId), string.Empty, "PUT"); } /// /// Set the volume for the user’s current playback device. /// /// Integer. The volume to set. Must be a value from 0 to 100 inclusive. /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public ErrorResponse SetVolume(int volumePercent, string deviceId = "") { return UploadData(_builder.SetVolume(volumePercent, deviceId), string.Empty, "PUT"); } /// /// Set the volume for the user’s current playback device. /// /// Integer. The volume to set. Must be a value from 0 to 100 inclusive. /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public Task SetVolumeAsync(int volumePercent, string deviceId = "") { return UploadDataAsync(_builder.SetVolume(volumePercent, deviceId), string.Empty, "PUT"); } /// /// Toggle shuffle on or off for user’s playback. /// /// True or False /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public ErrorResponse SetShuffle(bool shuffle, string deviceId = "") { return UploadData(_builder.SetShuffle(shuffle, deviceId), string.Empty, "PUT"); } /// /// Toggle shuffle on or off for user’s playback. /// /// True or False /// The id of the device this command is targeting. If not supplied, the user's currently active device is the target. /// public Task SetShuffleAsync(bool shuffle, string deviceId = "") { return UploadDataAsync(_builder.SetShuffle(shuffle, deviceId), string.Empty, "PUT"); } #endregion #region Util public TOut GetNextPage(Paging paging) where TOut : BasicModel { if (!paging.HasNextPage()) throw new InvalidOperationException("This Paging-Object has no Next-Page"); return DownloadData(paging.Next); } public Paging GetNextPage(Paging paging) { return GetNextPage, T>(paging); } public Task GetNextPageAsync(Paging paging) where TOut : BasicModel { if (!paging.HasNextPage()) throw new InvalidOperationException("This Paging-Object has no Next-Page"); return DownloadDataAsync(paging.Next); } public Task> GetNextPageAsync(Paging paging) { return GetNextPageAsync, T>(paging); } public TOut GetPreviousPage(Paging paging) where TOut : BasicModel { if (!paging.HasPreviousPage()) throw new InvalidOperationException("This Paging-Object has no Previous-Page"); return DownloadData(paging.Previous); } public Paging GetPreviousPage(Paging paging) { return GetPreviousPage, T>(paging); } public Task GetPreviousPageAsync(Paging paging) where TOut : BasicModel { if (!paging.HasPreviousPage()) throw new InvalidOperationException("This Paging-Object has no Previous-Page"); return DownloadDataAsync(paging.Previous); } public Task> GetPreviousPageAsync(Paging paging) { return GetPreviousPageAsync, T>(paging); } private ListResponse DownloadList(string url) { int triesLeft = RetryTimes + 1; Error lastError; ListResponse data = null; do { if (data != null) { Thread.Sleep(RetryAfter); } Tuple res = DownloadDataAlt(url); data = ExtractDataToListResponse(res); lastError = data.Error; triesLeft -= 1; } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status)); return data; } private async Task> DownloadListAsync(string url) { int triesLeft = RetryTimes + 1; Error lastError; ListResponse data = null; do { if (data != null) { await Task.Delay(RetryAfter).ConfigureAwait(false); } Tuple res = await DownloadDataAltAsync(url).ConfigureAwait(false); data = ExtractDataToListResponse(res); lastError = data.Error; triesLeft -= 1; } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status)); return data; } private static ListResponse ExtractDataToListResponse(Tuple res) { ListResponse ret; if (res.Item2 is JArray) { ret = new ListResponse { List = res.Item2.ToObject>(), Error = null }; } else { ret = new ListResponse { List = null, Error = res.Item2["error"].ToObject() }; } ret.AddResponseInfo(res.Item1); return ret; } public T UploadData(string url, string uploadData, string method = "POST") where T : BasicModel { if (!UseAuth) throw new InvalidOperationException("Auth is required for all Upload-Actions"); int triesLeft = RetryTimes + 1; Error lastError; Tuple response = null; do { Dictionary headers = new Dictionary { { "Authorization", TokenType + " " + AccessToken}, { "Content-Type", "application/json" } }; if (response != null) { Thread.Sleep(RetryAfter); } response = WebClient.UploadJson(url, uploadData, method, headers); response.Item2.AddResponseInfo(response.Item1); lastError = response.Item2.Error; triesLeft -= 1; } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status)); return response.Item2; } public async Task UploadDataAsync(string url, string uploadData, string method = "POST") where T : BasicModel { if (!UseAuth) throw new InvalidOperationException("Auth is required for all Upload-Actions"); int triesLeft = RetryTimes + 1; Error lastError; Tuple response = null; do { Dictionary headers = new Dictionary { { "Authorization", TokenType + " " + AccessToken}, { "Content-Type", "application/json" } }; if (response != null) { await Task.Delay(RetryAfter).ConfigureAwait(false); } response = await WebClient.UploadJsonAsync(url, uploadData, method, headers).ConfigureAwait(false); response.Item2.AddResponseInfo(response.Item1); lastError = response.Item2.Error; triesLeft -= 1; } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status)); return response.Item2; } public T DownloadData(string url) where T : BasicModel { int triesLeft = RetryTimes + 1; Error lastError; Tuple response = null; do { if(response != null) { Thread.Sleep(RetryAfter); } response = DownloadDataAlt(url); response.Item2.AddResponseInfo(response.Item1); lastError = response.Item2.Error; triesLeft -= 1; } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status)); return response.Item2; } /// /// Retrieves whether request had a "TooManyRequests" error, and get the amount Spotify recommends waiting before another request. /// /// Info object to analyze. /// Seconds to wait before making another request. -1 if no error. /// AUTH NEEDED private int GetTooManyRequests(ResponseInfo info) { // 429 is "TooManyRequests" value specified in Spotify API if (429 != (int)info.StatusCode) { return -1; } if (!int.TryParse(info.Headers.Get("Retry-After"), out var secondsToWait)) { return -1; } return secondsToWait; } public async Task DownloadDataAsync(string url) where T : BasicModel { int triesLeft = RetryTimes + 1; Error lastError; Tuple response = null; do { if (response != null) { int msToWait = RetryAfter; var secondsToWait = GetTooManyRequests(response.Item1); if (secondsToWait > 0) { msToWait = secondsToWait * 1000; } await Task.Delay(msToWait).ConfigureAwait(false); } response = await DownloadDataAltAsync(url).ConfigureAwait(false); response.Item2.AddResponseInfo(response.Item1); lastError = response.Item2.Error; if (TooManyRequestsConsumesARetry || GetTooManyRequests(response.Item1) == -1) { triesLeft -= 1; } } while (UseAutoRetry && triesLeft > 0 && (GetTooManyRequests(response.Item1) != -1 || (lastError != null && RetryErrorCodes.Contains(lastError.Status)))); return response.Item2; } private Tuple DownloadDataAlt(string url) { Dictionary headers = new Dictionary(); if (UseAuth) headers.Add("Authorization", TokenType + " " + AccessToken); return WebClient.DownloadJson(url, headers); } private Task> DownloadDataAltAsync(string url) { Dictionary headers = new Dictionary(); if (UseAuth) headers.Add("Authorization", TokenType + " " + AccessToken); return WebClient.DownloadJsonAsync(url, headers); } #endregion Util } }