diff --git a/README.md b/README.md
index d5e8a69a..e768a974 100644
--- a/README.md
+++ b/README.md
@@ -5,26 +5,26 @@ SpotifyAPI-NET
[![Nuget](https://badge.fury.io/nu/SpotifyAPI-NET.svg)](https://www.nuget.org/packages/SpotifyAPI-NET/)
[![Gitter](https://img.shields.io/gitter/room/SpotifyAPI-NET/Lobby.svg)](https://gitter.im/SpotifyAPI-NET/Lobby)
-An API for the Spotify-Client and Spotify's Web API, written in .NET
+A Wrapper for Spotify's Web API, written in .NET
**Spotify's Web API** ([link](https://developer.spotify.com/web-api/))
> Based on simple REST principles, our Web API endpoints return metadata in JSON format about artists, albums, and tracks directly from the Spotify catalogue.
> The API also provides access to user-related data such as playlists and music saved in a “Your Music” library, subject to user’s authorization.
-**Spotify's *unofficial* Local API**
-> Do you ever wanted to control your local Spotify Client with some sort of API? Now you can! This API gives you full control over your spotify client.
-> You can get infos about the currently playing song, get its Album-Art, skip/pause and much more. It also features multiple Event-Interfaces.
-
### Docs and Usage
More Information, Installation-Instructions, Examples and API-Reference can be found at [github.io/SpotifyAPI-Net/](http://johnnycrazy.github.io/SpotifyAPI-NET/)
### NuGet
-You can add the API to your project via [nuget-package](https://www.nuget.org/packages/SpotifyAPI-NET/):
+You can add the API to your project via [nuget-package](https://www.nuget.org/packages/SpotifyAPI.Web/):
```
-Install-Package SpotifyAPI-NET
+Install-Package SpotifyAPI.Web
+Install-Package SpotifyAPI.Web.Auth
+
//or
-Install-Package SpotifyAPI-Net -pre
+
+Install-Package SpotifyAPI.Web -pre
+Install-Package SpotifyAPI.Web.Auth -pre
```
### Example
diff --git a/SpotifyAPI.Web/Models/BasicModel.cs b/SpotifyAPI.Web/Models/BasicModel.cs
index d6f54697..04727628 100644
--- a/SpotifyAPI.Web/Models/BasicModel.cs
+++ b/SpotifyAPI.Web/Models/BasicModel.cs
@@ -1,4 +1,5 @@
using Newtonsoft.Json;
+using SpotifyAPI.Web.Enums;
using System;
using System.Net;
diff --git a/SpotifyAPI.Web/Models/ResponseInfo.cs b/SpotifyAPI.Web/Models/ResponseInfo.cs
index 1a8a12c2..7a794387 100644
--- a/SpotifyAPI.Web/Models/ResponseInfo.cs
+++ b/SpotifyAPI.Web/Models/ResponseInfo.cs
@@ -1,4 +1,5 @@
-using System.Net;
+using SpotifyAPI.Web.Enums;
+using System.Net;
namespace SpotifyAPI.Web.Models
{
diff --git a/SpotifyAPI.Web/SpotifyWebAPI.cs b/SpotifyAPI.Web/SpotifyWebAPI.cs
index e77af261..57f66acd 100644
--- a/SpotifyAPI.Web/SpotifyWebAPI.cs
+++ b/SpotifyAPI.Web/SpotifyWebAPI.cs
@@ -1,4 +1,4 @@
-using Newtonsoft.Json;
+using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SpotifyAPI.Web.Enums;
using SpotifyAPI.Web.Models;
@@ -23,15 +23,14 @@ namespace SpotifyAPI.Web
{
_builder = new SpotifyWebBuilder();
UseAuth = true;
- WebClient = new SpotifyWebClient
+ WebClient = new SpotifyWebClient(proxyConfig)
{
JsonSettings =
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.All
- },
- ProxyConfig = proxyConfig
+ }
};
}
@@ -79,6 +78,11 @@ namespace SpotifyAPI.Web
///
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.
///
@@ -2001,11 +2005,12 @@ namespace SpotifyAPI.Web
/// 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.
+ /// 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? offset = null, int positionMs = 0)
{
JObject ob = new JObject();
if(!string.IsNullOrEmpty(contextUri))
@@ -2014,6 +2019,8 @@ namespace SpotifyAPI.Web
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");
}
@@ -2023,11 +2030,12 @@ namespace SpotifyAPI.Web
/// 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.
+ /// 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? offset = null, int positionMs = 0)
{
JObject ob = new JObject();
if (!string.IsNullOrEmpty(contextUri))
@@ -2036,6 +2044,8 @@ namespace SpotifyAPI.Web
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");
}
@@ -2045,11 +2055,12 @@ namespace SpotifyAPI.Web
/// 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.
+ /// 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 = "")
+ string offset = "", int positionMs = 0)
{
JObject ob = new JObject();
if (!string.IsNullOrEmpty(contextUri))
@@ -2058,6 +2069,8 @@ namespace SpotifyAPI.Web
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");
}
@@ -2067,11 +2080,12 @@ namespace SpotifyAPI.Web
/// 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.
+ /// 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 = "")
+ string offset = "", int positionMs = 0)
{
JObject ob = new JObject();
if (!string.IsNullOrEmpty(contextUri))
@@ -2080,6 +2094,8 @@ namespace SpotifyAPI.Web
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");
}
@@ -2434,6 +2450,26 @@ namespace SpotifyAPI.Web
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;
@@ -2442,15 +2478,30 @@ namespace SpotifyAPI.Web
Tuple response = null;
do
{
- if (response != null) { await Task.Delay(RetryAfter).ConfigureAwait(false); }
+ 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;
- triesLeft -= 1;
+ if (TooManyRequestsConsumesARetry || GetTooManyRequests(response.Item1) == -1)
+ {
+ triesLeft -= 1;
+ }
- } while (UseAutoRetry && triesLeft > 0 && lastError != null && RetryErrorCodes.Contains(lastError.Status));
+ } while (UseAutoRetry
+ && triesLeft > 0
+ && (GetTooManyRequests(response.Item1) != -1
+ || (lastError != null && RetryErrorCodes.Contains(lastError.Status))));
return response.Item2;
diff --git a/SpotifyAPI.Web/SpotifyWebClient.cs b/SpotifyAPI.Web/SpotifyWebClient.cs
index 3789aa50..7c3a572b 100644
--- a/SpotifyAPI.Web/SpotifyWebClient.cs
+++ b/SpotifyAPI.Web/SpotifyWebClient.cs
@@ -1,4 +1,4 @@
-using Newtonsoft.Json;
+using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
@@ -7,16 +7,21 @@ using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using SpotifyAPI.Web.Models;
+using SpotifyAPI.Web.Enums;
namespace SpotifyAPI.Web
{
internal class SpotifyWebClient : IClient
{
public JsonSerializerSettings JsonSettings { get; set; }
-
- public ProxyConfig ProxyConfig { get; set; }
-
private readonly Encoding _encoding = Encoding.UTF8;
+ private readonly HttpClient _client;
+
+ public SpotifyWebClient(ProxyConfig proxyConfig = null)
+ {
+ HttpClientHandler clientHandler = CreateClientHandler(proxyConfig);
+ _client = new HttpClient(clientHandler);
+ }
public Tuple Download(string url, Dictionary headers = null)
{
@@ -32,47 +37,39 @@ namespace SpotifyAPI.Web
public Tuple DownloadRaw(string url, Dictionary headers = null)
{
- HttpClientHandler clientHandler = CreateClientHandler(ProxyConfig);
- using (HttpClient client = new HttpClient(clientHandler))
+ if (headers != null)
{
- if (headers != null)
+ foreach (KeyValuePair headerPair in headers)
{
- foreach (KeyValuePair headerPair in headers)
- {
- client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
- }
+ _client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
}
- using (HttpResponseMessage response = Task.Run(() => client.GetAsync(url)).Result)
+ }
+ using (HttpResponseMessage response = Task.Run(() => _client.GetAsync(url)).Result)
+ {
+ return new Tuple(new ResponseInfo
{
- return new Tuple(new ResponseInfo
- {
- StatusCode = response.StatusCode,
- Headers = ConvertHeaders(response.Headers)
- }, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result);
- }
+ StatusCode = response.StatusCode,
+ Headers = ConvertHeaders(response.Headers)
+ }, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result);
}
}
public async Task> DownloadRawAsync(string url, Dictionary headers = null)
{
- HttpClientHandler clientHandler = CreateClientHandler(ProxyConfig);
- using (HttpClient client = new HttpClient(clientHandler))
+ if (headers != null)
{
- if (headers != null)
+ foreach (KeyValuePair headerPair in headers)
{
- foreach (KeyValuePair headerPair in headers)
- {
- client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
- }
+ _client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
}
- using (HttpResponseMessage response = await client.GetAsync(url).ConfigureAwait(false))
+ }
+ using (HttpResponseMessage response = await _client.GetAsync(url).ConfigureAwait(false))
+ {
+ return new Tuple(new ResponseInfo
{
- return new Tuple(new ResponseInfo
- {
- StatusCode = response.StatusCode,
- Headers = ConvertHeaders(response.Headers)
- }, await response.Content.ReadAsByteArrayAsync());
- }
+ StatusCode = response.StatusCode,
+ Headers = ConvertHeaders(response.Headers)
+ }, await response.Content.ReadAsByteArrayAsync());
}
}
@@ -102,57 +99,49 @@ namespace SpotifyAPI.Web
public Tuple UploadRaw(string url, string body, string method, Dictionary headers = null)
{
- HttpClientHandler clientHandler = CreateClientHandler(ProxyConfig);
- using (HttpClient client = new HttpClient(clientHandler))
+ if (headers != null)
{
- if (headers != null)
+ foreach (KeyValuePair headerPair in headers)
{
- foreach (KeyValuePair headerPair in headers)
- {
- client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
- }
+ _client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
}
+ }
- HttpRequestMessage message = new HttpRequestMessage(new HttpMethod(method), url)
+ HttpRequestMessage message = new HttpRequestMessage(new HttpMethod(method), url)
+ {
+ Content = new StringContent(body, _encoding)
+ };
+ using (HttpResponseMessage response = Task.Run(() => _client.SendAsync(message)).Result)
+ {
+ return new Tuple(new ResponseInfo
{
- Content = new StringContent(body, _encoding)
- };
- using (HttpResponseMessage response = Task.Run(() => client.SendAsync(message)).Result)
- {
- return new Tuple(new ResponseInfo
- {
- StatusCode = response.StatusCode,
- Headers = ConvertHeaders(response.Headers)
- }, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result);
- }
+ StatusCode = response.StatusCode,
+ Headers = ConvertHeaders(response.Headers)
+ }, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result);
}
}
public async Task> UploadRawAsync(string url, string body, string method, Dictionary headers = null)
{
- HttpClientHandler clientHandler = CreateClientHandler(ProxyConfig);
- using (HttpClient client = new HttpClient(clientHandler))
+ if (headers != null)
{
- if (headers != null)
+ foreach (KeyValuePair headerPair in headers)
{
- foreach (KeyValuePair headerPair in headers)
- {
- client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
- }
+ _client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
}
+ }
- HttpRequestMessage message = new HttpRequestMessage(new HttpMethod(method), url)
+ HttpRequestMessage message = new HttpRequestMessage(new HttpMethod(method), url)
+ {
+ Content = new StringContent(body, _encoding)
+ };
+ using (HttpResponseMessage response = await _client.SendAsync(message))
+ {
+ return new Tuple(new ResponseInfo
{
- Content = new StringContent(body, _encoding)
- };
- using (HttpResponseMessage response = await client.SendAsync(message))
- {
- return new Tuple(new ResponseInfo
- {
- StatusCode = response.StatusCode,
- Headers = ConvertHeaders(response.Headers)
- }, await response.Content.ReadAsByteArrayAsync());
- }
+ StatusCode = response.StatusCode,
+ Headers = ConvertHeaders(response.Headers)
+ }, await response.Content.ReadAsByteArrayAsync());
}
}
@@ -170,6 +159,7 @@ namespace SpotifyAPI.Web
public void Dispose()
{
+ _client.Dispose();
GC.SuppressFinalize(this);
}