mirror of
https://github.com/Sarsoo/Spotify.NET.git
synced 2024-12-23 14:46:26 +00:00
VSCode Formatter - Also bumped tests to net core 3.1
This commit is contained in:
parent
307d69945e
commit
466e61523d
@ -1,30 +1,30 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SpotifyAPI.Web.Auth
|
||||
{
|
||||
internal static class AuthUtil
|
||||
internal static class AuthUtil
|
||||
{
|
||||
public static void OpenBrowser(string url)
|
||||
{
|
||||
public static void OpenBrowser(string url)
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
url = url.Replace("&", "^&");
|
||||
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}"));
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
Process.Start("xdg-open", url);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
Process.Start("open", url);
|
||||
}
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
url = url.Replace("&", "^&");
|
||||
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}"));
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
Process.Start("xdg-open", url);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
Process.Start("open", url);
|
||||
}
|
||||
#else
|
||||
url = url.Replace("&", "^&");
|
||||
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}"));
|
||||
url = url.Replace("&", "^&");
|
||||
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,125 +12,121 @@ using Unosquare.Labs.EmbedIO.Modules;
|
||||
|
||||
namespace SpotifyAPI.Web.Auth
|
||||
{
|
||||
public class AuthorizationCodeAuth : SpotifyAuthServer<AuthorizationCode>
|
||||
public class AuthorizationCodeAuth : SpotifyAuthServer<AuthorizationCode>
|
||||
{
|
||||
public string SecretId { get; set; }
|
||||
|
||||
public ProxyConfig ProxyConfig { get; set; }
|
||||
|
||||
public AuthorizationCodeAuth(string redirectUri, string serverUri, Scope scope = Scope.None, string state = "") : base("code", "AuthorizationCodeAuth", redirectUri, serverUri, scope, state)
|
||||
{ }
|
||||
|
||||
public AuthorizationCodeAuth(string clientId, string secretId, string redirectUri, string serverUri, Scope scope = Scope.None, string state = "") : this(redirectUri, serverUri, scope, state)
|
||||
{
|
||||
public string SecretId { get; set; }
|
||||
|
||||
public ProxyConfig ProxyConfig { get; set; }
|
||||
|
||||
public AuthorizationCodeAuth(string redirectUri, string serverUri, Scope scope = Scope.None, string state = "")
|
||||
: base("code", "AuthorizationCodeAuth", redirectUri, serverUri, scope, state)
|
||||
{
|
||||
}
|
||||
|
||||
public AuthorizationCodeAuth(string clientId, string secretId, string redirectUri, string serverUri, Scope scope = Scope.None, string state = "")
|
||||
: this(redirectUri, serverUri, scope, state)
|
||||
{
|
||||
ClientId = clientId;
|
||||
SecretId = secretId;
|
||||
}
|
||||
|
||||
private bool ShouldRegisterNewApp()
|
||||
{
|
||||
return string.IsNullOrEmpty(SecretId) || string.IsNullOrEmpty(ClientId);
|
||||
}
|
||||
|
||||
public override string GetUri()
|
||||
{
|
||||
return ShouldRegisterNewApp() ? $"{RedirectUri}/start.html#{State}" : base.GetUri();
|
||||
}
|
||||
|
||||
protected override void AdaptWebServer(WebServer webServer)
|
||||
{
|
||||
webServer.Module<WebApiModule>().RegisterController<AuthorizationCodeAuthController>();
|
||||
}
|
||||
|
||||
private string GetAuthHeader() => $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientId + ":" + SecretId))}";
|
||||
|
||||
public async Task<Token> RefreshToken(string refreshToken)
|
||||
{
|
||||
List<KeyValuePair<string, string>> args = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("grant_type", "refresh_token"),
|
||||
new KeyValuePair<string, string>("refresh_token", refreshToken)
|
||||
};
|
||||
|
||||
return await GetToken(args);
|
||||
}
|
||||
|
||||
public async Task<Token> ExchangeCode(string code)
|
||||
{
|
||||
List<KeyValuePair<string, string>> args = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("grant_type", "authorization_code"),
|
||||
new KeyValuePair<string, string>("code", code),
|
||||
new KeyValuePair<string, string>("redirect_uri", RedirectUri)
|
||||
};
|
||||
|
||||
return await GetToken(args);
|
||||
}
|
||||
|
||||
private async Task<Token> GetToken(IEnumerable<KeyValuePair<string, string>> args)
|
||||
{
|
||||
HttpClientHandler handler = ProxyConfig.CreateClientHandler(ProxyConfig);
|
||||
HttpClient client = new HttpClient(handler);
|
||||
client.DefaultRequestHeaders.Add("Authorization", GetAuthHeader());
|
||||
HttpContent content = new FormUrlEncodedContent(args);
|
||||
|
||||
HttpResponseMessage resp = await client.PostAsync("https://accounts.spotify.com/api/token", content);
|
||||
string msg = await resp.Content.ReadAsStringAsync();
|
||||
|
||||
return JsonConvert.DeserializeObject<Token>(msg);
|
||||
}
|
||||
ClientId = clientId;
|
||||
SecretId = secretId;
|
||||
}
|
||||
|
||||
public class AuthorizationCode
|
||||
private bool ShouldRegisterNewApp()
|
||||
{
|
||||
public string Code { get; set; }
|
||||
|
||||
public string Error { get; set; }
|
||||
return string.IsNullOrEmpty(SecretId) || string.IsNullOrEmpty(ClientId);
|
||||
}
|
||||
|
||||
internal class AuthorizationCodeAuthController : WebApiController
|
||||
public override string GetUri()
|
||||
{
|
||||
[WebApiHandler(HttpVerbs.Get, "/")]
|
||||
public Task<bool> GetEmpty()
|
||||
{
|
||||
string state = Request.QueryString["state"];
|
||||
AuthorizationCodeAuth.Instances.TryGetValue(state, out SpotifyAuthServer<AuthorizationCode> auth);
|
||||
|
||||
string code = null;
|
||||
string error = Request.QueryString["error"];
|
||||
if (error == null)
|
||||
code = Request.QueryString["code"];
|
||||
|
||||
Task.Factory.StartNew(() => auth?.TriggerAuth(new AuthorizationCode
|
||||
{
|
||||
Code = code,
|
||||
Error = error
|
||||
}));
|
||||
|
||||
return HttpContext.HtmlResponseAsync("<html><script type=\"text/javascript\">window.close();</script>OK - This window can be closed now</html>");
|
||||
}
|
||||
|
||||
[WebApiHandler(HttpVerbs.Post, "/")]
|
||||
public async Task<bool> PostValues()
|
||||
{
|
||||
Dictionary<string, object> formParams = await HttpContext.RequestFormDataDictionaryAsync();
|
||||
|
||||
string state = (string) formParams["state"];
|
||||
AuthorizationCodeAuth.Instances.TryGetValue(state, out SpotifyAuthServer<AuthorizationCode> authServer);
|
||||
|
||||
AuthorizationCodeAuth auth = (AuthorizationCodeAuth) authServer;
|
||||
auth.ClientId = (string) formParams["clientId"];
|
||||
auth.SecretId = (string) formParams["secretId"];
|
||||
|
||||
string uri = auth.GetUri();
|
||||
return HttpContext.Redirect(uri, false);
|
||||
}
|
||||
|
||||
public AuthorizationCodeAuthController(IHttpContext context) : base(context)
|
||||
{
|
||||
}
|
||||
return ShouldRegisterNewApp() ? $"{RedirectUri}/start.html#{State}" : base.GetUri();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AdaptWebServer(WebServer webServer)
|
||||
{
|
||||
webServer.Module<WebApiModule>().RegisterController<AuthorizationCodeAuthController>();
|
||||
}
|
||||
|
||||
private string GetAuthHeader() => $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientId + ":" + SecretId))}";
|
||||
|
||||
public async Task<Token> RefreshToken(string refreshToken)
|
||||
{
|
||||
List<KeyValuePair<string, string>> args = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("grant_type", "refresh_token"),
|
||||
new KeyValuePair<string, string>("refresh_token", refreshToken)
|
||||
};
|
||||
|
||||
return await GetToken(args);
|
||||
}
|
||||
|
||||
public async Task<Token> ExchangeCode(string code)
|
||||
{
|
||||
List<KeyValuePair<string, string>> args = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("grant_type", "authorization_code"),
|
||||
new KeyValuePair<string, string>("code", code),
|
||||
new KeyValuePair<string, string>("redirect_uri", RedirectUri)
|
||||
};
|
||||
|
||||
return await GetToken(args);
|
||||
}
|
||||
|
||||
private async Task<Token> GetToken(IEnumerable<KeyValuePair<string, string>> args)
|
||||
{
|
||||
HttpClientHandler handler = ProxyConfig.CreateClientHandler(ProxyConfig);
|
||||
HttpClient client = new HttpClient(handler);
|
||||
client.DefaultRequestHeaders.Add("Authorization", GetAuthHeader());
|
||||
HttpContent content = new FormUrlEncodedContent(args);
|
||||
|
||||
HttpResponseMessage resp = await client.PostAsync("https://accounts.spotify.com/api/token", content);
|
||||
string msg = await resp.Content.ReadAsStringAsync();
|
||||
|
||||
return JsonConvert.DeserializeObject<Token>(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public class AuthorizationCode
|
||||
{
|
||||
public string Code { get; set; }
|
||||
|
||||
public string Error { get; set; }
|
||||
}
|
||||
|
||||
internal class AuthorizationCodeAuthController : WebApiController
|
||||
{
|
||||
[WebApiHandler(HttpVerbs.Get, "/")]
|
||||
public Task<bool> GetEmpty()
|
||||
{
|
||||
string state = Request.QueryString["state"];
|
||||
AuthorizationCodeAuth.Instances.TryGetValue(state, out SpotifyAuthServer<AuthorizationCode> auth);
|
||||
|
||||
string code = null;
|
||||
string error = Request.QueryString["error"];
|
||||
if (error == null)
|
||||
code = Request.QueryString["code"];
|
||||
|
||||
Task.Factory.StartNew(() => auth?.TriggerAuth(new AuthorizationCode
|
||||
{
|
||||
Code = code,
|
||||
Error = error
|
||||
}));
|
||||
|
||||
return HttpContext.HtmlResponseAsync("<html><script type=\"text/javascript\">window.close();</script>OK - This window can be closed now</html>");
|
||||
}
|
||||
|
||||
[WebApiHandler(HttpVerbs.Post, "/")]
|
||||
public async Task<bool> PostValues()
|
||||
{
|
||||
Dictionary<string, object> formParams = await HttpContext.RequestFormDataDictionaryAsync();
|
||||
|
||||
string state = (string) formParams["state"];
|
||||
AuthorizationCodeAuth.Instances.TryGetValue(state, out SpotifyAuthServer<AuthorizationCode> authServer);
|
||||
|
||||
AuthorizationCodeAuth auth = (AuthorizationCodeAuth) authServer;
|
||||
auth.ClientId = (string) formParams["clientId"];
|
||||
auth.SecretId = (string) formParams["secretId"];
|
||||
|
||||
string uri = auth.GetUri();
|
||||
return HttpContext.Redirect(uri, false);
|
||||
}
|
||||
|
||||
public AuthorizationCodeAuthController(IHttpContext context) : base(context)
|
||||
{ }
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
@ -8,38 +8,37 @@ using SpotifyAPI.Web.Models;
|
||||
|
||||
namespace SpotifyAPI.Web.Auth
|
||||
{
|
||||
public class CredentialsAuth
|
||||
public class CredentialsAuth
|
||||
{
|
||||
public string ClientSecret { get; set; }
|
||||
|
||||
public string ClientId { get; set; }
|
||||
|
||||
public ProxyConfig ProxyConfig { get; set; }
|
||||
|
||||
public CredentialsAuth(string clientId, string clientSecret)
|
||||
{
|
||||
public string ClientSecret { get; set; }
|
||||
|
||||
public string ClientId { get; set; }
|
||||
|
||||
public ProxyConfig ProxyConfig { get; set; }
|
||||
|
||||
public CredentialsAuth(string clientId, string clientSecret)
|
||||
{
|
||||
ClientId = clientId;
|
||||
ClientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public async Task<Token> GetToken()
|
||||
{
|
||||
string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientId + ":" + ClientSecret));
|
||||
|
||||
List<KeyValuePair<string, string>> args = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("grant_type", "client_credentials")
|
||||
};
|
||||
|
||||
HttpClientHandler handler = ProxyConfig.CreateClientHandler(ProxyConfig);
|
||||
HttpClient client = new HttpClient(handler);
|
||||
client.DefaultRequestHeaders.Add("Authorization", $"Basic {auth}");
|
||||
HttpContent content = new FormUrlEncodedContent(args);
|
||||
|
||||
HttpResponseMessage resp = await client.PostAsync("https://accounts.spotify.com/api/token", content);
|
||||
string msg = await resp.Content.ReadAsStringAsync();
|
||||
|
||||
return JsonConvert.DeserializeObject<Token>(msg);
|
||||
}
|
||||
ClientId = clientId;
|
||||
ClientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public async Task<Token> GetToken()
|
||||
{
|
||||
string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientId + ":" + ClientSecret));
|
||||
|
||||
List<KeyValuePair<string, string>> args = new List<KeyValuePair<string, string>>
|
||||
{new KeyValuePair<string, string>("grant_type", "client_credentials")
|
||||
};
|
||||
|
||||
HttpClientHandler handler = ProxyConfig.CreateClientHandler(ProxyConfig);
|
||||
HttpClient client = new HttpClient(handler);
|
||||
client.DefaultRequestHeaders.Add("Authorization", $"Basic {auth}");
|
||||
HttpContent content = new FormUrlEncodedContent(args);
|
||||
|
||||
HttpResponseMessage resp = await client.PostAsync("https://accounts.spotify.com/api/token", content);
|
||||
string msg = await resp.Content.ReadAsStringAsync();
|
||||
|
||||
return JsonConvert.DeserializeObject<Token>(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,59 +7,57 @@ using Unosquare.Labs.EmbedIO.Modules;
|
||||
|
||||
namespace SpotifyAPI.Web.Auth
|
||||
{
|
||||
public class ImplicitGrantAuth : SpotifyAuthServer<Token>
|
||||
public class ImplicitGrantAuth : SpotifyAuthServer<Token>
|
||||
{
|
||||
public ImplicitGrantAuth(string clientId, string redirectUri, string serverUri, Scope scope = Scope.None, string state = "") : base("token", "ImplicitGrantAuth", redirectUri, serverUri, scope, state)
|
||||
{
|
||||
public ImplicitGrantAuth(string clientId, string redirectUri, string serverUri, Scope scope = Scope.None, string state = "") :
|
||||
base("token", "ImplicitGrantAuth", redirectUri, serverUri, scope, state)
|
||||
{
|
||||
ClientId = clientId;
|
||||
}
|
||||
|
||||
protected override void AdaptWebServer(WebServer webServer)
|
||||
{
|
||||
webServer.Module<WebApiModule>().RegisterController<ImplicitGrantAuthController>();
|
||||
}
|
||||
ClientId = clientId;
|
||||
}
|
||||
|
||||
public class ImplicitGrantAuthController : WebApiController
|
||||
protected override void AdaptWebServer(WebServer webServer)
|
||||
{
|
||||
[WebApiHandler(HttpVerbs.Get, "/auth")]
|
||||
public Task<bool> GetAuth()
|
||||
{
|
||||
string state = Request.QueryString["state"];
|
||||
SpotifyAuthServer<Token> auth = ImplicitGrantAuth.GetByState(state);
|
||||
if (auth == null)
|
||||
return HttpContext.StringResponseAsync(
|
||||
$"Failed - Unable to find auth request with state \"{state}\" - Please retry");
|
||||
|
||||
Token token;
|
||||
string error = Request.QueryString["error"];
|
||||
if (error == null)
|
||||
{
|
||||
string accessToken = Request.QueryString["access_token"];
|
||||
string tokenType = Request.QueryString["token_type"];
|
||||
string expiresIn = Request.QueryString["expires_in"];
|
||||
token = new Token
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
ExpiresIn = double.Parse(expiresIn),
|
||||
TokenType = tokenType
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
token = new Token
|
||||
{
|
||||
Error = error
|
||||
};
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(() => auth.TriggerAuth(token));
|
||||
return HttpContext.HtmlResponseAsync("<html><script type=\"text/javascript\">window.close();</script>OK - This window can be closed now</html>");
|
||||
}
|
||||
|
||||
public ImplicitGrantAuthController(IHttpContext context) : base(context)
|
||||
{
|
||||
}
|
||||
webServer.Module<WebApiModule>().RegisterController<ImplicitGrantAuthController>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ImplicitGrantAuthController : WebApiController
|
||||
{
|
||||
[WebApiHandler(HttpVerbs.Get, "/auth")]
|
||||
public Task<bool> GetAuth()
|
||||
{
|
||||
string state = Request.QueryString["state"];
|
||||
SpotifyAuthServer<Token> auth = ImplicitGrantAuth.GetByState(state);
|
||||
if (auth == null)
|
||||
return HttpContext.StringResponseAsync(
|
||||
$"Failed - Unable to find auth request with state \"{state}\" - Please retry");
|
||||
|
||||
Token token;
|
||||
string error = Request.QueryString["error"];
|
||||
if (error == null)
|
||||
{
|
||||
string accessToken = Request.QueryString["access_token"];
|
||||
string tokenType = Request.QueryString["token_type"];
|
||||
string expiresIn = Request.QueryString["expires_in"];
|
||||
token = new Token
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
ExpiresIn = double.Parse(expiresIn),
|
||||
TokenType = tokenType
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
token = new Token
|
||||
{
|
||||
Error = error
|
||||
};
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(() => auth.TriggerAuth(token));
|
||||
return HttpContext.HtmlResponseAsync("<html><script type=\"text/javascript\">window.close();</script>OK - This window can be closed now</html>");
|
||||
}
|
||||
|
||||
public ImplicitGrantAuthController(IHttpContext context) : base(context)
|
||||
{ }
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -10,84 +10,84 @@ using Unosquare.Labs.EmbedIO.Modules;
|
||||
|
||||
namespace SpotifyAPI.Web.Auth
|
||||
{
|
||||
public abstract class SpotifyAuthServer<T>
|
||||
public abstract class SpotifyAuthServer<T>
|
||||
{
|
||||
public string ClientId { get; set; }
|
||||
public string ServerUri { get; set; }
|
||||
public string RedirectUri { get; set; }
|
||||
public string State { get; set; }
|
||||
public Scope Scope { get; set; }
|
||||
public bool ShowDialog { get; set; }
|
||||
|
||||
private readonly string _folder;
|
||||
private readonly string _type;
|
||||
private WebServer _server;
|
||||
protected CancellationTokenSource _serverSource;
|
||||
|
||||
public delegate void OnAuthReceived(object sender, T payload);
|
||||
public event OnAuthReceived AuthReceived;
|
||||
|
||||
internal static readonly Dictionary<string, SpotifyAuthServer<T>> Instances = new Dictionary<string, SpotifyAuthServer<T>>();
|
||||
|
||||
internal SpotifyAuthServer(string type, string folder, string redirectUri, string serverUri, Scope scope = Scope.None, string state = "")
|
||||
{
|
||||
public string ClientId { get; set; }
|
||||
public string ServerUri { get; set; }
|
||||
public string RedirectUri { get; set; }
|
||||
public string State { get; set; }
|
||||
public Scope Scope { get; set; }
|
||||
public bool ShowDialog { get; set; }
|
||||
|
||||
private readonly string _folder;
|
||||
private readonly string _type;
|
||||
private WebServer _server;
|
||||
protected CancellationTokenSource _serverSource;
|
||||
|
||||
public delegate void OnAuthReceived(object sender, T payload);
|
||||
public event OnAuthReceived AuthReceived;
|
||||
|
||||
internal static readonly Dictionary<string, SpotifyAuthServer<T>> Instances = new Dictionary<string, SpotifyAuthServer<T>>();
|
||||
|
||||
internal SpotifyAuthServer(string type, string folder, string redirectUri, string serverUri, Scope scope = Scope.None, string state = "")
|
||||
{
|
||||
_type = type;
|
||||
_folder = folder;
|
||||
ServerUri = serverUri;
|
||||
RedirectUri = redirectUri;
|
||||
Scope = scope;
|
||||
State = string.IsNullOrEmpty(state) ? string.Join("", Guid.NewGuid().ToString("n").Take(8)) : state;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Instances.Add(State, this);
|
||||
_serverSource = new CancellationTokenSource();
|
||||
|
||||
_server = WebServer.Create(ServerUri);
|
||||
_server.RegisterModule(new WebApiModule());
|
||||
AdaptWebServer(_server);
|
||||
_server.RegisterModule(new ResourceFilesModule(Assembly.GetExecutingAssembly(), $"SpotifyAPI.Web.Auth.Resources.{_folder}"));
|
||||
#pragma warning disable 4014
|
||||
_server.RunAsync(_serverSource.Token);
|
||||
#pragma warning restore 4014
|
||||
}
|
||||
|
||||
public virtual string GetUri()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder("https://accounts.spotify.com/authorize/?");
|
||||
builder.Append("client_id=" + ClientId);
|
||||
builder.Append($"&response_type={_type}");
|
||||
builder.Append("&redirect_uri=" + RedirectUri);
|
||||
builder.Append("&state=" + State);
|
||||
builder.Append("&scope=" + Scope.GetStringAttribute(" "));
|
||||
builder.Append("&show_dialog=" + ShowDialog);
|
||||
return Uri.EscapeUriString(builder.ToString());
|
||||
}
|
||||
|
||||
public void Stop(int delay = 2000)
|
||||
{
|
||||
if (_serverSource == null) return;
|
||||
_serverSource.CancelAfter(delay);
|
||||
Instances.Remove(State);
|
||||
}
|
||||
|
||||
public void OpenBrowser()
|
||||
{
|
||||
string uri = GetUri();
|
||||
AuthUtil.OpenBrowser(uri);
|
||||
}
|
||||
|
||||
internal void TriggerAuth(T payload)
|
||||
{
|
||||
AuthReceived?.Invoke(this, payload);
|
||||
}
|
||||
|
||||
internal static SpotifyAuthServer<T> GetByState(string state)
|
||||
{
|
||||
return Instances.TryGetValue(state, out SpotifyAuthServer<T> auth) ? auth : null;
|
||||
}
|
||||
|
||||
protected abstract void AdaptWebServer(WebServer webServer);
|
||||
_type = type;
|
||||
_folder = folder;
|
||||
ServerUri = serverUri;
|
||||
RedirectUri = redirectUri;
|
||||
Scope = scope;
|
||||
State = string.IsNullOrEmpty(state) ? string.Join("", Guid.NewGuid().ToString("n").Take(8)) : state;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Instances.Add(State, this);
|
||||
_serverSource = new CancellationTokenSource();
|
||||
|
||||
_server = WebServer.Create(ServerUri);
|
||||
_server.RegisterModule(new WebApiModule());
|
||||
AdaptWebServer(_server);
|
||||
_server.RegisterModule(new ResourceFilesModule(Assembly.GetExecutingAssembly(), $"SpotifyAPI.Web.Auth.Resources.{_folder}"));
|
||||
#pragma warning disable 4014
|
||||
_server.RunAsync(_serverSource.Token);
|
||||
#pragma warning restore 4014
|
||||
}
|
||||
|
||||
public virtual string GetUri()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder("https://accounts.spotify.com/authorize/?");
|
||||
builder.Append("client_id=" + ClientId);
|
||||
builder.Append($"&response_type={_type}");
|
||||
builder.Append("&redirect_uri=" + RedirectUri);
|
||||
builder.Append("&state=" + State);
|
||||
builder.Append("&scope=" + Scope.GetStringAttribute(" "));
|
||||
builder.Append("&show_dialog=" + ShowDialog);
|
||||
return Uri.EscapeUriString(builder.ToString());
|
||||
}
|
||||
|
||||
public void Stop(int delay = 2000)
|
||||
{
|
||||
if (_serverSource == null) return;
|
||||
_serverSource.CancelAfter(delay);
|
||||
Instances.Remove(State);
|
||||
}
|
||||
|
||||
public void OpenBrowser()
|
||||
{
|
||||
string uri = GetUri();
|
||||
AuthUtil.OpenBrowser(uri);
|
||||
}
|
||||
|
||||
internal void TriggerAuth(T payload)
|
||||
{
|
||||
AuthReceived?.Invoke(this, payload);
|
||||
}
|
||||
|
||||
internal static SpotifyAuthServer<T> GetByState(string state)
|
||||
{
|
||||
return Instances.TryGetValue(state, out SpotifyAuthServer<T> auth) ? auth : null;
|
||||
}
|
||||
|
||||
protected abstract void AdaptWebServer(WebServer webServer);
|
||||
}
|
||||
}
|
@ -1,221 +1,218 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using SpotifyAPI.Web.Enums;
|
||||
using SpotifyAPI.Web.Models;
|
||||
using Unosquare.Labs.EmbedIO;
|
||||
using Unosquare.Labs.EmbedIO.Constants;
|
||||
using Unosquare.Labs.EmbedIO.Modules;
|
||||
using SpotifyAPI.Web.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace SpotifyAPI.Web.Auth
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// A version of <see cref="AuthorizationCodeAuth"/> that does not store your client secret, client ID or redirect URI, enforcing a secure authorization flow. Requires an exchange server that will return the authorization code to its callback server via GET request.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It's recommended that you use <see cref="TokenSwapWebAPIFactory"/> if you would like to use the TokenSwap method.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class TokenSwapAuth : SpotifyAuthServer<AuthorizationCode>
|
||||
{
|
||||
readonly string _exchangeServerUri;
|
||||
|
||||
/// <summary>
|
||||
/// The HTML to respond with when the callback server (serverUri) is reached. The default value will close the window on arrival.
|
||||
/// </summary>
|
||||
public string HtmlResponse { get; set; } = "<script>window.close();</script>";
|
||||
|
||||
/// <summary>
|
||||
/// If true, will time how long it takes for access to expire. On expiry, the <see cref="OnAccessTokenExpired"/> event fires.
|
||||
/// </summary>
|
||||
public bool TimeAccessExpiry { get; set; }
|
||||
|
||||
public ProxyConfig ProxyConfig { get; set; }
|
||||
|
||||
/// <param name="exchangeServerUri">The URI to an exchange server that will perform the key exchange.</param>
|
||||
/// <param name="serverUri">The URI to host the server at that your exchange server should return the authorization code to by GET request. (e.g. http://localhost:4002)</param>
|
||||
/// <param name="scope"></param>
|
||||
/// <param name="state">Stating none will randomly generate a state parameter.</param>
|
||||
/// <param name="htmlResponse">The HTML to respond with when the callback server (serverUri) is reached. The default value will close the window on arrival.</param>
|
||||
public TokenSwapAuth(string exchangeServerUri, string serverUri, Scope scope = Scope.None, string state = "",
|
||||
string htmlResponse = "") : base("code", "", "", serverUri, scope, state)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(htmlResponse))
|
||||
{
|
||||
HtmlResponse = htmlResponse;
|
||||
}
|
||||
|
||||
_exchangeServerUri = exchangeServerUri;
|
||||
}
|
||||
|
||||
protected override void AdaptWebServer(WebServer webServer)
|
||||
{
|
||||
webServer.Module<WebApiModule>().RegisterController<TokenSwapAuthController>();
|
||||
}
|
||||
|
||||
public override string GetUri()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(_exchangeServerUri);
|
||||
builder.Append("?");
|
||||
builder.Append("response_type=code");
|
||||
builder.Append("&state=" + State);
|
||||
builder.Append("&scope=" + Scope.GetStringAttribute(" "));
|
||||
builder.Append("&show_dialog=" + ShowDialog);
|
||||
return Uri.EscapeUriString(builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of times to retry getting a token.
|
||||
/// <para/>
|
||||
/// A token get is attempted every time you <see cref="RefreshAuthAsync(string)"/> and <see cref="ExchangeCodeAsync(string)"/>.
|
||||
/// </summary>
|
||||
public int MaxGetTokenRetries { get; set; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a HTTP request to obtain a token object.<para/>
|
||||
/// Parameter grantType can only be "refresh_token" or "authorization_code". authorizationCode and refreshToken are not mandatory, but at least one must be provided for your desired grant_type request otherwise an invalid response will be given and an exception is likely to be thrown.
|
||||
/// <para>
|
||||
/// A version of <see cref="AuthorizationCodeAuth"/> that does not store your client secret, client ID or redirect URI, enforcing a secure authorization flow. Requires an exchange server that will return the authorization code to its callback server via GET request.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It's recommended that you use <see cref="TokenSwapWebAPIFactory"/> if you would like to use the TokenSwap method.
|
||||
/// Will re-attempt on error, on null or on no access token <see cref="MaxGetTokenRetries"/> times before finally returning null.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class TokenSwapAuth : SpotifyAuthServer<AuthorizationCode>
|
||||
/// <param name="grantType">Can only be "refresh_token" or "authorization_code".</param>
|
||||
/// <param name="authorizationCode">This needs to be defined if "grantType" is "authorization_code".</param>
|
||||
/// <param name="refreshToken">This needs to be defined if "grantType" is "refresh_token".</param>
|
||||
/// <param name="currentRetries">Does not need to be defined. Used internally for retry attempt recursion.</param>
|
||||
/// <returns>Attempts to return a full <see cref="Token"/>, but after retry attempts, may return a <see cref="Token"/> with no <see cref="Token.AccessToken"/>, or null.</returns>
|
||||
async Task<Token> GetToken(string grantType, string authorizationCode = "", string refreshToken = "",
|
||||
int currentRetries = 0)
|
||||
{
|
||||
readonly string _exchangeServerUri;
|
||||
FormUrlEncodedContent content = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||
{ { "grant_type", grantType },
|
||||
{ "code", authorizationCode },
|
||||
{ "refresh_token", refreshToken }
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// The HTML to respond with when the callback server (serverUri) is reached. The default value will close the window on arrival.
|
||||
/// </summary>
|
||||
public string HtmlResponse { get; set; } = "<script>window.close();</script>";
|
||||
try
|
||||
{
|
||||
HttpClientHandler handler = ProxyConfig.CreateClientHandler(ProxyConfig);
|
||||
HttpClient client = new HttpClient(handler);
|
||||
HttpResponseMessage siteResponse = await client.PostAsync(_exchangeServerUri, content);
|
||||
|
||||
/// <summary>
|
||||
/// If true, will time how long it takes for access to expire. On expiry, the <see cref="OnAccessTokenExpired"/> event fires.
|
||||
/// </summary>
|
||||
public bool TimeAccessExpiry { get; set; }
|
||||
|
||||
public ProxyConfig ProxyConfig { get; set; }
|
||||
|
||||
/// <param name="exchangeServerUri">The URI to an exchange server that will perform the key exchange.</param>
|
||||
/// <param name="serverUri">The URI to host the server at that your exchange server should return the authorization code to by GET request. (e.g. http://localhost:4002)</param>
|
||||
/// <param name="scope"></param>
|
||||
/// <param name="state">Stating none will randomly generate a state parameter.</param>
|
||||
/// <param name="htmlResponse">The HTML to respond with when the callback server (serverUri) is reached. The default value will close the window on arrival.</param>
|
||||
public TokenSwapAuth(string exchangeServerUri, string serverUri, Scope scope = Scope.None, string state = "",
|
||||
string htmlResponse = "") : base("code", "", "", serverUri, scope, state)
|
||||
Token token = JsonConvert.DeserializeObject<Token>(await siteResponse.Content.ReadAsStringAsync());
|
||||
// Don't need to check if it was null - if it is, it will resort to the catch block.
|
||||
if (!token.HasError() && !string.IsNullOrEmpty(token.AccessToken))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(htmlResponse))
|
||||
{
|
||||
HtmlResponse = htmlResponse;
|
||||
}
|
||||
|
||||
_exchangeServerUri = exchangeServerUri;
|
||||
return token;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
|
||||
protected override void AdaptWebServer(WebServer webServer)
|
||||
{
|
||||
webServer.Module<WebApiModule>().RegisterController<TokenSwapAuthController>();
|
||||
}
|
||||
|
||||
public override string GetUri()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(_exchangeServerUri);
|
||||
builder.Append("?");
|
||||
builder.Append("response_type=code");
|
||||
builder.Append("&state=" + State);
|
||||
builder.Append("&scope=" + Scope.GetStringAttribute(" "));
|
||||
builder.Append("&show_dialog=" + ShowDialog);
|
||||
return Uri.EscapeUriString(builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of times to retry getting a token.
|
||||
/// <para/>
|
||||
/// A token get is attempted every time you <see cref="RefreshAuthAsync(string)"/> and <see cref="ExchangeCodeAsync(string)"/>.
|
||||
/// </summary>
|
||||
public int MaxGetTokenRetries { get; set; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a HTTP request to obtain a token object.<para/>
|
||||
/// Parameter grantType can only be "refresh_token" or "authorization_code". authorizationCode and refreshToken are not mandatory, but at least one must be provided for your desired grant_type request otherwise an invalid response will be given and an exception is likely to be thrown.
|
||||
/// <para>
|
||||
/// Will re-attempt on error, on null or on no access token <see cref="MaxGetTokenRetries"/> times before finally returning null.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="grantType">Can only be "refresh_token" or "authorization_code".</param>
|
||||
/// <param name="authorizationCode">This needs to be defined if "grantType" is "authorization_code".</param>
|
||||
/// <param name="refreshToken">This needs to be defined if "grantType" is "refresh_token".</param>
|
||||
/// <param name="currentRetries">Does not need to be defined. Used internally for retry attempt recursion.</param>
|
||||
/// <returns>Attempts to return a full <see cref="Token"/>, but after retry attempts, may return a <see cref="Token"/> with no <see cref="Token.AccessToken"/>, or null.</returns>
|
||||
async Task<Token> GetToken(string grantType, string authorizationCode = "", string refreshToken = "",
|
||||
int currentRetries = 0)
|
||||
{
|
||||
FormUrlEncodedContent content = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||
{
|
||||
{"grant_type", grantType},
|
||||
{"code", authorizationCode},
|
||||
{"refresh_token", refreshToken}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
HttpClientHandler handler = ProxyConfig.CreateClientHandler(ProxyConfig);
|
||||
HttpClient client = new HttpClient(handler);
|
||||
HttpResponseMessage siteResponse = await client.PostAsync(_exchangeServerUri, content);
|
||||
|
||||
Token token = JsonConvert.DeserializeObject<Token>(await siteResponse.Content.ReadAsStringAsync());
|
||||
// Don't need to check if it was null - if it is, it will resort to the catch block.
|
||||
if (!token.HasError() && !string.IsNullOrEmpty(token.AccessToken))
|
||||
{
|
||||
return token;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (currentRetries >= MaxGetTokenRetries)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRetries++;
|
||||
// The reason I chose to implement the retries system this way is because a static or instance
|
||||
// variable keeping track would inhibit parallelism i.e. using this function on multiple threads/tasks.
|
||||
// It's not clear why someone would like to do that, but it's better to cater for all kinds of uses.
|
||||
return await GetToken(grantType, authorizationCode, refreshToken, currentRetries);
|
||||
}
|
||||
}
|
||||
|
||||
System.Timers.Timer _accessTokenExpireTimer;
|
||||
|
||||
/// <summary>
|
||||
/// When Spotify authorization has expired. Will only trigger if <see cref="TimeAccessExpiry"/> is true.
|
||||
/// </summary>
|
||||
public event EventHandler OnAccessTokenExpired;
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="TimeAccessExpiry"/> is true, sets a timer for how long access will take to expire.
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
void SetAccessExpireTimer(Token token)
|
||||
{
|
||||
if (!TimeAccessExpiry) return;
|
||||
|
||||
if (_accessTokenExpireTimer != null)
|
||||
{
|
||||
_accessTokenExpireTimer.Stop();
|
||||
_accessTokenExpireTimer.Dispose();
|
||||
}
|
||||
|
||||
_accessTokenExpireTimer = new System.Timers.Timer
|
||||
{
|
||||
Enabled = true,
|
||||
Interval = token.ExpiresIn * 1000,
|
||||
AutoReset = false
|
||||
};
|
||||
_accessTokenExpireTimer.Elapsed += (sender, e) => OnAccessTokenExpired?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the authorization code to silently (doesn't open a browser) obtain both an access token and refresh token, where the refresh token would be required for you to use <see cref="RefreshAuthAsync(string)"/>.
|
||||
/// </summary>
|
||||
/// <param name="authorizationCode"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Token> ExchangeCodeAsync(string authorizationCode)
|
||||
{
|
||||
Token token = await GetToken("authorization_code", authorizationCode: authorizationCode);
|
||||
if (token != null && !token.HasError() && !string.IsNullOrEmpty(token.AccessToken))
|
||||
{
|
||||
SetAccessExpireTimer(token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the refresh token to silently (doesn't open a browser) obtain a fresh access token, no refresh token is given however (as it does not change).
|
||||
/// </summary>
|
||||
/// <param name="refreshToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Token> RefreshAuthAsync(string refreshToken)
|
||||
{
|
||||
Token token = await GetToken("refresh_token", refreshToken: refreshToken);
|
||||
if (token != null && !token.HasError() && !string.IsNullOrEmpty(token.AccessToken))
|
||||
{
|
||||
SetAccessExpireTimer(token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
if (currentRetries >= MaxGetTokenRetries)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRetries++;
|
||||
// The reason I chose to implement the retries system this way is because a static or instance
|
||||
// variable keeping track would inhibit parallelism i.e. using this function on multiple threads/tasks.
|
||||
// It's not clear why someone would like to do that, but it's better to cater for all kinds of uses.
|
||||
return await GetToken(grantType, authorizationCode, refreshToken, currentRetries);
|
||||
}
|
||||
}
|
||||
|
||||
internal class TokenSwapAuthController : WebApiController
|
||||
System.Timers.Timer _accessTokenExpireTimer;
|
||||
|
||||
/// <summary>
|
||||
/// When Spotify authorization has expired. Will only trigger if <see cref="TimeAccessExpiry"/> is true.
|
||||
/// </summary>
|
||||
public event EventHandler OnAccessTokenExpired;
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="TimeAccessExpiry"/> is true, sets a timer for how long access will take to expire.
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
void SetAccessExpireTimer(Token token)
|
||||
{
|
||||
public TokenSwapAuthController(IHttpContext context) : base(context)
|
||||
{
|
||||
}
|
||||
if (!TimeAccessExpiry) return;
|
||||
|
||||
[WebApiHandler(HttpVerbs.Get, "/auth")]
|
||||
public Task<bool> GetAuth()
|
||||
{
|
||||
string state = Request.QueryString["state"];
|
||||
SpotifyAuthServer<AuthorizationCode> auth = TokenSwapAuth.GetByState(state);
|
||||
if (_accessTokenExpireTimer != null)
|
||||
{
|
||||
_accessTokenExpireTimer.Stop();
|
||||
_accessTokenExpireTimer.Dispose();
|
||||
}
|
||||
|
||||
string code = null;
|
||||
string error = Request.QueryString["error"];
|
||||
if (error == null)
|
||||
{
|
||||
code = Request.QueryString["code"];
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(() => auth?.TriggerAuth(new AuthorizationCode
|
||||
{
|
||||
Code = code,
|
||||
Error = error
|
||||
}));
|
||||
return HttpContext.HtmlResponseAsync(((TokenSwapAuth) auth).HtmlResponse);
|
||||
}
|
||||
_accessTokenExpireTimer = new System.Timers.Timer
|
||||
{
|
||||
Enabled = true,
|
||||
Interval = token.ExpiresIn * 1000,
|
||||
AutoReset = false
|
||||
};
|
||||
_accessTokenExpireTimer.Elapsed += (sender, e) => OnAccessTokenExpired?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the authorization code to silently (doesn't open a browser) obtain both an access token and refresh token, where the refresh token would be required for you to use <see cref="RefreshAuthAsync(string)"/>.
|
||||
/// </summary>
|
||||
/// <param name="authorizationCode"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Token> ExchangeCodeAsync(string authorizationCode)
|
||||
{
|
||||
Token token = await GetToken("authorization_code", authorizationCode : authorizationCode);
|
||||
if (token != null && !token.HasError() && !string.IsNullOrEmpty(token.AccessToken))
|
||||
{
|
||||
SetAccessExpireTimer(token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the refresh token to silently (doesn't open a browser) obtain a fresh access token, no refresh token is given however (as it does not change).
|
||||
/// </summary>
|
||||
/// <param name="refreshToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Token> RefreshAuthAsync(string refreshToken)
|
||||
{
|
||||
Token token = await GetToken("refresh_token", refreshToken : refreshToken);
|
||||
if (token != null && !token.HasError() && !string.IsNullOrEmpty(token.AccessToken))
|
||||
{
|
||||
SetAccessExpireTimer(token);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
internal class TokenSwapAuthController : WebApiController
|
||||
{
|
||||
public TokenSwapAuthController(IHttpContext context) : base(context)
|
||||
{ }
|
||||
|
||||
[WebApiHandler(HttpVerbs.Get, "/auth")]
|
||||
public Task<bool> GetAuth()
|
||||
{
|
||||
string state = Request.QueryString["state"];
|
||||
SpotifyAuthServer<AuthorizationCode> auth = TokenSwapAuth.GetByState(state);
|
||||
|
||||
string code = null;
|
||||
string error = Request.QueryString["error"];
|
||||
if (error == null)
|
||||
{
|
||||
code = Request.QueryString["code"];
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(() => auth?.TriggerAuth(new AuthorizationCode
|
||||
{
|
||||
Code = code,
|
||||
Error = error
|
||||
}));
|
||||
return HttpContext.HtmlResponseAsync(((TokenSwapAuth) auth).HtmlResponse);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,282 +1,280 @@
|
||||
using SpotifyAPI.Web.Enums;
|
||||
using SpotifyAPI.Web.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SpotifyAPI.Web.Enums;
|
||||
using SpotifyAPI.Web.Models;
|
||||
|
||||
namespace SpotifyAPI.Web.Auth
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a <see cref="SpotifyWebAPI"/> using the TokenSwapAuth process.
|
||||
/// </summary>
|
||||
public class TokenSwapWebAPIFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a <see cref="SpotifyWebAPI"/> using the TokenSwapAuth process.
|
||||
/// Access provided by Spotify expires after 1 hour. If true, <see cref="TokenSwapAuth"/> will time the access tokens, and access will attempt to be silently (without opening a browser) refreshed automatically. This will not make <see cref="OnAccessTokenExpired"/> fire, see <see cref="TimeAccessExpiry"/> for that.
|
||||
/// </summary>
|
||||
public class TokenSwapWebAPIFactory
|
||||
public bool AutoRefresh { get; set; }
|
||||
/// <summary>
|
||||
/// If true when calling <see cref="GetWebApiAsync"/>, will time how long it takes for access to Spotify to expire. The event <see cref="OnAccessTokenExpired"/> fires when the timer elapses.
|
||||
/// </summary>
|
||||
public bool TimeAccessExpiry { get; set; }
|
||||
/// <summary>
|
||||
/// The maximum time in seconds to wait for a SpotifyWebAPI to be returned. The timeout is cancelled early regardless if an auth success or failure occured.
|
||||
/// </summary>
|
||||
public int Timeout { get; set; }
|
||||
public Scope Scope { get; set; }
|
||||
/// <summary>
|
||||
/// The URI (or URL) of the exchange server which exchanges the auth code for access and refresh tokens.
|
||||
/// </summary>
|
||||
public string ExchangeServerUri { get; set; }
|
||||
/// <summary>
|
||||
/// The URI (or URL) of where a callback server to receive the auth code will be hosted. e.g. http://localhost:4002
|
||||
/// </summary>
|
||||
public string HostServerUri { get; set; }
|
||||
/// <summary>
|
||||
/// Opens the user's browser and visits the exchange server for you, triggering the key exchange. This should be true unless you want to handle the key exchange in a nicer way.
|
||||
/// </summary>
|
||||
public bool OpenBrowser { get; set; }
|
||||
/// <summary>
|
||||
/// The HTML to respond with when the callback server has been reached. By default, it is set to close the window on arrival.
|
||||
/// </summary>
|
||||
public string HtmlResponse { get; set; }
|
||||
/// <summary>
|
||||
/// Whether or not to show a dialog saying "Is this you?" during the initial key exchange. It should be noted that this would allow a user the opportunity to change accounts.
|
||||
/// </summary>
|
||||
public bool ShowDialog { get; set; }
|
||||
/// <summary>
|
||||
/// The maximum amount of times to retry getting a token.
|
||||
/// <para/>
|
||||
/// A token get is attempted every time you <see cref="GetWebApiAsync"/> and <see cref="RefreshAuthAsync"/>. Increasing this may improve how often these actions succeed - although it won't solve any underlying problems causing a get token failure.
|
||||
/// </summary>
|
||||
public int MaxGetTokenRetries { get; set; } = 10;
|
||||
/// <summary>
|
||||
/// Returns a SpotifyWebAPI using the TokenSwapAuth process.
|
||||
/// </summary>
|
||||
/// <param name="exchangeServerUri">The URI (or URL) of the exchange server which exchanges the auth code for access and refresh tokens.</param>
|
||||
/// <param name="scope"></param>
|
||||
/// <param name="hostServerUri">The URI (or URL) of where a callback server to receive the auth code will be hosted. e.g. http://localhost:4002</param>
|
||||
/// <param name="timeout">The maximum time in seconds to wait for a SpotifyWebAPI to be returned. The timeout is cancelled early regardless if an auth success or failure occured.</param>
|
||||
/// <param name="autoRefresh">Access provided by Spotify expires after 1 hour. If true, access will attempt to be silently (without opening a browser) refreshed automatically.</param>
|
||||
/// <param name="openBrowser">Opens the user's browser and visits the exchange server for you, triggering the key exchange. This should be true unless you want to handle the key exchange in a nicer way.</param>
|
||||
public TokenSwapWebAPIFactory(string exchangeServerUri, Scope scope = Scope.None, string hostServerUri = "http://localhost:4002", int timeout = 10, bool autoRefresh = false, bool openBrowser = true)
|
||||
{
|
||||
/// <summary>
|
||||
/// Access provided by Spotify expires after 1 hour. If true, <see cref="TokenSwapAuth"/> will time the access tokens, and access will attempt to be silently (without opening a browser) refreshed automatically. This will not make <see cref="OnAccessTokenExpired"/> fire, see <see cref="TimeAccessExpiry"/> for that.
|
||||
/// </summary>
|
||||
public bool AutoRefresh { get; set; }
|
||||
/// <summary>
|
||||
/// If true when calling <see cref="GetWebApiAsync"/>, will time how long it takes for access to Spotify to expire. The event <see cref="OnAccessTokenExpired"/> fires when the timer elapses.
|
||||
/// </summary>
|
||||
public bool TimeAccessExpiry { get; set; }
|
||||
/// <summary>
|
||||
/// The maximum time in seconds to wait for a SpotifyWebAPI to be returned. The timeout is cancelled early regardless if an auth success or failure occured.
|
||||
/// </summary>
|
||||
public int Timeout { get; set; }
|
||||
public Scope Scope { get; set; }
|
||||
/// <summary>
|
||||
/// The URI (or URL) of the exchange server which exchanges the auth code for access and refresh tokens.
|
||||
/// </summary>
|
||||
public string ExchangeServerUri { get; set; }
|
||||
/// <summary>
|
||||
/// The URI (or URL) of where a callback server to receive the auth code will be hosted. e.g. http://localhost:4002
|
||||
/// </summary>
|
||||
public string HostServerUri { get; set; }
|
||||
/// <summary>
|
||||
/// Opens the user's browser and visits the exchange server for you, triggering the key exchange. This should be true unless you want to handle the key exchange in a nicer way.
|
||||
/// </summary>
|
||||
public bool OpenBrowser { get; set; }
|
||||
/// <summary>
|
||||
/// The HTML to respond with when the callback server has been reached. By default, it is set to close the window on arrival.
|
||||
/// </summary>
|
||||
public string HtmlResponse { get; set; }
|
||||
/// <summary>
|
||||
/// Whether or not to show a dialog saying "Is this you?" during the initial key exchange. It should be noted that this would allow a user the opportunity to change accounts.
|
||||
/// </summary>
|
||||
public bool ShowDialog { get; set; }
|
||||
/// <summary>
|
||||
/// The maximum amount of times to retry getting a token.
|
||||
/// <para/>
|
||||
/// A token get is attempted every time you <see cref="GetWebApiAsync"/> and <see cref="RefreshAuthAsync"/>. Increasing this may improve how often these actions succeed - although it won't solve any underlying problems causing a get token failure.
|
||||
/// </summary>
|
||||
public int MaxGetTokenRetries { get; set; } = 10;
|
||||
/// <summary>
|
||||
/// Returns a SpotifyWebAPI using the TokenSwapAuth process.
|
||||
/// </summary>
|
||||
/// <param name="exchangeServerUri">The URI (or URL) of the exchange server which exchanges the auth code for access and refresh tokens.</param>
|
||||
/// <param name="scope"></param>
|
||||
/// <param name="hostServerUri">The URI (or URL) of where a callback server to receive the auth code will be hosted. e.g. http://localhost:4002</param>
|
||||
/// <param name="timeout">The maximum time in seconds to wait for a SpotifyWebAPI to be returned. The timeout is cancelled early regardless if an auth success or failure occured.</param>
|
||||
/// <param name="autoRefresh">Access provided by Spotify expires after 1 hour. If true, access will attempt to be silently (without opening a browser) refreshed automatically.</param>
|
||||
/// <param name="openBrowser">Opens the user's browser and visits the exchange server for you, triggering the key exchange. This should be true unless you want to handle the key exchange in a nicer way.</param>
|
||||
public TokenSwapWebAPIFactory(string exchangeServerUri, Scope scope = Scope.None, string hostServerUri = "http://localhost:4002", int timeout = 10, bool autoRefresh = false, bool openBrowser = true)
|
||||
AutoRefresh = autoRefresh;
|
||||
Timeout = timeout;
|
||||
Scope = scope;
|
||||
ExchangeServerUri = exchangeServerUri;
|
||||
HostServerUri = hostServerUri;
|
||||
OpenBrowser = openBrowser;
|
||||
|
||||
OnAccessTokenExpired += async(sender, e) =>
|
||||
{
|
||||
if (AutoRefresh)
|
||||
{
|
||||
AutoRefresh = autoRefresh;
|
||||
Timeout = timeout;
|
||||
Scope = scope;
|
||||
ExchangeServerUri = exchangeServerUri;
|
||||
HostServerUri = hostServerUri;
|
||||
OpenBrowser = openBrowser;
|
||||
|
||||
OnAccessTokenExpired += async (sender, e) =>
|
||||
{
|
||||
if (AutoRefresh)
|
||||
{
|
||||
await RefreshAuthAsync();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Token lastToken;
|
||||
SpotifyWebAPI lastWebApi;
|
||||
TokenSwapAuth lastAuth;
|
||||
|
||||
public class ExchangeReadyEventArgs : EventArgs
|
||||
{
|
||||
public string ExchangeUri { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// When the URI to get an authorization code is ready to be used to be visited. Not required if <see cref="OpenBrowser"/> is true as the exchange URI will automatically be visited for you.
|
||||
/// </summary>
|
||||
public event EventHandler<ExchangeReadyEventArgs> OnExchangeReady;
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the access for a SpotifyWebAPI returned by this factory.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task RefreshAuthAsync()
|
||||
{
|
||||
Token token = await lastAuth.RefreshAuthAsync(lastToken.RefreshToken);
|
||||
|
||||
if (token == null)
|
||||
{
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs($"Token not returned by server."));
|
||||
}
|
||||
else if (token.HasError())
|
||||
{
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs($"{token.Error} {token.ErrorDescription}"));
|
||||
}
|
||||
else if (string.IsNullOrEmpty(token.AccessToken))
|
||||
{
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs("Token had no access token attached."));
|
||||
}
|
||||
else
|
||||
{
|
||||
lastWebApi.AccessToken = token.AccessToken;
|
||||
OnAuthSuccess?.Invoke(this, new AuthSuccessEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
// By defining empty EventArgs objects, you can specify additional information later on as you see fit and it won't
|
||||
// be considered a breaking change to consumers of this API.
|
||||
//
|
||||
// They don't even need to be constructed for their associated events to be invoked - just pass the static Empty property.
|
||||
public class AccessTokenExpiredEventArgs : EventArgs
|
||||
{
|
||||
public static new AccessTokenExpiredEventArgs Empty { get; } = new AccessTokenExpiredEventArgs();
|
||||
|
||||
public AccessTokenExpiredEventArgs()
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// When the authorization from Spotify expires. This will only occur if <see cref="AutoRefresh"/> is true.
|
||||
/// </summary>
|
||||
public event EventHandler<AccessTokenExpiredEventArgs> OnAccessTokenExpired;
|
||||
|
||||
public class AuthSuccessEventArgs : EventArgs
|
||||
{
|
||||
public static new AuthSuccessEventArgs Empty { get; } = new AuthSuccessEventArgs();
|
||||
|
||||
public AuthSuccessEventArgs()
|
||||
{
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// When an authorization attempt succeeds and gains authorization.
|
||||
/// </summary>
|
||||
public event EventHandler<AuthSuccessEventArgs> OnAuthSuccess;
|
||||
|
||||
public class AuthFailureEventArgs : EventArgs
|
||||
{
|
||||
public static new AuthFailureEventArgs Empty { get; } = new AuthFailureEventArgs("");
|
||||
|
||||
public string Error { get; }
|
||||
|
||||
public AuthFailureEventArgs(string error)
|
||||
{
|
||||
Error = error;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// When an authorization attempt fails to gain authorization.
|
||||
/// </summary>
|
||||
public event EventHandler<AuthFailureEventArgs> OnAuthFailure;
|
||||
|
||||
/// <summary>
|
||||
/// Manually triggers the timeout for any ongoing get web API request.
|
||||
/// </summary>
|
||||
public void CancelGetWebApiRequest()
|
||||
{
|
||||
if (webApiTimeoutTimer == null) return;
|
||||
|
||||
// The while loop in GetWebApiSync() will react and trigger the timeout.
|
||||
webApiTimeoutTimer.Stop();
|
||||
webApiTimeoutTimer.Dispose();
|
||||
webApiTimeoutTimer = null;
|
||||
}
|
||||
|
||||
System.Timers.Timer webApiTimeoutTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an authorized and ready to use SpotifyWebAPI by following the SecureAuthorizationCodeAuth process with its current settings.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<SpotifyWebAPI> GetWebApiAsync()
|
||||
{
|
||||
return await Task<SpotifyWebAPI>.Factory.StartNew(() =>
|
||||
{
|
||||
bool currentlyAuthorizing = true;
|
||||
|
||||
// Cancel any ongoing get web API requests
|
||||
CancelGetWebApiRequest();
|
||||
|
||||
lastAuth = new TokenSwapAuth(
|
||||
exchangeServerUri: ExchangeServerUri,
|
||||
serverUri: HostServerUri,
|
||||
scope: Scope,
|
||||
htmlResponse: HtmlResponse)
|
||||
{
|
||||
ShowDialog = ShowDialog,
|
||||
MaxGetTokenRetries = MaxGetTokenRetries,
|
||||
TimeAccessExpiry = AutoRefresh || TimeAccessExpiry
|
||||
};
|
||||
lastAuth.AuthReceived += async (sender, response) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(response.Error) || string.IsNullOrEmpty(response.Code))
|
||||
{
|
||||
// We only want one auth failure to be fired, if the request timed out then don't bother.
|
||||
if (!webApiTimeoutTimer.Enabled) return;
|
||||
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs(response.Error));
|
||||
currentlyAuthorizing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
lastToken = await lastAuth.ExchangeCodeAsync(response.Code);
|
||||
|
||||
if (lastToken == null || lastToken.HasError() || string.IsNullOrEmpty(lastToken.AccessToken))
|
||||
{
|
||||
// We only want one auth failure to be fired, if the request timed out then don't bother.
|
||||
if (!webApiTimeoutTimer.Enabled) return;
|
||||
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs("Exchange token not returned by server."));
|
||||
currentlyAuthorizing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastWebApi != null)
|
||||
{
|
||||
lastWebApi.Dispose();
|
||||
}
|
||||
lastWebApi = new SpotifyWebAPI()
|
||||
{
|
||||
TokenType = lastToken.TokenType,
|
||||
AccessToken = lastToken.AccessToken
|
||||
};
|
||||
|
||||
lastAuth.Stop();
|
||||
|
||||
OnAuthSuccess?.Invoke(this, AuthSuccessEventArgs.Empty);
|
||||
currentlyAuthorizing = false;
|
||||
};
|
||||
lastAuth.OnAccessTokenExpired += async (sender, e) =>
|
||||
{
|
||||
if (TimeAccessExpiry)
|
||||
{
|
||||
OnAccessTokenExpired?.Invoke(sender, AccessTokenExpiredEventArgs.Empty);
|
||||
}
|
||||
|
||||
if (AutoRefresh)
|
||||
{
|
||||
await RefreshAuthAsync();
|
||||
}
|
||||
};
|
||||
lastAuth.Start();
|
||||
OnExchangeReady?.Invoke(this, new ExchangeReadyEventArgs { ExchangeUri = lastAuth.GetUri() });
|
||||
if (OpenBrowser)
|
||||
{
|
||||
lastAuth.OpenBrowser();
|
||||
}
|
||||
|
||||
webApiTimeoutTimer = new System.Timers.Timer
|
||||
{
|
||||
AutoReset = false,
|
||||
Enabled = true,
|
||||
Interval = Timeout * 1000
|
||||
};
|
||||
|
||||
while (currentlyAuthorizing && webApiTimeoutTimer.Enabled) ;
|
||||
|
||||
// If a timeout occurred
|
||||
if (lastWebApi == null && currentlyAuthorizing)
|
||||
{
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs("Authorization request has timed out."));
|
||||
}
|
||||
|
||||
return lastWebApi;
|
||||
});
|
||||
await RefreshAuthAsync();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Token lastToken;
|
||||
SpotifyWebAPI lastWebApi;
|
||||
TokenSwapAuth lastAuth;
|
||||
|
||||
public class ExchangeReadyEventArgs : EventArgs
|
||||
{
|
||||
public string ExchangeUri { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// When the URI to get an authorization code is ready to be used to be visited. Not required if <see cref="OpenBrowser"/> is true as the exchange URI will automatically be visited for you.
|
||||
/// </summary>
|
||||
public event EventHandler<ExchangeReadyEventArgs> OnExchangeReady;
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the access for a SpotifyWebAPI returned by this factory.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task RefreshAuthAsync()
|
||||
{
|
||||
Token token = await lastAuth.RefreshAuthAsync(lastToken.RefreshToken);
|
||||
|
||||
if (token == null)
|
||||
{
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs($"Token not returned by server."));
|
||||
}
|
||||
else if (token.HasError())
|
||||
{
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs($"{token.Error} {token.ErrorDescription}"));
|
||||
}
|
||||
else if (string.IsNullOrEmpty(token.AccessToken))
|
||||
{
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs("Token had no access token attached."));
|
||||
}
|
||||
else
|
||||
{
|
||||
lastWebApi.AccessToken = token.AccessToken;
|
||||
OnAuthSuccess?.Invoke(this, new AuthSuccessEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
// By defining empty EventArgs objects, you can specify additional information later on as you see fit and it won't
|
||||
// be considered a breaking change to consumers of this API.
|
||||
//
|
||||
// They don't even need to be constructed for their associated events to be invoked - just pass the static Empty property.
|
||||
public class AccessTokenExpiredEventArgs : EventArgs
|
||||
{
|
||||
public static new AccessTokenExpiredEventArgs Empty { get; } = new AccessTokenExpiredEventArgs();
|
||||
|
||||
public AccessTokenExpiredEventArgs()
|
||||
{ }
|
||||
}
|
||||
/// <summary>
|
||||
/// When the authorization from Spotify expires. This will only occur if <see cref="AutoRefresh"/> is true.
|
||||
/// </summary>
|
||||
public event EventHandler<AccessTokenExpiredEventArgs> OnAccessTokenExpired;
|
||||
|
||||
public class AuthSuccessEventArgs : EventArgs
|
||||
{
|
||||
public static new AuthSuccessEventArgs Empty { get; } = new AuthSuccessEventArgs();
|
||||
|
||||
public AuthSuccessEventArgs()
|
||||
{ }
|
||||
}
|
||||
/// <summary>
|
||||
/// When an authorization attempt succeeds and gains authorization.
|
||||
/// </summary>
|
||||
public event EventHandler<AuthSuccessEventArgs> OnAuthSuccess;
|
||||
|
||||
public class AuthFailureEventArgs : EventArgs
|
||||
{
|
||||
public static new AuthFailureEventArgs Empty { get; } = new AuthFailureEventArgs("");
|
||||
|
||||
public string Error { get; }
|
||||
|
||||
public AuthFailureEventArgs(string error)
|
||||
{
|
||||
Error = error;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// When an authorization attempt fails to gain authorization.
|
||||
/// </summary>
|
||||
public event EventHandler<AuthFailureEventArgs> OnAuthFailure;
|
||||
|
||||
/// <summary>
|
||||
/// Manually triggers the timeout for any ongoing get web API request.
|
||||
/// </summary>
|
||||
public void CancelGetWebApiRequest()
|
||||
{
|
||||
if (webApiTimeoutTimer == null) return;
|
||||
|
||||
// The while loop in GetWebApiSync() will react and trigger the timeout.
|
||||
webApiTimeoutTimer.Stop();
|
||||
webApiTimeoutTimer.Dispose();
|
||||
webApiTimeoutTimer = null;
|
||||
}
|
||||
|
||||
System.Timers.Timer webApiTimeoutTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an authorized and ready to use SpotifyWebAPI by following the SecureAuthorizationCodeAuth process with its current settings.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<SpotifyWebAPI> GetWebApiAsync()
|
||||
{
|
||||
return await Task<SpotifyWebAPI>.Factory.StartNew(() =>
|
||||
{
|
||||
bool currentlyAuthorizing = true;
|
||||
|
||||
// Cancel any ongoing get web API requests
|
||||
CancelGetWebApiRequest();
|
||||
|
||||
lastAuth = new TokenSwapAuth(
|
||||
exchangeServerUri: ExchangeServerUri,
|
||||
serverUri: HostServerUri,
|
||||
scope: Scope,
|
||||
htmlResponse: HtmlResponse)
|
||||
{
|
||||
ShowDialog = ShowDialog,
|
||||
MaxGetTokenRetries = MaxGetTokenRetries,
|
||||
TimeAccessExpiry = AutoRefresh || TimeAccessExpiry
|
||||
};
|
||||
lastAuth.AuthReceived += async(sender, response) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(response.Error) || string.IsNullOrEmpty(response.Code))
|
||||
{
|
||||
// We only want one auth failure to be fired, if the request timed out then don't bother.
|
||||
if (!webApiTimeoutTimer.Enabled) return;
|
||||
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs(response.Error));
|
||||
currentlyAuthorizing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
lastToken = await lastAuth.ExchangeCodeAsync(response.Code);
|
||||
|
||||
if (lastToken == null || lastToken.HasError() || string.IsNullOrEmpty(lastToken.AccessToken))
|
||||
{
|
||||
// We only want one auth failure to be fired, if the request timed out then don't bother.
|
||||
if (!webApiTimeoutTimer.Enabled) return;
|
||||
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs("Exchange token not returned by server."));
|
||||
currentlyAuthorizing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastWebApi != null)
|
||||
{
|
||||
lastWebApi.Dispose();
|
||||
}
|
||||
lastWebApi = new SpotifyWebAPI()
|
||||
{
|
||||
TokenType = lastToken.TokenType,
|
||||
AccessToken = lastToken.AccessToken
|
||||
};
|
||||
|
||||
lastAuth.Stop();
|
||||
|
||||
OnAuthSuccess?.Invoke(this, AuthSuccessEventArgs.Empty);
|
||||
currentlyAuthorizing = false;
|
||||
};
|
||||
lastAuth.OnAccessTokenExpired += async(sender, e) =>
|
||||
{
|
||||
if (TimeAccessExpiry)
|
||||
{
|
||||
OnAccessTokenExpired?.Invoke(sender, AccessTokenExpiredEventArgs.Empty);
|
||||
}
|
||||
|
||||
if (AutoRefresh)
|
||||
{
|
||||
await RefreshAuthAsync();
|
||||
}
|
||||
};
|
||||
lastAuth.Start();
|
||||
OnExchangeReady?.Invoke(this, new ExchangeReadyEventArgs { ExchangeUri = lastAuth.GetUri() });
|
||||
if (OpenBrowser)
|
||||
{
|
||||
lastAuth.OpenBrowser();
|
||||
}
|
||||
|
||||
webApiTimeoutTimer = new System.Timers.Timer
|
||||
{
|
||||
AutoReset = false,
|
||||
Enabled = true,
|
||||
Interval = Timeout * 1000
|
||||
};
|
||||
|
||||
while (currentlyAuthorizing && webApiTimeoutTimer.Enabled);
|
||||
|
||||
// If a timeout occurred
|
||||
if (lastWebApi == null && currentlyAuthorizing)
|
||||
{
|
||||
OnAuthFailure?.Invoke(this, new AuthFailureEventArgs("Authorization request has timed out."));
|
||||
}
|
||||
|
||||
return lastWebApi;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -7,27 +7,27 @@ using SpotifyAPI.Web.Examples.ASP.Models;
|
||||
|
||||
namespace SpotifyAPI.Web.Examples.ASP.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = "Spotify")]
|
||||
public class HomeController : Controller
|
||||
[Authorize(AuthenticationSchemes = "Spotify")]
|
||||
public class HomeController : Controller
|
||||
{
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var accessToken = await HttpContext.GetTokenAsync("Spotify", "access_token");
|
||||
SpotifyWebAPI api = new SpotifyWebAPI
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
TokenType = "Bearer"
|
||||
};
|
||||
var accessToken = await HttpContext.GetTokenAsync("Spotify", "access_token");
|
||||
SpotifyWebAPI api = new SpotifyWebAPI
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
TokenType = "Bearer"
|
||||
};
|
||||
|
||||
var savedTracks = await api.GetSavedTracksAsync(50);
|
||||
var savedTracks = await api.GetSavedTracksAsync(50);
|
||||
|
||||
return View(new IndexModel { SavedTracks = savedTracks });
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
return View(new IndexModel { SavedTracks = savedTracks });
|
||||
}
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
@ -2,10 +2,10 @@ using System;
|
||||
|
||||
namespace SpotifyAPI.Web.Examples.ASP.Models
|
||||
{
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
}
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
}
|
@ -3,8 +3,8 @@ using SpotifyAPI.Web.Models;
|
||||
|
||||
namespace SpotifyAPI.Web.Examples.ASP.Models
|
||||
{
|
||||
public class IndexModel
|
||||
{
|
||||
public Paging<SavedTrack> SavedTracks;
|
||||
}
|
||||
}
|
||||
public class IndexModel
|
||||
{
|
||||
public Paging<SavedTrack> SavedTracks;
|
||||
}
|
||||
}
|
@ -9,18 +9,18 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace SpotifyAPI.Web.Examples.ASP
|
||||
{
|
||||
public class Program
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
@ -8,61 +8,61 @@ using SpotifyAPI.Web.Enums;
|
||||
|
||||
namespace SpotifyAPI.Web.Examples.ASP
|
||||
{
|
||||
public class Startup
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddControllersWithViews();
|
||||
|
||||
services.AddAuthentication(o => o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddCookie()
|
||||
.AddSpotify(options =>
|
||||
{
|
||||
var scopes = Scope.UserLibraryRead;
|
||||
options.Scope.Add(scopes.GetStringAttribute(","));
|
||||
|
||||
options.SaveTokens = true;
|
||||
options.ClientId = Configuration["client_id"];
|
||||
options.ClientSecret = Configuration["client_secret"];
|
||||
options.CallbackPath = "/callback";
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
// app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
}
|
||||
Configuration = configuration;
|
||||
}
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddControllersWithViews();
|
||||
|
||||
services.AddAuthentication(o => o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddCookie()
|
||||
.AddSpotify(options =>
|
||||
{
|
||||
var scopes = Scope.UserLibraryRead | Scope.UserModifyPlaybackState;
|
||||
options.Scope.Add(scopes.GetStringAttribute(","));
|
||||
|
||||
options.SaveTokens = true;
|
||||
options.ClientId = Configuration["client_id"];
|
||||
options.ClientSecret = Configuration["client_secret"];
|
||||
options.CallbackPath = "/callback";
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
// app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using SpotifyAPI.Web.Auth;
|
||||
using SpotifyAPI.Web.Enums;
|
||||
@ -6,72 +6,70 @@ using SpotifyAPI.Web.Models;
|
||||
|
||||
namespace SpotifyAPI.Web.Examples.CLI
|
||||
{
|
||||
internal static class Program
|
||||
internal static class Program
|
||||
{
|
||||
private static string _clientId = ""; //"";
|
||||
private static string _secretId = ""; //"";
|
||||
|
||||
// ReSharper disable once UnusedParameter.Local
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
private static string _clientId = ""; //"";
|
||||
private static string _secretId = ""; //"";
|
||||
_clientId = string.IsNullOrEmpty(_clientId) ?
|
||||
Environment.GetEnvironmentVariable("SPOTIFY_CLIENT_ID") :
|
||||
_clientId;
|
||||
|
||||
// ReSharper disable once UnusedParameter.Local
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
_clientId = string.IsNullOrEmpty(_clientId)
|
||||
? Environment.GetEnvironmentVariable("SPOTIFY_CLIENT_ID")
|
||||
: _clientId;
|
||||
_secretId = string.IsNullOrEmpty(_secretId) ?
|
||||
Environment.GetEnvironmentVariable("SPOTIFY_SECRET_ID") :
|
||||
_secretId;
|
||||
|
||||
_secretId = string.IsNullOrEmpty(_secretId)
|
||||
? Environment.GetEnvironmentVariable("SPOTIFY_SECRET_ID")
|
||||
: _secretId;
|
||||
Console.WriteLine("####### Spotify API Example #######");
|
||||
Console.WriteLine("This example uses AuthorizationCodeAuth.");
|
||||
Console.WriteLine(
|
||||
"Tip: If you want to supply your ClientID and SecretId beforehand, use env variables (SPOTIFY_CLIENT_ID and SPOTIFY_SECRET_ID)");
|
||||
|
||||
Console.WriteLine("####### Spotify API Example #######");
|
||||
Console.WriteLine("This example uses AuthorizationCodeAuth.");
|
||||
Console.WriteLine(
|
||||
"Tip: If you want to supply your ClientID and SecretId beforehand, use env variables (SPOTIFY_CLIENT_ID and SPOTIFY_SECRET_ID)");
|
||||
var auth =
|
||||
new AuthorizationCodeAuth(_clientId, _secretId, "http://localhost:4002", "http://localhost:4002",
|
||||
Scope.PlaylistReadPrivate | Scope.PlaylistReadCollaborative);
|
||||
auth.AuthReceived += AuthOnAuthReceived;
|
||||
auth.Start();
|
||||
auth.OpenBrowser();
|
||||
|
||||
var auth =
|
||||
new AuthorizationCodeAuth(_clientId, _secretId, "http://localhost:4002", "http://localhost:4002",
|
||||
Scope.PlaylistReadPrivate | Scope.PlaylistReadCollaborative);
|
||||
auth.AuthReceived += AuthOnAuthReceived;
|
||||
auth.Start();
|
||||
auth.OpenBrowser();
|
||||
Console.ReadLine();
|
||||
auth.Stop(0);
|
||||
|
||||
Console.ReadLine();
|
||||
auth.Stop(0);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static async void AuthOnAuthReceived(object sender, AuthorizationCode payload)
|
||||
{
|
||||
var auth = (AuthorizationCodeAuth) sender;
|
||||
auth.Stop();
|
||||
|
||||
Token token = await auth.ExchangeCode(payload.Code);
|
||||
var api = new SpotifyWebAPI
|
||||
{
|
||||
AccessToken = token.AccessToken,
|
||||
TokenType = token.TokenType
|
||||
};
|
||||
await PrintUsefulData(api);
|
||||
}
|
||||
|
||||
private static async Task PrintAllPlaylistTracks(SpotifyWebAPI api, Paging<SimplePlaylist> playlists)
|
||||
{
|
||||
if (playlists.Items == null) return;
|
||||
|
||||
playlists.Items.ForEach(playlist => Console.WriteLine($"- {playlist.Name}"));
|
||||
if(playlists.HasNextPage())
|
||||
await PrintAllPlaylistTracks(api, await api.GetNextPageAsync(playlists));
|
||||
}
|
||||
|
||||
private static async Task PrintUsefulData(SpotifyWebAPI api)
|
||||
{
|
||||
PrivateProfile profile = await api.GetPrivateProfileAsync();
|
||||
string name = string.IsNullOrEmpty(profile.DisplayName) ? profile.Id : profile.DisplayName;
|
||||
Console.WriteLine($"Hello there, {name}!");
|
||||
|
||||
Console.WriteLine("Your playlists:");
|
||||
await PrintAllPlaylistTracks(api, api.GetUserPlaylists(profile.Id));
|
||||
}
|
||||
}
|
||||
|
||||
private static async void AuthOnAuthReceived(object sender, AuthorizationCode payload)
|
||||
{
|
||||
var auth = (AuthorizationCodeAuth) sender;
|
||||
auth.Stop();
|
||||
|
||||
Token token = await auth.ExchangeCode(payload.Code);
|
||||
var api = new SpotifyWebAPI
|
||||
{
|
||||
AccessToken = token.AccessToken,
|
||||
TokenType = token.TokenType
|
||||
};
|
||||
await PrintUsefulData(api);
|
||||
}
|
||||
|
||||
private static async Task PrintAllPlaylistTracks(SpotifyWebAPI api, Paging<SimplePlaylist> playlists)
|
||||
{
|
||||
if (playlists.Items == null) return;
|
||||
|
||||
playlists.Items.ForEach(playlist => Console.WriteLine($"- {playlist.Name}"));
|
||||
if (playlists.HasNextPage())
|
||||
await PrintAllPlaylistTracks(api, await api.GetNextPageAsync(playlists));
|
||||
}
|
||||
|
||||
private static async Task PrintUsefulData(SpotifyWebAPI api)
|
||||
{
|
||||
PrivateProfile profile = await api.GetPrivateProfileAsync();
|
||||
string name = string.IsNullOrEmpty(profile.DisplayName) ? profile.Id : profile.DisplayName;
|
||||
Console.WriteLine($"Hello there, {name}!");
|
||||
|
||||
Console.WriteLine("Your playlists:");
|
||||
await PrintAllPlaylistTracks(api, api.GetUserPlaylists(profile.Id));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,116 +1,116 @@
|
||||
using System;
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace SpotifyAPI.Web.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ProxyConfigTest
|
||||
[TestFixture]
|
||||
public class ProxyConfigTest
|
||||
{
|
||||
#region GetUri
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostNameWithScheme()
|
||||
{
|
||||
#region GetUri
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostNameWithScheme()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = "https://test-host.com" };
|
||||
CheckUri(config.GetUri(), "https", "test-host.com", 80);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostNameWithoutScheme()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = "test-host.com" };
|
||||
CheckUri(config.GetUri(), "http", "test-host.com", 80);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostNameWithSchemeAndPort()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig
|
||||
{
|
||||
Host = "https://test-host.com",
|
||||
Port = 8080
|
||||
};
|
||||
CheckUri(config.GetUri(), "https", "test-host.com", 8080);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostAddressWithScheme()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = "https://192.168.0.1" };
|
||||
CheckUri(config.GetUri(), "https", "192.168.0.1", 80);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostAddressWithoutScheme()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = "192.168.0.1" };
|
||||
CheckUri(config.GetUri(), "http", "192.168.0.1", 80);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostAddressWithSchemeAndPort()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig
|
||||
{
|
||||
Host = "https://192.168.0.1",
|
||||
Port = 8080
|
||||
};
|
||||
CheckUri(config.GetUri(), "https", "192.168.0.1", 8080);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_NullHost()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = null };
|
||||
Assert.Throws<ArgumentNullException>(() => config.GetUri());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_EmptyHost()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = string.Empty };
|
||||
Assert.Throws<UriFormatException>(() => config.GetUri());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_NegativePort()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig
|
||||
{
|
||||
Host = "test-host.com",
|
||||
Port = -10
|
||||
};
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => config.GetUri());
|
||||
}
|
||||
|
||||
#endregion GetUri
|
||||
|
||||
[Test]
|
||||
public void Set_Null()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig
|
||||
{
|
||||
Host = "https://test-host.com",
|
||||
Port = 1234,
|
||||
Username = "admin",
|
||||
Password = "password",
|
||||
BypassProxyOnLocal = true
|
||||
};
|
||||
config.Set(null);
|
||||
|
||||
Assert.NotNull(config);
|
||||
Assert.Null(config.Host);
|
||||
Assert.AreEqual(80, config.Port);
|
||||
Assert.Null(config.Username);
|
||||
Assert.Null(config.Password);
|
||||
Assert.False(config.BypassProxyOnLocal);
|
||||
}
|
||||
|
||||
private static void CheckUri(Uri uri, string expectedScheme, string expectedHost, int expectedPort)
|
||||
{
|
||||
Assert.AreEqual(expectedScheme, uri.Scheme);
|
||||
Assert.AreEqual(expectedHost, uri.Host);
|
||||
Assert.AreEqual(expectedPort, uri.Port);
|
||||
}
|
||||
ProxyConfig config = new ProxyConfig { Host = "https://test-host.com" };
|
||||
CheckUri(config.GetUri(), "https", "test-host.com", 80);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostNameWithoutScheme()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = "test-host.com" };
|
||||
CheckUri(config.GetUri(), "http", "test-host.com", 80);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostNameWithSchemeAndPort()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig
|
||||
{
|
||||
Host = "https://test-host.com",
|
||||
Port = 8080
|
||||
};
|
||||
CheckUri(config.GetUri(), "https", "test-host.com", 8080);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostAddressWithScheme()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = "https://192.168.0.1" };
|
||||
CheckUri(config.GetUri(), "https", "192.168.0.1", 80);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostAddressWithoutScheme()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = "192.168.0.1" };
|
||||
CheckUri(config.GetUri(), "http", "192.168.0.1", 80);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_HostAddressWithSchemeAndPort()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig
|
||||
{
|
||||
Host = "https://192.168.0.1",
|
||||
Port = 8080
|
||||
};
|
||||
CheckUri(config.GetUri(), "https", "192.168.0.1", 8080);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_NullHost()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = null };
|
||||
Assert.Throws<ArgumentNullException>(() => config.GetUri());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_EmptyHost()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig { Host = string.Empty };
|
||||
Assert.Throws<UriFormatException>(() => config.GetUri());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUri_NegativePort()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig
|
||||
{
|
||||
Host = "test-host.com",
|
||||
Port = -10
|
||||
};
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => config.GetUri());
|
||||
}
|
||||
|
||||
#endregion GetUri
|
||||
|
||||
[Test]
|
||||
public void Set_Null()
|
||||
{
|
||||
ProxyConfig config = new ProxyConfig
|
||||
{
|
||||
Host = "https://test-host.com",
|
||||
Port = 1234,
|
||||
Username = "admin",
|
||||
Password = "password",
|
||||
BypassProxyOnLocal = true
|
||||
};
|
||||
config.Set(null);
|
||||
|
||||
Assert.NotNull(config);
|
||||
Assert.Null(config.Host);
|
||||
Assert.AreEqual(80, config.Port);
|
||||
Assert.Null(config.Username);
|
||||
Assert.Null(config.Password);
|
||||
Assert.False(config.BypassProxyOnLocal);
|
||||
}
|
||||
|
||||
private static void CheckUri(Uri uri, string expectedScheme, string expectedHost, int expectedPort)
|
||||
{
|
||||
Assert.AreEqual(expectedScheme, uri.Scheme);
|
||||
Assert.AreEqual(expectedHost, uri.Host);
|
||||
Assert.AreEqual(expectedPort, uri.Port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -9,72 +9,71 @@ using SpotifyAPI.Web.Models;
|
||||
|
||||
namespace SpotifyAPI.Web.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SpotifyWebAPITest
|
||||
[TestFixture]
|
||||
public class SpotifyWebAPITest
|
||||
{
|
||||
private static readonly string FixtureDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../fixtures/");
|
||||
|
||||
private Mock<IClient> _mock;
|
||||
private SpotifyWebAPI _spotify;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
private static readonly string FixtureDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../fixtures/");
|
||||
|
||||
private Mock<IClient> _mock;
|
||||
private SpotifyWebAPI _spotify;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_mock = new Mock<IClient>();
|
||||
_spotify = new SpotifyWebAPI
|
||||
{
|
||||
WebClient = _mock.Object,
|
||||
UseAuth = false
|
||||
};
|
||||
}
|
||||
|
||||
private static T GetFixture<T>(string file)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(File.ReadAllText(Path.Combine(FixtureDir, file)));
|
||||
}
|
||||
|
||||
private static bool ContainsValues(string str, params string[] values)
|
||||
{
|
||||
return values.All(str.Contains);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldGetPrivateProfile_WithoutAuth()
|
||||
{
|
||||
_spotify.UseAuth = false;
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => _spotify.GetPrivateProfile());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldGetPrivateProfile_WithAuth()
|
||||
{
|
||||
PrivateProfile profile = GetFixture<PrivateProfile>("private-user.json");
|
||||
_mock.Setup(client => client.DownloadJson<PrivateProfile>(It.IsAny<string>(), It.IsAny<Dictionary<string, string>>()))
|
||||
.Returns(new Tuple<ResponseInfo, PrivateProfile>(ResponseInfo.Empty, profile));
|
||||
|
||||
_spotify.UseAuth = true;
|
||||
Assert.AreEqual(profile, _spotify.GetPrivateProfile());
|
||||
_mock.Verify(client => client.DownloadJson<PrivateProfile>(
|
||||
It.Is<string>(str => ContainsValues(str, "/me")),
|
||||
It.IsNotNull<Dictionary<string, string>>()), Times.Exactly(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldGetPublicProfile()
|
||||
{
|
||||
PublicProfile profile = GetFixture<PublicProfile>("public-user.json");
|
||||
_mock.Setup(client => client.DownloadJson<PublicProfile>(It.IsAny<string>(), It.IsAny<Dictionary<string, string>>()))
|
||||
.Returns(new Tuple<ResponseInfo, PublicProfile>(ResponseInfo.Empty, profile));
|
||||
|
||||
|
||||
_spotify.UseAuth = false;
|
||||
Assert.AreEqual(profile, _spotify.GetPublicProfile("wizzler"));
|
||||
_mock.Verify(client => client.DownloadJson<PublicProfile>(
|
||||
It.Is<string>(str => ContainsValues(str, "/users/wizzler")),
|
||||
It.Is<Dictionary<string, string>>(headers => headers.Count == 0)), Times.Exactly(1));
|
||||
}
|
||||
|
||||
//Will add more tests once I decided if this is worth the effort (propably not?)
|
||||
_mock = new Mock<IClient>();
|
||||
_spotify = new SpotifyWebAPI
|
||||
{
|
||||
WebClient = _mock.Object,
|
||||
UseAuth = false
|
||||
};
|
||||
}
|
||||
|
||||
private static T GetFixture<T>(string file)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(File.ReadAllText(Path.Combine(FixtureDir, file)));
|
||||
}
|
||||
|
||||
private static bool ContainsValues(string str, params string[] values)
|
||||
{
|
||||
return values.All(str.Contains);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldGetPrivateProfile_WithoutAuth()
|
||||
{
|
||||
_spotify.UseAuth = false;
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => _spotify.GetPrivateProfile());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldGetPrivateProfile_WithAuth()
|
||||
{
|
||||
PrivateProfile profile = GetFixture<PrivateProfile>("private-user.json");
|
||||
_mock.Setup(client => client.DownloadJson<PrivateProfile>(It.IsAny<string>(), It.IsAny<Dictionary<string, string>>()))
|
||||
.Returns(new Tuple<ResponseInfo, PrivateProfile>(ResponseInfo.Empty, profile));
|
||||
|
||||
_spotify.UseAuth = true;
|
||||
Assert.AreEqual(profile, _spotify.GetPrivateProfile());
|
||||
_mock.Verify(client => client.DownloadJson<PrivateProfile>(
|
||||
It.Is<string>(str => ContainsValues(str, "/me")),
|
||||
It.IsNotNull<Dictionary<string, string>>()), Times.Exactly(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldGetPublicProfile()
|
||||
{
|
||||
PublicProfile profile = GetFixture<PublicProfile>("public-user.json");
|
||||
_mock.Setup(client => client.DownloadJson<PublicProfile>(It.IsAny<string>(), It.IsAny<Dictionary<string, string>>()))
|
||||
.Returns(new Tuple<ResponseInfo, PublicProfile>(ResponseInfo.Empty, profile));
|
||||
|
||||
_spotify.UseAuth = false;
|
||||
Assert.AreEqual(profile, _spotify.GetPublicProfile("wizzler"));
|
||||
_mock.Verify(client => client.DownloadJson<PublicProfile>(
|
||||
It.Is<string>(str => ContainsValues(str, "/users/wizzler")),
|
||||
It.Is<Dictionary<string, string>>(headers => headers.Count == 0)), Times.Exactly(1));
|
||||
}
|
||||
|
||||
//Will add more tests once I decided if this is worth the effort (propably not?)
|
||||
}
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
using System;
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace SpotifyAPI.Web.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class UtilTest
|
||||
[TestFixture]
|
||||
public class UtilTest
|
||||
{
|
||||
[Test]
|
||||
public void TimestampShouldBeNoFloatingPoint()
|
||||
{
|
||||
[Test]
|
||||
public void TimestampShouldBeNoFloatingPoint()
|
||||
{
|
||||
string timestamp = DateTime.Now.ToUnixTimeMillisecondsPoly().ToString();
|
||||
string timestamp = DateTime.Now.ToUnixTimeMillisecondsPoly().ToString();
|
||||
|
||||
StringAssert.DoesNotContain(".", timestamp);
|
||||
StringAssert.DoesNotContain(",", timestamp);
|
||||
}
|
||||
StringAssert.DoesNotContain(".", timestamp);
|
||||
StringAssert.DoesNotContain(",", timestamp);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace SpotifyAPI.Web.Enums
|
||||
{
|
||||
[Flags]
|
||||
public enum AlbumType
|
||||
{
|
||||
[String("album")]
|
||||
Album = 1,
|
||||
[Flags]
|
||||
public enum AlbumType
|
||||
{
|
||||
[String("album")]
|
||||
Album = 1,
|
||||
|
||||
[String("single")]
|
||||
Single = 2,
|
||||
[String("single")]
|
||||
Single = 2,
|
||||
|
||||
[String("compilation")]
|
||||
Compilation = 4,
|
||||
[String("compilation")]
|
||||
Compilation = 4,
|
||||
|
||||
[String("appears_on")]
|
||||
AppearsOn = 8,
|
||||
[String("appears_on")]
|
||||
AppearsOn = 8,
|
||||
|
||||
[String("album,single,compilation,appears_on")]
|
||||
All = 16
|
||||
}
|
||||
[String("album,single,compilation,appears_on")]
|
||||
All = 16
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace SpotifyAPI.Web.Enums
|
||||
{
|
||||
[Flags]
|
||||
public enum FollowType
|
||||
{
|
||||
[String("artist")]
|
||||
Artist = 1,
|
||||
[Flags]
|
||||
public enum FollowType
|
||||
{
|
||||
[String("artist")]
|
||||
Artist = 1,
|
||||
|
||||
[String("user")]
|
||||
User = 2
|
||||
}
|
||||
[String("user")]
|
||||
User = 2
|
||||
}
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace SpotifyAPI.Web.Enums
|
||||
{
|
||||
[Flags]
|
||||
public enum RepeatState
|
||||
{
|
||||
[String("track")]
|
||||
Track = 1,
|
||||
[Flags]
|
||||
public enum RepeatState
|
||||
{
|
||||
[String("track")]
|
||||
Track = 1,
|
||||
|
||||
[String("context")]
|
||||
Context = 2,
|
||||
[String("context")]
|
||||
Context = 2,
|
||||
|
||||
[String("off")]
|
||||
Off = 4
|
||||
}
|
||||
[String("off")]
|
||||
Off = 4
|
||||
}
|
||||
}
|
@ -1,68 +1,68 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace SpotifyAPI.Web.Enums
|
||||
{
|
||||
[Flags]
|
||||
public enum Scope
|
||||
{
|
||||
[String("")]
|
||||
None = 1,
|
||||
[Flags]
|
||||
public enum Scope
|
||||
{
|
||||
[String("")]
|
||||
None = 1,
|
||||
|
||||
[String("playlist-modify-public")]
|
||||
PlaylistModifyPublic = 2,
|
||||
[String("playlist-modify-public")]
|
||||
PlaylistModifyPublic = 2,
|
||||
|
||||
[String("playlist-modify-private")]
|
||||
PlaylistModifyPrivate = 4,
|
||||
[String("playlist-modify-private")]
|
||||
PlaylistModifyPrivate = 4,
|
||||
|
||||
[String("playlist-read-private")]
|
||||
PlaylistReadPrivate = 8,
|
||||
[String("playlist-read-private")]
|
||||
PlaylistReadPrivate = 8,
|
||||
|
||||
[String("streaming")]
|
||||
Streaming = 16,
|
||||
[String("streaming")]
|
||||
Streaming = 16,
|
||||
|
||||
[String("user-read-private")]
|
||||
UserReadPrivate = 32,
|
||||
[String("user-read-private")]
|
||||
UserReadPrivate = 32,
|
||||
|
||||
[String("user-read-email")]
|
||||
UserReadEmail = 64,
|
||||
[String("user-read-email")]
|
||||
UserReadEmail = 64,
|
||||
|
||||
[String("user-library-read")]
|
||||
UserLibraryRead = 128,
|
||||
[String("user-library-read")]
|
||||
UserLibraryRead = 128,
|
||||
|
||||
[String("user-library-modify")]
|
||||
UserLibraryModify = 256,
|
||||
[String("user-library-modify")]
|
||||
UserLibraryModify = 256,
|
||||
|
||||
[String("user-follow-modify")]
|
||||
UserFollowModify = 512,
|
||||
[String("user-follow-modify")]
|
||||
UserFollowModify = 512,
|
||||
|
||||
[String("user-follow-read")]
|
||||
UserFollowRead = 1024,
|
||||
[String("user-follow-read")]
|
||||
UserFollowRead = 1024,
|
||||
|
||||
[String("user-read-birthdate")]
|
||||
UserReadBirthdate = 2048,
|
||||
[String("user-read-birthdate")]
|
||||
UserReadBirthdate = 2048,
|
||||
|
||||
[String("user-top-read")]
|
||||
UserTopRead = 4096,
|
||||
[String("user-top-read")]
|
||||
UserTopRead = 4096,
|
||||
|
||||
[String("playlist-read-collaborative")]
|
||||
PlaylistReadCollaborative = 8192,
|
||||
[String("playlist-read-collaborative")]
|
||||
PlaylistReadCollaborative = 8192,
|
||||
|
||||
[String("user-read-recently-played")]
|
||||
UserReadRecentlyPlayed = 16384,
|
||||
[String("user-read-recently-played")]
|
||||
UserReadRecentlyPlayed = 16384,
|
||||
|
||||
[String("user-read-playback-state")]
|
||||
UserReadPlaybackState = 32768,
|
||||
[String("user-read-playback-state")]
|
||||
UserReadPlaybackState = 32768,
|
||||
|
||||
[String("user-modify-playback-state")]
|
||||
UserModifyPlaybackState = 65536,
|
||||
[String("user-modify-playback-state")]
|
||||
UserModifyPlaybackState = 65536,
|
||||
|
||||
[String("user-read-currently-playing")]
|
||||
UserReadCurrentlyPlaying = 131072,
|
||||
[String("user-read-currently-playing")]
|
||||
UserReadCurrentlyPlaying = 131072,
|
||||
|
||||
[String("app-remote-control")]
|
||||
AppRemoteControl = 262144,
|
||||
[String("app-remote-control")]
|
||||
AppRemoteControl = 262144,
|
||||
|
||||
[String("ugc-image-upload")]
|
||||
UgcImageUpload = 524288
|
||||
}
|
||||
}
|
||||
[String("ugc-image-upload")]
|
||||
UgcImageUpload = 524288
|
||||
}
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace SpotifyAPI.Web.Enums
|
||||
{
|
||||
[Flags]
|
||||
public enum SearchType
|
||||
{
|
||||
[String("artist")]
|
||||
Artist = 1,
|
||||
[Flags]
|
||||
public enum SearchType
|
||||
{
|
||||
[String("artist")]
|
||||
Artist = 1,
|
||||
|
||||
[String("album")]
|
||||
Album = 2,
|
||||
[String("album")]
|
||||
Album = 2,
|
||||
|
||||
[String("track")]
|
||||
Track = 4,
|
||||
[String("track")]
|
||||
Track = 4,
|
||||
|
||||
[String("playlist")]
|
||||
Playlist = 8,
|
||||
[String("playlist")]
|
||||
Playlist = 8,
|
||||
|
||||
[String("track,album,artist,playlist")]
|
||||
All = 16
|
||||
}
|
||||
[String("track,album,artist,playlist")]
|
||||
All = 16
|
||||
}
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace SpotifyAPI.Web.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// Only one value allowed
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum TimeRangeType
|
||||
{
|
||||
[String("long_term")]
|
||||
LongTerm = 1,
|
||||
/// <summary>
|
||||
/// Only one value allowed
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum TimeRangeType
|
||||
{
|
||||
[String("long_term")]
|
||||
LongTerm = 1,
|
||||
|
||||
[String("medium_term")]
|
||||
MediumTerm = 2,
|
||||
[String("medium_term")]
|
||||
MediumTerm = 2,
|
||||
|
||||
[String("short_term")]
|
||||
ShortTerm = 4
|
||||
}
|
||||
[String("short_term")]
|
||||
ShortTerm = 4
|
||||
}
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace SpotifyAPI.Web.Enums
|
||||
{
|
||||
[Flags]
|
||||
public enum TrackType
|
||||
{
|
||||
[String("track")]
|
||||
Track = 1,
|
||||
[Flags]
|
||||
public enum TrackType
|
||||
{
|
||||
[String("track")]
|
||||
Track = 1,
|
||||
|
||||
[String("episode")]
|
||||
Episode = 2,
|
||||
[String("episode")]
|
||||
Episode = 2,
|
||||
|
||||
[String("ad")]
|
||||
Ad = 4,
|
||||
[String("ad")]
|
||||
Ad = 4,
|
||||
|
||||
[String("unknown")]
|
||||
Unknown = 8
|
||||
}
|
||||
[String("unknown")]
|
||||
Unknown = 8
|
||||
}
|
||||
}
|
@ -1,125 +1,125 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using SpotifyAPI.Web.Models;
|
||||
|
||||
namespace SpotifyAPI.Web
|
||||
{
|
||||
public interface IClient : IDisposable
|
||||
{
|
||||
JsonSerializerSettings JsonSettings { get; set; }
|
||||
public interface IClient : IDisposable
|
||||
{
|
||||
JsonSerializerSettings JsonSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Downloads data from an URL and returns it
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, string> Download(string url, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Downloads data from an URL and returns it
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, string> Download(string url, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Downloads data async from an URL and returns it
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, string>> DownloadAsync(string url, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Downloads data async from an URL and returns it
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, string>> DownloadAsync(string url, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Downloads data from an URL and returns it
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, byte[]> DownloadRaw(string url, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Downloads data from an URL and returns it
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, byte[]> DownloadRaw(string url, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Downloads data async from an URL and returns it
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, byte[]>> DownloadRawAsync(string url, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Downloads data async from an URL and returns it
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, byte[]>> DownloadRawAsync(string url, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Downloads data from an URL and converts it to an object
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type which the object gets converted to</typeparam>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, T> DownloadJson<T>(string url, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Downloads data from an URL and converts it to an object
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type which the object gets converted to</typeparam>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, T> DownloadJson<T>(string url, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Downloads data async from an URL and converts it to an object
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type which the object gets converted to</typeparam>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, T>> DownloadJsonAsync<T>(string url, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Downloads data async from an URL and converts it to an object
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type which the object gets converted to</typeparam>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, T>> DownloadJsonAsync<T>(string url, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads data from an URL and returns the response
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, string> Upload(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Uploads data from an URL and returns the response
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, string> Upload(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads data async from an URL and returns the response
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, string>> UploadAsync(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Uploads data async from an URL and returns the response
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, string>> UploadAsync(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads data from an URL and returns the response
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, byte[]> UploadRaw(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Uploads data from an URL and returns the response
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, byte[]> UploadRaw(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads data async from an URL and returns the response
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, byte[]>> UploadRawAsync(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Uploads data async from an URL and returns the response
|
||||
/// </summary>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, byte[]>> UploadRawAsync(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads data from an URL and converts the response to an object
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type which the object gets converted to</typeparam>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, T> UploadJson<T>(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
/// <summary>
|
||||
/// Uploads data from an URL and converts the response to an object
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type which the object gets converted to</typeparam>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Tuple<ResponseInfo, T> UploadJson<T>(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
|
||||
/// <summary>
|
||||
/// Uploads data async from an URL and converts the response to an object
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type which the object gets converted to</typeparam>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, T>> UploadJsonAsync<T>(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
}
|
||||
/// <summary>
|
||||
/// Uploads data async from an URL and converts the response to an object
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The Type which the object gets converted to</typeparam>
|
||||
/// <param name="url">An URL</param>
|
||||
/// <param name="body">The Body-Data (most likely a JSON String)</param>
|
||||
/// <param name="method">The Upload-method (POST,DELETE,PUT)</param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
Task<Tuple<ResponseInfo, T>> UploadJsonAsync<T>(string url, string body, string method, Dictionary<string, string> headers = null);
|
||||
}
|
||||
}
|
@ -1,28 +1,28 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class AnalysisMeta
|
||||
{
|
||||
[JsonProperty("analyzer_platform")]
|
||||
public string AnalyzerVersion { get; set; }
|
||||
public class AnalysisMeta
|
||||
{
|
||||
[JsonProperty("analyzer_platform")]
|
||||
public string AnalyzerVersion { get; set; }
|
||||
|
||||
[JsonProperty("platform")]
|
||||
public string Platform { get; set; }
|
||||
[JsonProperty("platform")]
|
||||
public string Platform { get; set; }
|
||||
|
||||
[JsonProperty("status_code")]
|
||||
public int StatusCode { get; set; }
|
||||
[JsonProperty("status_code")]
|
||||
public int StatusCode { get; set; }
|
||||
|
||||
[JsonProperty("detailed_status")]
|
||||
public string DetailedStatus { get; set; }
|
||||
[JsonProperty("detailed_status")]
|
||||
public string DetailedStatus { get; set; }
|
||||
|
||||
[JsonProperty("timestamp")]
|
||||
public long Timestamp { get; set; }
|
||||
[JsonProperty("timestamp")]
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
[JsonProperty("analysis_time")]
|
||||
public double AnalysisTime { get; set; }
|
||||
[JsonProperty("analysis_time")]
|
||||
public double AnalysisTime { get; set; }
|
||||
|
||||
[JsonProperty("input_process")]
|
||||
public string InputProcess { get; set; }
|
||||
}
|
||||
}
|
||||
[JsonProperty("input_process")]
|
||||
public string InputProcess { get; set; }
|
||||
}
|
||||
}
|
@ -1,43 +1,43 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class AnalysisSection
|
||||
{
|
||||
[JsonProperty("start")]
|
||||
public double Start { get; set; }
|
||||
public class AnalysisSection
|
||||
{
|
||||
[JsonProperty("start")]
|
||||
public double Start { get; set; }
|
||||
|
||||
[JsonProperty("duration")]
|
||||
public double Duration { get; set; }
|
||||
[JsonProperty("duration")]
|
||||
public double Duration { get; set; }
|
||||
|
||||
[JsonProperty("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
[JsonProperty("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
|
||||
[JsonProperty("loudness")]
|
||||
public double Loudness { get; set; }
|
||||
[JsonProperty("loudness")]
|
||||
public double Loudness { get; set; }
|
||||
|
||||
[JsonProperty("tempo")]
|
||||
public double Tempo { get; set; }
|
||||
[JsonProperty("tempo")]
|
||||
public double Tempo { get; set; }
|
||||
|
||||
[JsonProperty("tempo_confidence")]
|
||||
public double TempoConfidence { get; set; }
|
||||
[JsonProperty("tempo_confidence")]
|
||||
public double TempoConfidence { get; set; }
|
||||
|
||||
[JsonProperty("key")]
|
||||
public int Key { get; set; }
|
||||
[JsonProperty("key")]
|
||||
public int Key { get; set; }
|
||||
|
||||
[JsonProperty("key_confidence")]
|
||||
public double KeyConfidence { get; set; }
|
||||
[JsonProperty("key_confidence")]
|
||||
public double KeyConfidence { get; set; }
|
||||
|
||||
[JsonProperty("mode")]
|
||||
public int Mode { get; set; }
|
||||
[JsonProperty("mode")]
|
||||
public int Mode { get; set; }
|
||||
|
||||
[JsonProperty("mode_confidence")]
|
||||
public double ModeConfidence { get; set; }
|
||||
[JsonProperty("mode_confidence")]
|
||||
public double ModeConfidence { get; set; }
|
||||
|
||||
[JsonProperty("time_signature")]
|
||||
public int TimeSignature { get; set; }
|
||||
[JsonProperty("time_signature")]
|
||||
public int TimeSignature { get; set; }
|
||||
|
||||
[JsonProperty("time_signature_confidence")]
|
||||
public double TimeSignatureConfidence { get; set; }
|
||||
}
|
||||
}
|
||||
[JsonProperty("time_signature_confidence")]
|
||||
public double TimeSignatureConfidence { get; set; }
|
||||
}
|
||||
}
|
@ -1,35 +1,35 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class AnalysisSegment
|
||||
{
|
||||
[JsonProperty("start")]
|
||||
public double Start { get; set; }
|
||||
public class AnalysisSegment
|
||||
{
|
||||
[JsonProperty("start")]
|
||||
public double Start { get; set; }
|
||||
|
||||
[JsonProperty("duration")]
|
||||
public double Duration { get; set; }
|
||||
[JsonProperty("duration")]
|
||||
public double Duration { get; set; }
|
||||
|
||||
[JsonProperty("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
[JsonProperty("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
|
||||
[JsonProperty("loudness_start")]
|
||||
public double LoudnessStart { get; set; }
|
||||
[JsonProperty("loudness_start")]
|
||||
public double LoudnessStart { get; set; }
|
||||
|
||||
[JsonProperty("loudness_max_time")]
|
||||
public double LoudnessMaxTime { get; set; }
|
||||
[JsonProperty("loudness_max_time")]
|
||||
public double LoudnessMaxTime { get; set; }
|
||||
|
||||
[JsonProperty("loudness_max")]
|
||||
public double LoudnessMax { get; set; }
|
||||
[JsonProperty("loudness_max")]
|
||||
public double LoudnessMax { get; set; }
|
||||
|
||||
[JsonProperty("loudness_end")]
|
||||
public double LoudnessEnd { get; set; }
|
||||
[JsonProperty("loudness_end")]
|
||||
public double LoudnessEnd { get; set; }
|
||||
|
||||
[JsonProperty("pitches")]
|
||||
public List<double> Pitches { get; set; }
|
||||
[JsonProperty("pitches")]
|
||||
public List<double> Pitches { get; set; }
|
||||
|
||||
[JsonProperty("timbre")]
|
||||
public List<double> Timbre { get; set; }
|
||||
}
|
||||
}
|
||||
[JsonProperty("timbre")]
|
||||
public List<double> Timbre { get; set; }
|
||||
}
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class AnalysisTimeSlice
|
||||
{
|
||||
[JsonProperty("start")]
|
||||
public double Start { get; set; }
|
||||
public class AnalysisTimeSlice
|
||||
{
|
||||
[JsonProperty("start")]
|
||||
public double Start { get; set; }
|
||||
|
||||
[JsonProperty("duration")]
|
||||
public double Duration { get; set; }
|
||||
[JsonProperty("duration")]
|
||||
public double Duration { get; set; }
|
||||
|
||||
[JsonProperty("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
}
|
||||
}
|
||||
[JsonProperty("confidence")]
|
||||
public double Confidence { get; set; }
|
||||
}
|
||||
}
|
@ -1,86 +1,86 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class AnalysisTrack
|
||||
{
|
||||
[JsonProperty("num_samples")]
|
||||
public int NumSamples { get; set; }
|
||||
public class AnalysisTrack
|
||||
{
|
||||
[JsonProperty("num_samples")]
|
||||
public int NumSamples { get; set; }
|
||||
|
||||
[JsonProperty("duration")]
|
||||
public double Duration { get; set; }
|
||||
[JsonProperty("duration")]
|
||||
public double Duration { get; set; }
|
||||
|
||||
[JsonProperty("sample_md5")]
|
||||
public string SampleMD5 { get; set; }
|
||||
[JsonProperty("sample_md5")]
|
||||
public string SampleMD5 { get; set; }
|
||||
|
||||
[JsonProperty("offset_seconds")]
|
||||
public double OffsetSeconds { get; set; }
|
||||
[JsonProperty("offset_seconds")]
|
||||
public double OffsetSeconds { get; set; }
|
||||
|
||||
[JsonProperty("window_seconds")]
|
||||
public double WindowSeconds { get; set; }
|
||||
[JsonProperty("window_seconds")]
|
||||
public double WindowSeconds { get; set; }
|
||||
|
||||
[JsonProperty("analysis_sample_rate")]
|
||||
public int AnalysisSampleRate { get; set; }
|
||||
[JsonProperty("analysis_sample_rate")]
|
||||
public int AnalysisSampleRate { get; set; }
|
||||
|
||||
[JsonProperty("analysis_channels")]
|
||||
public int AnalysisChannels { get; set; }
|
||||
[JsonProperty("analysis_channels")]
|
||||
public int AnalysisChannels { get; set; }
|
||||
|
||||
[JsonProperty("end_of_fade_in")]
|
||||
public double EndOfFadeIn { get; set; }
|
||||
[JsonProperty("end_of_fade_in")]
|
||||
public double EndOfFadeIn { get; set; }
|
||||
|
||||
[JsonProperty("start_of_fade_out")]
|
||||
public double StartOfFadeOut { get; set; }
|
||||
[JsonProperty("start_of_fade_out")]
|
||||
public double StartOfFadeOut { get; set; }
|
||||
|
||||
[JsonProperty("loudness")]
|
||||
public double Loudness { get; set; }
|
||||
[JsonProperty("loudness")]
|
||||
public double Loudness { get; set; }
|
||||
|
||||
[JsonProperty("tempo")]
|
||||
public double Tempo { get; set; }
|
||||
[JsonProperty("tempo")]
|
||||
public double Tempo { get; set; }
|
||||
|
||||
[JsonProperty("tempo_confidence")]
|
||||
public double TempoConfidence { get; set; }
|
||||
[JsonProperty("tempo_confidence")]
|
||||
public double TempoConfidence { get; set; }
|
||||
|
||||
[JsonProperty("time_signature")]
|
||||
public double TimeSignature { get; set; }
|
||||
[JsonProperty("time_signature")]
|
||||
public double TimeSignature { get; set; }
|
||||
|
||||
[JsonProperty("time_signature_confidence")]
|
||||
public double TimeSignatureConfidence { get; set; }
|
||||
[JsonProperty("time_signature_confidence")]
|
||||
public double TimeSignatureConfidence { get; set; }
|
||||
|
||||
[JsonProperty("key")]
|
||||
public int Key { get; set; }
|
||||
[JsonProperty("key")]
|
||||
public int Key { get; set; }
|
||||
|
||||
[JsonProperty("key_confidence")]
|
||||
public double KeyConfidence { get; set; }
|
||||
[JsonProperty("key_confidence")]
|
||||
public double KeyConfidence { get; set; }
|
||||
|
||||
[JsonProperty("mode")]
|
||||
public int Mode { get; set; }
|
||||
[JsonProperty("mode")]
|
||||
public int Mode { get; set; }
|
||||
|
||||
[JsonProperty("mode_confidence")]
|
||||
public double ModeConfidence { get; set; }
|
||||
[JsonProperty("mode_confidence")]
|
||||
public double ModeConfidence { get; set; }
|
||||
|
||||
[JsonProperty("codestring")]
|
||||
public string Codestring { get; set; }
|
||||
[JsonProperty("codestring")]
|
||||
public string Codestring { get; set; }
|
||||
|
||||
[JsonProperty("code_version")]
|
||||
public double CodeVersion { get; set; }
|
||||
[JsonProperty("code_version")]
|
||||
public double CodeVersion { get; set; }
|
||||
|
||||
[JsonProperty("echoprintstring")]
|
||||
public string Echoprintstring { get; set; }
|
||||
[JsonProperty("echoprintstring")]
|
||||
public string Echoprintstring { get; set; }
|
||||
|
||||
[JsonProperty("echoprint_version")]
|
||||
public double EchoprintVersion { get; set; }
|
||||
[JsonProperty("echoprint_version")]
|
||||
public double EchoprintVersion { get; set; }
|
||||
|
||||
[JsonProperty("synchstring")]
|
||||
public string Synchstring { get; set; }
|
||||
[JsonProperty("synchstring")]
|
||||
public string Synchstring { get; set; }
|
||||
|
||||
[JsonProperty("synch_version")]
|
||||
public double SynchVersion { get; set; }
|
||||
[JsonProperty("synch_version")]
|
||||
public double SynchVersion { get; set; }
|
||||
|
||||
[JsonProperty("rhythmstring")]
|
||||
public string Rhythmstring { get; set; }
|
||||
[JsonProperty("rhythmstring")]
|
||||
public string Rhythmstring { get; set; }
|
||||
|
||||
[JsonProperty("rhythm_version")]
|
||||
public double RhythmVersion { get; set; }
|
||||
[JsonProperty("rhythm_version")]
|
||||
public double RhythmVersion { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class ListResponse<T> : BasicModel
|
||||
{
|
||||
public List<T> List { get; set; }
|
||||
}
|
||||
public class ListResponse<T> : BasicModel
|
||||
{
|
||||
public List<T> List { get; set; }
|
||||
}
|
||||
}
|
@ -1,29 +1,29 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class AudioAnalysis : BasicModel
|
||||
{
|
||||
[JsonProperty("bars")]
|
||||
public List<AnalysisTimeSlice> Bars { get; set; }
|
||||
public class AudioAnalysis : BasicModel
|
||||
{
|
||||
[JsonProperty("bars")]
|
||||
public List<AnalysisTimeSlice> Bars { get; set; }
|
||||
|
||||
[JsonProperty("beats")]
|
||||
public List<AnalysisTimeSlice> Beats { get; set; }
|
||||
[JsonProperty("beats")]
|
||||
public List<AnalysisTimeSlice> Beats { get; set; }
|
||||
|
||||
[JsonProperty("meta")]
|
||||
public AnalysisMeta Meta { get; set; }
|
||||
[JsonProperty("meta")]
|
||||
public AnalysisMeta Meta { get; set; }
|
||||
|
||||
[JsonProperty("sections")]
|
||||
public List<AnalysisSection> Sections { get; set; }
|
||||
[JsonProperty("sections")]
|
||||
public List<AnalysisSection> Sections { get; set; }
|
||||
|
||||
[JsonProperty("segments")]
|
||||
public List<AnalysisSegment> Segments { get; set; }
|
||||
[JsonProperty("segments")]
|
||||
public List<AnalysisSegment> Segments { get; set; }
|
||||
|
||||
[JsonProperty("tatums")]
|
||||
public List<AnalysisTimeSlice> Tatums { get; set; }
|
||||
[JsonProperty("tatums")]
|
||||
public List<AnalysisTimeSlice> Tatums { get; set; }
|
||||
|
||||
[JsonProperty("track")]
|
||||
public AnalysisTrack Track { get; set; }
|
||||
}
|
||||
}
|
||||
[JsonProperty("track")]
|
||||
public AnalysisTrack Track { get; set; }
|
||||
}
|
||||
}
|
@ -1,61 +1,61 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class AudioFeatures : BasicModel
|
||||
{
|
||||
[JsonProperty("acousticness")]
|
||||
public float Acousticness { get; set; }
|
||||
public class AudioFeatures : BasicModel
|
||||
{
|
||||
[JsonProperty("acousticness")]
|
||||
public float Acousticness { get; set; }
|
||||
|
||||
[JsonProperty("analysis_url")]
|
||||
public string AnalysisUrl { get; set; }
|
||||
[JsonProperty("analysis_url")]
|
||||
public string AnalysisUrl { get; set; }
|
||||
|
||||
[JsonProperty("danceability")]
|
||||
public float Danceability { get; set; }
|
||||
[JsonProperty("danceability")]
|
||||
public float Danceability { get; set; }
|
||||
|
||||
[JsonProperty("duration_ms")]
|
||||
public int DurationMs { get; set; }
|
||||
[JsonProperty("duration_ms")]
|
||||
public int DurationMs { get; set; }
|
||||
|
||||
[JsonProperty("energy")]
|
||||
public float Energy { get; set; }
|
||||
[JsonProperty("energy")]
|
||||
public float Energy { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("instrumentalness")]
|
||||
public float Instrumentalness { get; set; }
|
||||
[JsonProperty("instrumentalness")]
|
||||
public float Instrumentalness { get; set; }
|
||||
|
||||
[JsonProperty("key")]
|
||||
public int Key { get; set; }
|
||||
[JsonProperty("key")]
|
||||
public int Key { get; set; }
|
||||
|
||||
[JsonProperty("liveness")]
|
||||
public float Liveness { get; set; }
|
||||
[JsonProperty("liveness")]
|
||||
public float Liveness { get; set; }
|
||||
|
||||
[JsonProperty("loudness")]
|
||||
public float Loudness { get; set; }
|
||||
[JsonProperty("loudness")]
|
||||
public float Loudness { get; set; }
|
||||
|
||||
[JsonProperty("mode")]
|
||||
public int Mode { get; set; }
|
||||
[JsonProperty("mode")]
|
||||
public int Mode { get; set; }
|
||||
|
||||
[JsonProperty("speechiness")]
|
||||
public float Speechiness { get; set; }
|
||||
[JsonProperty("speechiness")]
|
||||
public float Speechiness { get; set; }
|
||||
|
||||
[JsonProperty("tempo")]
|
||||
public float Tempo { get; set; }
|
||||
[JsonProperty("tempo")]
|
||||
public float Tempo { get; set; }
|
||||
|
||||
[JsonProperty("time_signature")]
|
||||
public int TimeSignature { get; set; }
|
||||
[JsonProperty("time_signature")]
|
||||
public int TimeSignature { get; set; }
|
||||
|
||||
[JsonProperty("track_href")]
|
||||
public string TrackHref { get; set; }
|
||||
[JsonProperty("track_href")]
|
||||
public string TrackHref { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
|
||||
[JsonProperty("valence")]
|
||||
public float Valence { get; set; }
|
||||
}
|
||||
[JsonProperty("valence")]
|
||||
public float Valence { get; set; }
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class AvailabeDevices : BasicModel
|
||||
{
|
||||
[JsonProperty("devices")]
|
||||
public List<Device> Devices { get; set; }
|
||||
}
|
||||
public class AvailabeDevices : BasicModel
|
||||
{
|
||||
[JsonProperty("devices")]
|
||||
public List<Device> Devices { get; set; }
|
||||
}
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public abstract class BasicModel
|
||||
{
|
||||
[JsonProperty("error")]
|
||||
public Error Error { get; set; }
|
||||
public abstract class BasicModel
|
||||
{
|
||||
[JsonProperty("error")]
|
||||
public Error Error { get; set; }
|
||||
|
||||
private ResponseInfo _info;
|
||||
private ResponseInfo _info;
|
||||
|
||||
public bool HasError() => Error != null;
|
||||
public bool HasError() => Error != null;
|
||||
|
||||
internal void AddResponseInfo(ResponseInfo info) => _info = info;
|
||||
internal void AddResponseInfo(ResponseInfo info) => _info = info;
|
||||
|
||||
public string Header(string key) => _info.Headers?.Get(key);
|
||||
public string Header(string key) => _info.Headers?.Get(key);
|
||||
|
||||
public WebHeaderCollection Headers() => _info.Headers;
|
||||
public WebHeaderCollection Headers() => _info.Headers;
|
||||
|
||||
public HttpStatusCode StatusCode() => _info.StatusCode;
|
||||
}
|
||||
public HttpStatusCode StatusCode() => _info.StatusCode;
|
||||
}
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class Category : BasicModel
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
public class Category : BasicModel
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("icons")]
|
||||
public List<Image> Icons { get; set; }
|
||||
[JsonProperty("icons")]
|
||||
public List<Image> Icons { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class CategoryList : BasicModel
|
||||
{
|
||||
[JsonProperty("categories")]
|
||||
public Paging<Category> Categories { get; set; }
|
||||
}
|
||||
public class CategoryList : BasicModel
|
||||
{
|
||||
[JsonProperty("categories")]
|
||||
public Paging<Category> Categories { get; set; }
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class CategoryPlaylist : BasicModel
|
||||
{
|
||||
[JsonProperty("playlists")]
|
||||
public Paging<SimplePlaylist> Playlists { get; set; }
|
||||
}
|
||||
public class CategoryPlaylist : BasicModel
|
||||
{
|
||||
[JsonProperty("playlists")]
|
||||
public Paging<SimplePlaylist> Playlists { get; set; }
|
||||
}
|
||||
}
|
@ -1,31 +1,31 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class CursorPaging<T> : BasicModel
|
||||
public class CursorPaging<T> : BasicModel
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("items")]
|
||||
public List<T> Items { get; set; }
|
||||
|
||||
[JsonProperty("limit")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
[JsonProperty("next")]
|
||||
public string Next { get; set; }
|
||||
|
||||
[JsonProperty("cursors")]
|
||||
public Cursor Cursors { get; set; }
|
||||
|
||||
[JsonProperty("total")]
|
||||
public int Total { get; set; }
|
||||
|
||||
public bool HasNext()
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("items")]
|
||||
public List<T> Items { get; set; }
|
||||
|
||||
[JsonProperty("limit")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
[JsonProperty("next")]
|
||||
public string Next { get; set; }
|
||||
|
||||
[JsonProperty("cursors")]
|
||||
public Cursor Cursors { get; set; }
|
||||
|
||||
[JsonProperty("total")]
|
||||
public int Total { get; set; }
|
||||
|
||||
public bool HasNext()
|
||||
{
|
||||
return !string.IsNullOrEmpty(Next);
|
||||
}
|
||||
return !string.IsNullOrEmpty(Next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +1,25 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class Device
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
public class Device
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("is_active")]
|
||||
public bool IsActive { get; set; }
|
||||
[JsonProperty("is_active")]
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
[JsonProperty("is_restricted")]
|
||||
public bool IsRestricted { get; set; }
|
||||
[JsonProperty("is_restricted")]
|
||||
public bool IsRestricted { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("volume_percent")]
|
||||
public int VolumePercent { get; set; }
|
||||
}
|
||||
[JsonProperty("volume_percent")]
|
||||
public int VolumePercent { get; set; }
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class FeaturedPlaylists : BasicModel
|
||||
{
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
public class FeaturedPlaylists : BasicModel
|
||||
{
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
[JsonProperty("playlists")]
|
||||
public Paging<SimplePlaylist> Playlists { get; set; }
|
||||
}
|
||||
[JsonProperty("playlists")]
|
||||
public Paging<SimplePlaylist> Playlists { get; set; }
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class FollowedArtists : BasicModel
|
||||
{
|
||||
[JsonProperty("artists")]
|
||||
public CursorPaging<FullArtist> Artists { get; set; }
|
||||
}
|
||||
public class FollowedArtists : BasicModel
|
||||
{
|
||||
[JsonProperty("artists")]
|
||||
public CursorPaging<FullArtist> Artists { get; set; }
|
||||
}
|
||||
}
|
@ -1,68 +1,68 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class FullAlbum : BasicModel
|
||||
{
|
||||
[JsonProperty("album_type")]
|
||||
public string AlbumType { get; set; }
|
||||
public class FullAlbum : BasicModel
|
||||
{
|
||||
[JsonProperty("album_type")]
|
||||
public string AlbumType { get; set; }
|
||||
|
||||
[JsonProperty("artists")]
|
||||
public List<SimpleArtist> Artists { get; set; }
|
||||
[JsonProperty("artists")]
|
||||
public List<SimpleArtist> Artists { get; set; }
|
||||
|
||||
[JsonProperty("available_markets")]
|
||||
public List<string> AvailableMarkets { get; set; }
|
||||
[JsonProperty("available_markets")]
|
||||
public List<string> AvailableMarkets { get; set; }
|
||||
|
||||
[JsonProperty("copyrights")]
|
||||
public List<Copyright> Copyrights { get; set; }
|
||||
[JsonProperty("copyrights")]
|
||||
public List<Copyright> Copyrights { get; set; }
|
||||
|
||||
[JsonProperty("external_ids")]
|
||||
public Dictionary<string, string> ExternalIds { get; set; }
|
||||
[JsonProperty("external_ids")]
|
||||
public Dictionary<string, string> ExternalIds { get; set; }
|
||||
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("genres")]
|
||||
public List<string> Genres { get; set; }
|
||||
[JsonProperty("genres")]
|
||||
public List<string> Genres { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
|
||||
[JsonProperty("label")]
|
||||
public string Label { get; set; }
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("label")]
|
||||
public string Label { get; set; }
|
||||
|
||||
[JsonProperty("popularity")]
|
||||
public int Popularity { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("release_date")]
|
||||
public string ReleaseDate { get; set; }
|
||||
[JsonProperty("popularity")]
|
||||
public int Popularity { get; set; }
|
||||
|
||||
[JsonProperty("release_date_precision")]
|
||||
public string ReleaseDatePrecision { get; set; }
|
||||
[JsonProperty("release_date")]
|
||||
public string ReleaseDate { get; set; }
|
||||
|
||||
[JsonProperty("tracks")]
|
||||
public Paging<SimpleTrack> Tracks { get; set; }
|
||||
[JsonProperty("release_date_precision")]
|
||||
public string ReleaseDatePrecision { get; set; }
|
||||
|
||||
[JsonProperty("restrictions")]
|
||||
public Dictionary<string, string> Restrictions { get; set; }
|
||||
[JsonProperty("tracks")]
|
||||
public Paging<SimpleTrack> Tracks { get; set; }
|
||||
|
||||
[JsonProperty("total_tracks")]
|
||||
public int TotalTracks { get; set; }
|
||||
[JsonProperty("restrictions")]
|
||||
public Dictionary<string, string> Restrictions { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("total_tracks")]
|
||||
public int TotalTracks { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
@ -1,38 +1,38 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class FullArtist : BasicModel
|
||||
{
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
public class FullArtist : BasicModel
|
||||
{
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("followers")]
|
||||
public Followers Followers { get; set; }
|
||||
[JsonProperty("followers")]
|
||||
public Followers Followers { get; set; }
|
||||
|
||||
[JsonProperty("genres")]
|
||||
public List<string> Genres { get; set; }
|
||||
[JsonProperty("genres")]
|
||||
public List<string> Genres { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("popularity")]
|
||||
public int Popularity { get; set; }
|
||||
[JsonProperty("popularity")]
|
||||
public int Popularity { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
@ -1,50 +1,50 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class FullPlaylist : BasicModel
|
||||
{
|
||||
[JsonProperty("collaborative")]
|
||||
public bool Collaborative { get; set; }
|
||||
public class FullPlaylist : BasicModel
|
||||
{
|
||||
[JsonProperty("collaborative")]
|
||||
public bool Collaborative { get; set; }
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("followers")]
|
||||
public Followers Followers { get; set; }
|
||||
[JsonProperty("followers")]
|
||||
public Followers Followers { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("owner")]
|
||||
public PublicProfile Owner { get; set; }
|
||||
[JsonProperty("owner")]
|
||||
public PublicProfile Owner { get; set; }
|
||||
|
||||
[JsonProperty("public")]
|
||||
public bool Public { get; set; }
|
||||
[JsonProperty("public")]
|
||||
public bool Public { get; set; }
|
||||
|
||||
[JsonProperty("snapshot_id")]
|
||||
public string SnapshotId { get; set; }
|
||||
[JsonProperty("snapshot_id")]
|
||||
public string SnapshotId { get; set; }
|
||||
|
||||
[JsonProperty("tracks")]
|
||||
public Paging<PlaylistTrack> Tracks { get; set; }
|
||||
[JsonProperty("tracks")]
|
||||
public Paging<PlaylistTrack> Tracks { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
@ -1,71 +1,71 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class FullTrack : BasicModel
|
||||
{
|
||||
[JsonProperty("album")]
|
||||
public SimpleAlbum Album { get; set; }
|
||||
public class FullTrack : BasicModel
|
||||
{
|
||||
[JsonProperty("album")]
|
||||
public SimpleAlbum Album { get; set; }
|
||||
|
||||
[JsonProperty("artists")]
|
||||
public List<SimpleArtist> Artists { get; set; }
|
||||
[JsonProperty("artists")]
|
||||
public List<SimpleArtist> Artists { get; set; }
|
||||
|
||||
[JsonProperty("available_markets")]
|
||||
public List<string> AvailableMarkets { get; set; }
|
||||
[JsonProperty("available_markets")]
|
||||
public List<string> AvailableMarkets { get; set; }
|
||||
|
||||
[JsonProperty("disc_number")]
|
||||
public int DiscNumber { get; set; }
|
||||
[JsonProperty("disc_number")]
|
||||
public int DiscNumber { get; set; }
|
||||
|
||||
[JsonProperty("duration_ms")]
|
||||
public int DurationMs { get; set; }
|
||||
[JsonProperty("duration_ms")]
|
||||
public int DurationMs { get; set; }
|
||||
|
||||
[JsonProperty("explicit")]
|
||||
public bool Explicit { get; set; }
|
||||
[JsonProperty("explicit")]
|
||||
public bool Explicit { get; set; }
|
||||
|
||||
[JsonProperty("external_ids")]
|
||||
public Dictionary<string, string> ExternalIds { get; set; }
|
||||
[JsonProperty("external_ids")]
|
||||
public Dictionary<string, string> ExternalIds { get; set; }
|
||||
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternUrls { get; set; }
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternUrls { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("popularity")]
|
||||
public int Popularity { get; set; }
|
||||
[JsonProperty("popularity")]
|
||||
public int Popularity { get; set; }
|
||||
|
||||
[JsonProperty("preview_url")]
|
||||
public string PreviewUrl { get; set; }
|
||||
[JsonProperty("preview_url")]
|
||||
public string PreviewUrl { get; set; }
|
||||
|
||||
[JsonProperty("track_number")]
|
||||
public int TrackNumber { get; set; }
|
||||
[JsonProperty("track_number")]
|
||||
public int TrackNumber { get; set; }
|
||||
|
||||
[JsonProperty("restrictions")]
|
||||
public Dictionary<string, string> Restrictions { get; set; }
|
||||
[JsonProperty("restrictions")]
|
||||
public Dictionary<string, string> Restrictions { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only filled when the "market"-parameter was supplied!
|
||||
/// </summary>
|
||||
[JsonProperty("is_playable")]
|
||||
public bool? IsPlayable { get; set; }
|
||||
/// <summary>
|
||||
/// Only filled when the "market"-parameter was supplied!
|
||||
/// </summary>
|
||||
[JsonProperty("is_playable")]
|
||||
public bool? IsPlayable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only filled when the "market"-parameter was supplied!
|
||||
/// </summary>
|
||||
[JsonProperty("linked_from")]
|
||||
public LinkedFrom LinkedFrom { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Only filled when the "market"-parameter was supplied!
|
||||
/// </summary>
|
||||
[JsonProperty("linked_from")]
|
||||
public LinkedFrom LinkedFrom { get; set; }
|
||||
}
|
||||
}
|
@ -1,159 +1,158 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class Image
|
||||
public class Image
|
||||
{
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; }
|
||||
|
||||
[JsonProperty("width")]
|
||||
public int Width { get; set; }
|
||||
|
||||
[JsonProperty("height")]
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
public class ErrorResponse : BasicModel
|
||||
{ }
|
||||
|
||||
public class Error
|
||||
{
|
||||
[JsonProperty("status")]
|
||||
public int Status { get; set; }
|
||||
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class PlaylistTrackCollection
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("total")]
|
||||
public int Total { get; set; }
|
||||
}
|
||||
|
||||
public class Followers
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("total")]
|
||||
public int Total { get; set; }
|
||||
}
|
||||
|
||||
public class PlaylistTrack
|
||||
{
|
||||
[JsonProperty("added_at")]
|
||||
public DateTime AddedAt { get; set; }
|
||||
|
||||
[JsonProperty("added_by")]
|
||||
public PublicProfile AddedBy { get; set; }
|
||||
|
||||
[JsonProperty("track")]
|
||||
public FullTrack Track { get; set; }
|
||||
|
||||
[JsonProperty("is_local")]
|
||||
public bool IsLocal { get; set; }
|
||||
}
|
||||
|
||||
public class DeleteTrackUri
|
||||
{
|
||||
/// <summary>
|
||||
/// Delete-Track Wrapper
|
||||
/// </summary>
|
||||
/// <param name="uri">An Spotify-URI</param>
|
||||
/// <param name="positions">Optional positions</param>
|
||||
public DeleteTrackUri(string uri, params int[] positions)
|
||||
{
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; }
|
||||
|
||||
[JsonProperty("width")]
|
||||
public int Width { get; set; }
|
||||
|
||||
[JsonProperty("height")]
|
||||
public int Height { get; set; }
|
||||
Positions = positions.ToList();
|
||||
Uri = uri;
|
||||
}
|
||||
|
||||
public class ErrorResponse : BasicModel
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
|
||||
[JsonProperty("positions")]
|
||||
public List<int> Positions { get; set; }
|
||||
|
||||
public bool ShouldSerializePositions()
|
||||
{
|
||||
return Positions.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class Error
|
||||
{
|
||||
[JsonProperty("status")]
|
||||
public int Status { get; set; }
|
||||
public class Copyright
|
||||
{
|
||||
[JsonProperty("text")]
|
||||
public string Text { get; set; }
|
||||
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
}
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
}
|
||||
|
||||
public class PlaylistTrackCollection
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
public class LinkedFrom
|
||||
{
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("total")]
|
||||
public int Total { get; set; }
|
||||
}
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
public class Followers
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("total")]
|
||||
public int Total { get; set; }
|
||||
}
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
public class PlaylistTrack
|
||||
{
|
||||
[JsonProperty("added_at")]
|
||||
public DateTime AddedAt { get; set; }
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
|
||||
[JsonProperty("added_by")]
|
||||
public PublicProfile AddedBy { get; set; }
|
||||
public class SavedTrack
|
||||
{
|
||||
[JsonProperty("added_at")]
|
||||
public DateTime AddedAt { get; set; }
|
||||
|
||||
[JsonProperty("track")]
|
||||
public FullTrack Track { get; set; }
|
||||
[JsonProperty("track")]
|
||||
public FullTrack Track { get; set; }
|
||||
}
|
||||
|
||||
[JsonProperty("is_local")]
|
||||
public bool IsLocal { get; set; }
|
||||
}
|
||||
public class SavedAlbum
|
||||
{
|
||||
[JsonProperty("added_at")]
|
||||
public DateTime AddedAt { get; set; }
|
||||
|
||||
public class DeleteTrackUri
|
||||
{
|
||||
/// <summary>
|
||||
/// Delete-Track Wrapper
|
||||
/// </summary>
|
||||
/// <param name="uri">An Spotify-URI</param>
|
||||
/// <param name="positions">Optional positions</param>
|
||||
public DeleteTrackUri(string uri, params int[] positions)
|
||||
{
|
||||
Positions = positions.ToList();
|
||||
Uri = uri;
|
||||
}
|
||||
[JsonProperty("album")]
|
||||
public FullAlbum Album { get; set; }
|
||||
}
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
public class Cursor
|
||||
{
|
||||
[JsonProperty("after")]
|
||||
public string After { get; set; }
|
||||
|
||||
[JsonProperty("positions")]
|
||||
public List<int> Positions { get; set; }
|
||||
[JsonProperty("before")]
|
||||
public string Before { get; set; }
|
||||
}
|
||||
|
||||
public bool ShouldSerializePositions()
|
||||
{
|
||||
return Positions.Count > 0;
|
||||
}
|
||||
}
|
||||
public class Context
|
||||
{
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
public class Copyright
|
||||
{
|
||||
[JsonProperty("text")]
|
||||
public string Text { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
}
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
public class LinkedFrom
|
||||
{
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
|
||||
public class SavedTrack
|
||||
{
|
||||
[JsonProperty("added_at")]
|
||||
public DateTime AddedAt { get; set; }
|
||||
|
||||
[JsonProperty("track")]
|
||||
public FullTrack Track { get; set; }
|
||||
}
|
||||
|
||||
public class SavedAlbum
|
||||
{
|
||||
[JsonProperty("added_at")]
|
||||
public DateTime AddedAt { get; set; }
|
||||
|
||||
[JsonProperty("album")]
|
||||
public FullAlbum Album { get; set; }
|
||||
}
|
||||
|
||||
public class Cursor
|
||||
{
|
||||
[JsonProperty("after")]
|
||||
public string After { get; set; }
|
||||
|
||||
[JsonProperty("before")]
|
||||
public string Before { get; set; }
|
||||
}
|
||||
|
||||
public class Context
|
||||
{
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class NewAlbumReleases : BasicModel
|
||||
{
|
||||
[JsonProperty("albums")]
|
||||
public Paging<SimpleAlbum> Albums { get; set; }
|
||||
}
|
||||
public class NewAlbumReleases : BasicModel
|
||||
{
|
||||
[JsonProperty("albums")]
|
||||
public Paging<SimpleAlbum> Albums { get; set; }
|
||||
}
|
||||
}
|
@ -1,39 +1,39 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class Paging<T> : BasicModel
|
||||
public class Paging<T> : BasicModel
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("items")]
|
||||
public List<T> Items { get; set; }
|
||||
|
||||
[JsonProperty("limit")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
[JsonProperty("next")]
|
||||
public string Next { get; set; }
|
||||
|
||||
[JsonProperty("offset")]
|
||||
public int Offset { get; set; }
|
||||
|
||||
[JsonProperty("previous")]
|
||||
public string Previous { get; set; }
|
||||
|
||||
[JsonProperty("total")]
|
||||
public int Total { get; set; }
|
||||
|
||||
public bool HasNextPage()
|
||||
{
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("items")]
|
||||
public List<T> Items { get; set; }
|
||||
|
||||
[JsonProperty("limit")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
[JsonProperty("next")]
|
||||
public string Next { get; set; }
|
||||
|
||||
[JsonProperty("offset")]
|
||||
public int Offset { get; set; }
|
||||
|
||||
[JsonProperty("previous")]
|
||||
public string Previous { get; set; }
|
||||
|
||||
[JsonProperty("total")]
|
||||
public int Total { get; set; }
|
||||
|
||||
public bool HasNextPage()
|
||||
{
|
||||
return Next != null;
|
||||
}
|
||||
|
||||
public bool HasPreviousPage()
|
||||
{
|
||||
return Previous != null;
|
||||
}
|
||||
return Next != null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasPreviousPage()
|
||||
{
|
||||
return Previous != null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
using System;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class PlayHistory : BasicModel
|
||||
{
|
||||
[JsonProperty("track")]
|
||||
public SimpleTrack Track { get; set; }
|
||||
public class PlayHistory : BasicModel
|
||||
{
|
||||
[JsonProperty("track")]
|
||||
public SimpleTrack Track { get; set; }
|
||||
|
||||
[JsonProperty("played_at")]
|
||||
public DateTime PlayedAt { get; set; }
|
||||
[JsonProperty("played_at")]
|
||||
public DateTime PlayedAt { get; set; }
|
||||
|
||||
[JsonProperty("context")]
|
||||
public Context Context { get; set; }
|
||||
}
|
||||
[JsonProperty("context")]
|
||||
public Context Context { get; set; }
|
||||
}
|
||||
}
|
@ -1,38 +1,38 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using SpotifyAPI.Web.Enums;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class PlaybackContext : BasicModel
|
||||
{
|
||||
[JsonProperty("device")]
|
||||
public Device Device { get; set; }
|
||||
public class PlaybackContext : BasicModel
|
||||
{
|
||||
[JsonProperty("device")]
|
||||
public Device Device { get; set; }
|
||||
|
||||
[JsonProperty("repeat_state")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public RepeatState RepeatState { get; set; }
|
||||
[JsonProperty("repeat_state")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public RepeatState RepeatState { get; set; }
|
||||
|
||||
[JsonProperty("shuffle_state")]
|
||||
public bool ShuffleState { get; set; }
|
||||
[JsonProperty("shuffle_state")]
|
||||
public bool ShuffleState { get; set; }
|
||||
|
||||
[JsonProperty("context")]
|
||||
public Context Context { get; set; }
|
||||
[JsonProperty("context")]
|
||||
public Context Context { get; set; }
|
||||
|
||||
[JsonProperty("timestamp")]
|
||||
public long Timestamp { get; set; }
|
||||
[JsonProperty("timestamp")]
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
[JsonProperty("progress_ms")]
|
||||
public int ProgressMs { get; set; }
|
||||
[JsonProperty("progress_ms")]
|
||||
public int ProgressMs { get; set; }
|
||||
|
||||
[JsonProperty("is_playing")]
|
||||
public bool IsPlaying { get; set; }
|
||||
[JsonProperty("is_playing")]
|
||||
public bool IsPlaying { get; set; }
|
||||
|
||||
[JsonProperty("item")]
|
||||
public FullTrack Item { get; set; }
|
||||
[JsonProperty("item")]
|
||||
public FullTrack Item { get; set; }
|
||||
|
||||
[JsonProperty("currently_playing_type")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public TrackType CurrentlyPlayingType { get; set; }
|
||||
}
|
||||
[JsonProperty("currently_playing_type")]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public TrackType CurrentlyPlayingType { get; set; }
|
||||
}
|
||||
}
|
@ -1,44 +1,44 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class PrivateProfile : BasicModel
|
||||
{
|
||||
[JsonProperty("birthdate")]
|
||||
public string Birthdate { get; set; }
|
||||
public class PrivateProfile : BasicModel
|
||||
{
|
||||
[JsonProperty("birthdate")]
|
||||
public string Birthdate { get; set; }
|
||||
|
||||
[JsonProperty("country")]
|
||||
public string Country { get; set; }
|
||||
[JsonProperty("country")]
|
||||
public string Country { get; set; }
|
||||
|
||||
[JsonProperty("display_name")]
|
||||
public string DisplayName { get; set; }
|
||||
[JsonProperty("display_name")]
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
[JsonProperty("email")]
|
||||
public string Email { get; set; }
|
||||
[JsonProperty("email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("followers")]
|
||||
public Followers Followers { get; set; }
|
||||
[JsonProperty("followers")]
|
||||
public Followers Followers { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
|
||||
[JsonProperty("product")]
|
||||
public string Product { get; set; }
|
||||
[JsonProperty("product")]
|
||||
public string Product { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
@ -1,32 +1,32 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class PublicProfile : BasicModel
|
||||
{
|
||||
[JsonProperty("display_name")]
|
||||
public string DisplayName { get; set; }
|
||||
public class PublicProfile : BasicModel
|
||||
{
|
||||
[JsonProperty("display_name")]
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("followers")]
|
||||
public Followers Followers { get; set; }
|
||||
[JsonProperty("followers")]
|
||||
public Followers Followers { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
@ -1,25 +1,25 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class RecommendationSeed
|
||||
{
|
||||
[JsonProperty("afterFilteringSize")]
|
||||
public int AfterFilteringSize { get; set; }
|
||||
public class RecommendationSeed
|
||||
{
|
||||
[JsonProperty("afterFilteringSize")]
|
||||
public int AfterFilteringSize { get; set; }
|
||||
|
||||
[JsonProperty("afterRelinkingSize")]
|
||||
public int AfterRelinkingSize { get; set; }
|
||||
[JsonProperty("afterRelinkingSize")]
|
||||
public int AfterRelinkingSize { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("initialPoolSize")]
|
||||
public int InitialPoolSize { get; set; }
|
||||
[JsonProperty("initialPoolSize")]
|
||||
public int InitialPoolSize { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
}
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class RecommendationSeedGenres : BasicModel
|
||||
{
|
||||
[JsonProperty("genres")]
|
||||
public List<string> Genres { get; set; }
|
||||
}
|
||||
public class RecommendationSeedGenres : BasicModel
|
||||
{
|
||||
[JsonProperty("genres")]
|
||||
public List<string> Genres { get; set; }
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class Recommendations : BasicModel
|
||||
{
|
||||
[JsonProperty("seeds")]
|
||||
public List<RecommendationSeed> Seeds { get; set; }
|
||||
public class Recommendations : BasicModel
|
||||
{
|
||||
[JsonProperty("seeds")]
|
||||
public List<RecommendationSeed> Seeds { get; set; }
|
||||
|
||||
[JsonProperty("tracks")]
|
||||
public List<SimpleTrack> Tracks { get; set; }
|
||||
}
|
||||
[JsonProperty("tracks")]
|
||||
public List<SimpleTrack> Tracks { get; set; }
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
using System.Net;
|
||||
using System.Net;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class ResponseInfo
|
||||
{
|
||||
public WebHeaderCollection Headers { get; set; }
|
||||
public class ResponseInfo
|
||||
{
|
||||
public WebHeaderCollection Headers { get; set; }
|
||||
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
|
||||
public static readonly ResponseInfo Empty = new ResponseInfo();
|
||||
}
|
||||
public static readonly ResponseInfo Empty = new ResponseInfo();
|
||||
}
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class SearchItem : BasicModel
|
||||
{
|
||||
[JsonProperty("artists")]
|
||||
public Paging<FullArtist> Artists { get; set; }
|
||||
public class SearchItem : BasicModel
|
||||
{
|
||||
[JsonProperty("artists")]
|
||||
public Paging<FullArtist> Artists { get; set; }
|
||||
|
||||
[JsonProperty("albums")]
|
||||
public Paging<SimpleAlbum> Albums { get; set; }
|
||||
[JsonProperty("albums")]
|
||||
public Paging<SimpleAlbum> Albums { get; set; }
|
||||
|
||||
[JsonProperty("tracks")]
|
||||
public Paging<FullTrack> Tracks { get; set; }
|
||||
[JsonProperty("tracks")]
|
||||
public Paging<FullTrack> Tracks { get; set; }
|
||||
|
||||
[JsonProperty("playlists")]
|
||||
public Paging<SimplePlaylist> Playlists { get; set; }
|
||||
}
|
||||
[JsonProperty("playlists")]
|
||||
public Paging<SimplePlaylist> Playlists { get; set; }
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class SeveralAlbums : BasicModel
|
||||
{
|
||||
[JsonProperty("albums")]
|
||||
public List<FullAlbum> Albums { get; set; }
|
||||
}
|
||||
public class SeveralAlbums : BasicModel
|
||||
{
|
||||
[JsonProperty("albums")]
|
||||
public List<FullAlbum> Albums { get; set; }
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class SeveralArtists : BasicModel
|
||||
{
|
||||
[JsonProperty("artists")]
|
||||
public List<FullArtist> Artists { get; set; }
|
||||
}
|
||||
public class SeveralArtists : BasicModel
|
||||
{
|
||||
[JsonProperty("artists")]
|
||||
public List<FullArtist> Artists { get; set; }
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class SeveralAudioFeatures : BasicModel
|
||||
{
|
||||
[JsonProperty("audio_features")]
|
||||
public List<AudioFeatures> AudioFeatures { get; set; }
|
||||
}
|
||||
public class SeveralAudioFeatures : BasicModel
|
||||
{
|
||||
[JsonProperty("audio_features")]
|
||||
public List<AudioFeatures> AudioFeatures { get; set; }
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class SeveralTracks : BasicModel
|
||||
{
|
||||
[JsonProperty("tracks")]
|
||||
public List<FullTrack> Tracks { get; set; }
|
||||
}
|
||||
public class SeveralTracks : BasicModel
|
||||
{
|
||||
[JsonProperty("tracks")]
|
||||
public List<FullTrack> Tracks { get; set; }
|
||||
}
|
||||
}
|
@ -1,53 +1,53 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class SimpleAlbum : BasicModel
|
||||
{
|
||||
[JsonProperty("album_group")]
|
||||
public string AlbumGroup { get; set; }
|
||||
public class SimpleAlbum : BasicModel
|
||||
{
|
||||
[JsonProperty("album_group")]
|
||||
public string AlbumGroup { get; set; }
|
||||
|
||||
[JsonProperty("album_type")]
|
||||
public string AlbumType { get; set; }
|
||||
[JsonProperty("album_type")]
|
||||
public string AlbumType { get; set; }
|
||||
|
||||
[JsonProperty("artists")]
|
||||
public List<SimpleArtist> Artists { get; set; }
|
||||
[JsonProperty("artists")]
|
||||
public List<SimpleArtist> Artists { get; set; }
|
||||
|
||||
[JsonProperty("available_markets")]
|
||||
public List<string> AvailableMarkets { get; set; }
|
||||
[JsonProperty("available_markets")]
|
||||
public List<string> AvailableMarkets { get; set; }
|
||||
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("release_date")]
|
||||
public string ReleaseDate { get; set; }
|
||||
[JsonProperty("release_date")]
|
||||
public string ReleaseDate { get; set; }
|
||||
|
||||
[JsonProperty("release_date_precision")]
|
||||
public string ReleaseDatePrecision { get; set; }
|
||||
[JsonProperty("release_date_precision")]
|
||||
public string ReleaseDatePrecision { get; set; }
|
||||
|
||||
[JsonProperty("restrictions")]
|
||||
public Dictionary<string, string> Restrictions { get; set; }
|
||||
[JsonProperty("restrictions")]
|
||||
public Dictionary<string, string> Restrictions { get; set; }
|
||||
|
||||
[JsonProperty("total_tracks")]
|
||||
public int TotalTracks { get; set; }
|
||||
[JsonProperty("total_tracks")]
|
||||
public int TotalTracks { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
@ -1,26 +1,26 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class SimpleArtist : BasicModel
|
||||
{
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
public class SimpleArtist : BasicModel
|
||||
{
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +1,44 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class SimplePlaylist : BasicModel
|
||||
{
|
||||
[JsonProperty("collaborative")]
|
||||
public bool Collaborative { get; set; }
|
||||
public class SimplePlaylist : BasicModel
|
||||
{
|
||||
[JsonProperty("collaborative")]
|
||||
public bool Collaborative { get; set; }
|
||||
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternalUrls { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
[JsonProperty("images")]
|
||||
public List<Image> Images { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("owner")]
|
||||
public PublicProfile Owner { get; set; }
|
||||
[JsonProperty("owner")]
|
||||
public PublicProfile Owner { get; set; }
|
||||
|
||||
[JsonProperty("public")]
|
||||
public bool Public { get; set; }
|
||||
[JsonProperty("public")]
|
||||
public bool Public { get; set; }
|
||||
|
||||
[JsonProperty("snapshot_id")]
|
||||
public string SnapshotId { get; set; }
|
||||
[JsonProperty("snapshot_id")]
|
||||
public string SnapshotId { get; set; }
|
||||
|
||||
[JsonProperty("tracks")]
|
||||
public PlaylistTrackCollection Tracks { get; set; }
|
||||
[JsonProperty("tracks")]
|
||||
public PlaylistTrackCollection Tracks { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
@ -1,50 +1,50 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class SimpleTrack : BasicModel
|
||||
{
|
||||
[JsonProperty("artists")]
|
||||
public List<SimpleArtist> Artists { get; set; }
|
||||
public class SimpleTrack : BasicModel
|
||||
{
|
||||
[JsonProperty("artists")]
|
||||
public List<SimpleArtist> Artists { get; set; }
|
||||
|
||||
[JsonProperty("available_markets")]
|
||||
public List<string> AvailableMarkets { get; set; }
|
||||
[JsonProperty("available_markets")]
|
||||
public List<string> AvailableMarkets { get; set; }
|
||||
|
||||
[JsonProperty("disc_number")]
|
||||
public int DiscNumber { get; set; }
|
||||
[JsonProperty("disc_number")]
|
||||
public int DiscNumber { get; set; }
|
||||
|
||||
[JsonProperty("duration_ms")]
|
||||
public int DurationMs { get; set; }
|
||||
[JsonProperty("duration_ms")]
|
||||
public int DurationMs { get; set; }
|
||||
|
||||
[JsonProperty("explicit")]
|
||||
public bool Explicit { get; set; }
|
||||
[JsonProperty("explicit")]
|
||||
public bool Explicit { get; set; }
|
||||
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternUrls { get; set; }
|
||||
[JsonProperty("external_urls")]
|
||||
public Dictionary<string, string> ExternUrls { get; set; }
|
||||
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
[JsonProperty("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("preview_url")]
|
||||
public string PreviewUrl { get; set; }
|
||||
[JsonProperty("preview_url")]
|
||||
public string PreviewUrl { get; set; }
|
||||
|
||||
[JsonProperty("track_number")]
|
||||
public int TrackNumber { get; set; }
|
||||
[JsonProperty("track_number")]
|
||||
public int TrackNumber { get; set; }
|
||||
|
||||
[JsonProperty("restrictions")]
|
||||
public Dictionary<string, string> Restrictions { get; set; }
|
||||
[JsonProperty("restrictions")]
|
||||
public Dictionary<string, string> Restrictions { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class Snapshot : BasicModel
|
||||
{
|
||||
[JsonProperty("snapshot_id")]
|
||||
public string SnapshotId { get; set; }
|
||||
}
|
||||
public class Snapshot : BasicModel
|
||||
{
|
||||
[JsonProperty("snapshot_id")]
|
||||
public string SnapshotId { get; set; }
|
||||
}
|
||||
}
|
@ -1,47 +1,47 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class Token
|
||||
public class Token
|
||||
{
|
||||
public Token()
|
||||
{
|
||||
public Token()
|
||||
{
|
||||
CreateDate = DateTime.Now;
|
||||
}
|
||||
|
||||
[JsonProperty("access_token")]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[JsonProperty("token_type")]
|
||||
public string TokenType { get; set; }
|
||||
|
||||
[JsonProperty("expires_in")]
|
||||
public double ExpiresIn { get; set; }
|
||||
|
||||
[JsonProperty("refresh_token")]
|
||||
public string RefreshToken { get; set; }
|
||||
|
||||
[JsonProperty("error")]
|
||||
public string Error { get; set; }
|
||||
|
||||
[JsonProperty("error_description")]
|
||||
public string ErrorDescription { get; set; }
|
||||
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the token has expired
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsExpired()
|
||||
{
|
||||
return CreateDate.Add(TimeSpan.FromSeconds(ExpiresIn)) <= DateTime.Now;
|
||||
}
|
||||
|
||||
public bool HasError()
|
||||
{
|
||||
return Error != null;
|
||||
}
|
||||
CreateDate = DateTime.Now;
|
||||
}
|
||||
|
||||
[JsonProperty("access_token")]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[JsonProperty("token_type")]
|
||||
public string TokenType { get; set; }
|
||||
|
||||
[JsonProperty("expires_in")]
|
||||
public double ExpiresIn { get; set; }
|
||||
|
||||
[JsonProperty("refresh_token")]
|
||||
public string RefreshToken { get; set; }
|
||||
|
||||
[JsonProperty("error")]
|
||||
public string Error { get; set; }
|
||||
|
||||
[JsonProperty("error_description")]
|
||||
public string ErrorDescription { get; set; }
|
||||
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the token has expired
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsExpired()
|
||||
{
|
||||
return CreateDate.Add(TimeSpan.FromSeconds(ExpiresIn)) <= DateTime.Now;
|
||||
}
|
||||
|
||||
public bool HasError()
|
||||
{
|
||||
return Error != null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,69 +1,69 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SpotifyAPI.Web.Models
|
||||
{
|
||||
public class TuneableTrack
|
||||
public class TuneableTrack
|
||||
{
|
||||
[String("acousticness")]
|
||||
public float? Acousticness { get; set; }
|
||||
|
||||
[String("danceability")]
|
||||
public float? Danceability { get; set; }
|
||||
|
||||
[String("duration_ms")]
|
||||
public int? DurationMs { get; set; }
|
||||
|
||||
[String("energy")]
|
||||
public float? Energy { get; set; }
|
||||
|
||||
[String("instrumentalness")]
|
||||
public float? Instrumentalness { get; set; }
|
||||
|
||||
[String("key")]
|
||||
public int? Key { get; set; }
|
||||
|
||||
[String("liveness")]
|
||||
public float? Liveness { get; set; }
|
||||
|
||||
[String("loudness")]
|
||||
public float? Loudness { get; set; }
|
||||
|
||||
[String("mode")]
|
||||
public int? Mode { get; set; }
|
||||
|
||||
[String("popularity")]
|
||||
public int? Popularity { get; set; }
|
||||
|
||||
[String("speechiness")]
|
||||
public float? Speechiness { get; set; }
|
||||
|
||||
[String("tempo")]
|
||||
public float? Tempo { get; set; }
|
||||
|
||||
[String("time_signature")]
|
||||
public int? TimeSignature { get; set; }
|
||||
|
||||
[String("valence")]
|
||||
public float? Valence { get; set; }
|
||||
|
||||
public string BuildUrlParams(string prefix)
|
||||
{
|
||||
[String("acousticness")]
|
||||
public float? Acousticness { get; set; }
|
||||
|
||||
[String("danceability")]
|
||||
public float? Danceability { get; set; }
|
||||
|
||||
[String("duration_ms")]
|
||||
public int? DurationMs { get; set; }
|
||||
|
||||
[String("energy")]
|
||||
public float? Energy { get; set; }
|
||||
|
||||
[String("instrumentalness")]
|
||||
public float? Instrumentalness { get; set; }
|
||||
|
||||
[String("key")]
|
||||
public int? Key { get; set; }
|
||||
|
||||
[String("liveness")]
|
||||
public float? Liveness { get; set; }
|
||||
|
||||
[String("loudness")]
|
||||
public float? Loudness { get; set; }
|
||||
|
||||
[String("mode")]
|
||||
public int? Mode { get; set; }
|
||||
|
||||
[String("popularity")]
|
||||
public int? Popularity { get; set; }
|
||||
|
||||
[String("speechiness")]
|
||||
public float? Speechiness { get; set; }
|
||||
|
||||
[String("tempo")]
|
||||
public float? Tempo { get; set; }
|
||||
|
||||
[String("time_signature")]
|
||||
public int? TimeSignature { get; set; }
|
||||
|
||||
[String("valence")]
|
||||
public float? Valence { get; set; }
|
||||
|
||||
public string BuildUrlParams(string prefix)
|
||||
{
|
||||
List<string> urlParams = new List<string>();
|
||||
foreach (PropertyInfo info in GetType().GetProperties())
|
||||
{
|
||||
object value = info.GetValue(this);
|
||||
string name = info.GetCustomAttribute<StringAttribute>()?.Text;
|
||||
if(name == null || value == null)
|
||||
continue;
|
||||
urlParams.Add(value is float valueAsFloat
|
||||
? $"{prefix}_{name}={valueAsFloat.ToString(CultureInfo.InvariantCulture)}"
|
||||
: $"{prefix}_{name}={value}");
|
||||
}
|
||||
if (urlParams.Count > 0)
|
||||
return "&" + string.Join("&", urlParams);
|
||||
return "";
|
||||
}
|
||||
List<string> urlParams = new List<string>();
|
||||
foreach (PropertyInfo info in GetType().GetProperties())
|
||||
{
|
||||
object value = info.GetValue(this);
|
||||
string name = info.GetCustomAttribute<StringAttribute>()?.Text;
|
||||
if (name == null || value == null)
|
||||
continue;
|
||||
urlParams.Add(value is float valueAsFloat ?
|
||||
$"{prefix}_{name}={valueAsFloat.ToString(CultureInfo.InvariantCulture)}" :
|
||||
$"{prefix}_{name}={value}");
|
||||
}
|
||||
if (urlParams.Count > 0)
|
||||
return "&" + string.Join("&", urlParams);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +1,97 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace SpotifyAPI.Web
|
||||
{
|
||||
public class ProxyConfig
|
||||
public class ProxyConfig
|
||||
{
|
||||
public string Host { get; set; }
|
||||
|
||||
public int Port { get; set; } = 80;
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to bypass the proxy server for local addresses.
|
||||
/// </summary>
|
||||
public bool BypassProxyOnLocal { get; set; }
|
||||
|
||||
public void Set(ProxyConfig proxyConfig)
|
||||
{
|
||||
public string Host { get; set; }
|
||||
|
||||
public int Port { get; set; } = 80;
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to bypass the proxy server for local addresses.
|
||||
/// </summary>
|
||||
public bool BypassProxyOnLocal { get; set; }
|
||||
|
||||
public void Set(ProxyConfig proxyConfig)
|
||||
{
|
||||
Host = proxyConfig?.Host;
|
||||
Port = proxyConfig?.Port ?? 80;
|
||||
Username = proxyConfig?.Username;
|
||||
Password = proxyConfig?.Password;
|
||||
BypassProxyOnLocal = proxyConfig?.BypassProxyOnLocal ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether both <see cref="Host"/> and <see cref="Port"/> have valid values.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(Host) && Port > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="Uri"/> from the host and port number
|
||||
/// </summary>
|
||||
/// <returns>A URI</returns>
|
||||
public Uri GetUri()
|
||||
{
|
||||
UriBuilder uriBuilder = new UriBuilder(Host)
|
||||
{
|
||||
Port = Port
|
||||
};
|
||||
return uriBuilder.Uri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="WebProxy"/> from the proxy details of this object.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="WebProxy"/> or <code>null</code> if the proxy details are invalid.</returns>
|
||||
public WebProxy CreateWebProxy()
|
||||
{
|
||||
if (!IsValid())
|
||||
return null;
|
||||
|
||||
WebProxy proxy = new WebProxy
|
||||
{
|
||||
Address = GetUri(),
|
||||
UseDefaultCredentials = true,
|
||||
BypassProxyOnLocal = BypassProxyOnLocal
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password))
|
||||
return proxy;
|
||||
|
||||
proxy.UseDefaultCredentials = false;
|
||||
proxy.Credentials = new NetworkCredential(Username, Password);
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
public static HttpClientHandler CreateClientHandler(ProxyConfig proxyConfig = null)
|
||||
{
|
||||
HttpClientHandler clientHandler = new HttpClientHandler
|
||||
{
|
||||
PreAuthenticate = false,
|
||||
UseDefaultCredentials = true,
|
||||
UseProxy = false
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(proxyConfig?.Host)) return clientHandler;
|
||||
WebProxy proxy = proxyConfig.CreateWebProxy();
|
||||
clientHandler.UseProxy = true;
|
||||
clientHandler.Proxy = proxy;
|
||||
clientHandler.UseDefaultCredentials = proxy.UseDefaultCredentials;
|
||||
clientHandler.PreAuthenticate = proxy.UseDefaultCredentials;
|
||||
|
||||
return clientHandler;
|
||||
}
|
||||
Host = proxyConfig?.Host;
|
||||
Port = proxyConfig?.Port ?? 80;
|
||||
Username = proxyConfig?.Username;
|
||||
Password = proxyConfig?.Password;
|
||||
BypassProxyOnLocal = proxyConfig?.BypassProxyOnLocal ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether both <see cref="Host"/> and <see cref="Port"/> have valid values.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(Host) && Port > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="Uri"/> from the host and port number
|
||||
/// </summary>
|
||||
/// <returns>A URI</returns>
|
||||
public Uri GetUri()
|
||||
{
|
||||
UriBuilder uriBuilder = new UriBuilder(Host)
|
||||
{
|
||||
Port = Port
|
||||
};
|
||||
return uriBuilder.Uri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="WebProxy"/> from the proxy details of this object.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="WebProxy"/> or <code>null</code> if the proxy details are invalid.</returns>
|
||||
public WebProxy CreateWebProxy()
|
||||
{
|
||||
if (!IsValid())
|
||||
return null;
|
||||
|
||||
WebProxy proxy = new WebProxy
|
||||
{
|
||||
Address = GetUri(),
|
||||
UseDefaultCredentials = true,
|
||||
BypassProxyOnLocal = BypassProxyOnLocal
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password))
|
||||
return proxy;
|
||||
|
||||
proxy.UseDefaultCredentials = false;
|
||||
proxy.Credentials = new NetworkCredential(Username, Password);
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
public static HttpClientHandler CreateClientHandler(ProxyConfig proxyConfig = null)
|
||||
{
|
||||
HttpClientHandler clientHandler = new HttpClientHandler
|
||||
{
|
||||
PreAuthenticate = false,
|
||||
UseDefaultCredentials = true,
|
||||
UseProxy = false
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(proxyConfig?.Host)) return clientHandler;
|
||||
WebProxy proxy = proxyConfig.CreateWebProxy();
|
||||
clientHandler.UseProxy = true;
|
||||
clientHandler.Proxy = proxy;
|
||||
clientHandler.UseDefaultCredentials = proxy.UseDefaultCredentials;
|
||||
clientHandler.PreAuthenticate = proxy.UseDefaultCredentials;
|
||||
|
||||
return clientHandler;
|
||||
}
|
||||
}
|
||||
}
|
@ -2938,4 +2938,4 @@ namespace SpotifyAPI.Web
|
||||
|
||||
#endregion Util
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -1132,4 +1132,4 @@ namespace SpotifyAPI.Web
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
@ -6,199 +5,201 @@ using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using SpotifyAPI.Web.Models;
|
||||
|
||||
namespace SpotifyAPI.Web
|
||||
{
|
||||
internal class SpotifyWebClient : IClient
|
||||
internal class SpotifyWebClient : IClient
|
||||
{
|
||||
public JsonSerializerSettings JsonSettings { get; set; }
|
||||
private readonly Encoding _encoding = Encoding.UTF8;
|
||||
private readonly HttpClient _client;
|
||||
|
||||
private const string UnknownErrorJson = "{\"error\": { \"status\": 0, \"message\": \"SpotifyAPI.Web - Unkown Spotify Error\" }}";
|
||||
|
||||
public SpotifyWebClient(ProxyConfig proxyConfig = null)
|
||||
{
|
||||
public JsonSerializerSettings JsonSettings { get; set; }
|
||||
private readonly Encoding _encoding = Encoding.UTF8;
|
||||
private readonly HttpClient _client;
|
||||
|
||||
private const string UnknownErrorJson = "{\"error\": { \"status\": 0, \"message\": \"SpotifyAPI.Web - Unkown Spotify Error\" }}";
|
||||
|
||||
public SpotifyWebClient(ProxyConfig proxyConfig = null)
|
||||
{
|
||||
HttpClientHandler clientHandler = ProxyConfig.CreateClientHandler(proxyConfig);
|
||||
_client = new HttpClient(clientHandler);
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, string> Download(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, byte[]> raw = DownloadRaw(url, headers);
|
||||
return new Tuple<ResponseInfo, string>(raw.Item1, raw.Item2.Length > 0 ? _encoding.GetString(raw.Item2) : "{}");
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, string>> DownloadAsync(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, byte[]> raw = await DownloadRawAsync(url, headers).ConfigureAwait(false);
|
||||
return new Tuple<ResponseInfo, string>(raw.Item1, raw.Item2.Length > 0 ? _encoding.GetString(raw.Item2) : "{}");
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, byte[]> DownloadRaw(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
AddHeaders(headers);
|
||||
}
|
||||
using (HttpResponseMessage response = Task.Run(() => _client.GetAsync(url)).Result)
|
||||
{
|
||||
return new Tuple<ResponseInfo, byte[]>(new ResponseInfo
|
||||
{
|
||||
StatusCode = response.StatusCode,
|
||||
Headers = ConvertHeaders(response.Headers)
|
||||
}, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, byte[]>> DownloadRawAsync(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
AddHeaders(headers);
|
||||
}
|
||||
using (HttpResponseMessage response = await _client.GetAsync(url).ConfigureAwait(false))
|
||||
{
|
||||
return new Tuple<ResponseInfo, byte[]>(new ResponseInfo
|
||||
{
|
||||
StatusCode = response.StatusCode,
|
||||
Headers = ConvertHeaders(response.Headers)
|
||||
}, await response.Content.ReadAsByteArrayAsync());
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, T> DownloadJson<T>(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, string> response = Download(url, headers);
|
||||
try
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(response.Item2, JsonSettings));
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(UnknownErrorJson, JsonSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, T>> DownloadJsonAsync<T>(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, string> response = await DownloadAsync(url, headers).ConfigureAwait(false);try
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(response.Item2, JsonSettings));
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(UnknownErrorJson, JsonSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, string> Upload(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, byte[]> data = UploadRaw(url, body, method, headers);
|
||||
return new Tuple<ResponseInfo, string>(data.Item1, data.Item2.Length > 0 ? _encoding.GetString(data.Item2) : "{}");
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, string>> UploadAsync(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, byte[]> data = await UploadRawAsync(url, body, method, headers).ConfigureAwait(false);
|
||||
return new Tuple<ResponseInfo, string>(data.Item1, data.Item2.Length > 0 ? _encoding.GetString(data.Item2) : "{}");
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, byte[]> UploadRaw(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
AddHeaders(headers);
|
||||
}
|
||||
|
||||
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<ResponseInfo, byte[]>(new ResponseInfo
|
||||
{
|
||||
StatusCode = response.StatusCode,
|
||||
Headers = ConvertHeaders(response.Headers)
|
||||
}, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, byte[]>> UploadRawAsync(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
AddHeaders(headers);
|
||||
}
|
||||
|
||||
HttpRequestMessage message = new HttpRequestMessage(new HttpMethod(method), url)
|
||||
{
|
||||
Content = new StringContent(body, _encoding)
|
||||
};
|
||||
using (HttpResponseMessage response = await _client.SendAsync(message))
|
||||
{
|
||||
return new Tuple<ResponseInfo, byte[]>(new ResponseInfo
|
||||
{
|
||||
StatusCode = response.StatusCode,
|
||||
Headers = ConvertHeaders(response.Headers)
|
||||
}, await response.Content.ReadAsByteArrayAsync());
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, T> UploadJson<T>(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, string> response = Upload(url, body, method, headers);
|
||||
try
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(response.Item2, JsonSettings));
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(UnknownErrorJson, JsonSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, T>> UploadJsonAsync<T>(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, string> response = await UploadAsync(url, body, method, headers).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(response.Item2, JsonSettings));
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(UnknownErrorJson, JsonSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private static WebHeaderCollection ConvertHeaders(HttpResponseHeaders headers)
|
||||
{
|
||||
WebHeaderCollection newHeaders = new WebHeaderCollection();
|
||||
foreach (KeyValuePair<string, IEnumerable<string>> headerPair in headers)
|
||||
{
|
||||
foreach (string headerValue in headerPair.Value)
|
||||
{
|
||||
newHeaders.Add(headerPair.Key, headerValue);
|
||||
}
|
||||
}
|
||||
return newHeaders;
|
||||
}
|
||||
|
||||
private void AddHeaders(Dictionary<string,string> headers)
|
||||
{
|
||||
_client.DefaultRequestHeaders.Clear();
|
||||
foreach (KeyValuePair<string, string> headerPair in headers)
|
||||
{
|
||||
_client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
|
||||
}
|
||||
}
|
||||
HttpClientHandler clientHandler = ProxyConfig.CreateClientHandler(proxyConfig);
|
||||
_client = new HttpClient(clientHandler);
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, string> Download(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, byte[]> raw = DownloadRaw(url, headers);
|
||||
return new Tuple<ResponseInfo, string>(raw.Item1, raw.Item2.Length > 0 ? _encoding.GetString(raw.Item2) : "{}");
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, string>> DownloadAsync(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, byte[]> raw = await DownloadRawAsync(url, headers).ConfigureAwait(false);
|
||||
return new Tuple<ResponseInfo, string>(raw.Item1, raw.Item2.Length > 0 ? _encoding.GetString(raw.Item2) : "{}");
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, byte[]> DownloadRaw(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
AddHeaders(headers);
|
||||
}
|
||||
using(HttpResponseMessage response = Task.Run(() => _client.GetAsync(url)).Result)
|
||||
{
|
||||
return new Tuple<ResponseInfo, byte[]>(new ResponseInfo
|
||||
{
|
||||
StatusCode = response.StatusCode,
|
||||
Headers = ConvertHeaders(response.Headers)
|
||||
}, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, byte[]>> DownloadRawAsync(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
AddHeaders(headers);
|
||||
}
|
||||
using(HttpResponseMessage response = await _client.GetAsync(url).ConfigureAwait(false))
|
||||
{
|
||||
return new Tuple<ResponseInfo, byte[]>(new ResponseInfo
|
||||
{
|
||||
StatusCode = response.StatusCode,
|
||||
Headers = ConvertHeaders(response.Headers)
|
||||
}, await response.Content.ReadAsByteArrayAsync());
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, T> DownloadJson<T>(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, string> response = Download(url, headers);
|
||||
try
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(response.Item2, JsonSettings));
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(UnknownErrorJson, JsonSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, T>> DownloadJsonAsync<T>(string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, string> response = await DownloadAsync(url, headers).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(response.Item2, JsonSettings));
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(UnknownErrorJson, JsonSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, string> Upload(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, byte[]> data = UploadRaw(url, body, method, headers);
|
||||
return new Tuple<ResponseInfo, string>(data.Item1, data.Item2.Length > 0 ? _encoding.GetString(data.Item2) : "{}");
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, string>> UploadAsync(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, byte[]> data = await UploadRawAsync(url, body, method, headers).ConfigureAwait(false);
|
||||
return new Tuple<ResponseInfo, string>(data.Item1, data.Item2.Length > 0 ? _encoding.GetString(data.Item2) : "{}");
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, byte[]> UploadRaw(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
AddHeaders(headers);
|
||||
}
|
||||
|
||||
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<ResponseInfo, byte[]>(new ResponseInfo
|
||||
{
|
||||
StatusCode = response.StatusCode,
|
||||
Headers = ConvertHeaders(response.Headers)
|
||||
}, Task.Run(() => response.Content.ReadAsByteArrayAsync()).Result);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, byte[]>> UploadRawAsync(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
AddHeaders(headers);
|
||||
}
|
||||
|
||||
HttpRequestMessage message = new HttpRequestMessage(new HttpMethod(method), url)
|
||||
{
|
||||
Content = new StringContent(body, _encoding)
|
||||
};
|
||||
using(HttpResponseMessage response = await _client.SendAsync(message))
|
||||
{
|
||||
return new Tuple<ResponseInfo, byte[]>(new ResponseInfo
|
||||
{
|
||||
StatusCode = response.StatusCode,
|
||||
Headers = ConvertHeaders(response.Headers)
|
||||
}, await response.Content.ReadAsByteArrayAsync());
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<ResponseInfo, T> UploadJson<T>(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, string> response = Upload(url, body, method, headers);
|
||||
try
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(response.Item2, JsonSettings));
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(UnknownErrorJson, JsonSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<ResponseInfo, T>> UploadJsonAsync<T>(string url, string body, string method, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Tuple<ResponseInfo, string> response = await UploadAsync(url, body, method, headers).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(response.Item2, JsonSettings));
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return new Tuple<ResponseInfo, T>(response.Item1, JsonConvert.DeserializeObject<T>(UnknownErrorJson, JsonSettings));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private static WebHeaderCollection ConvertHeaders(HttpResponseHeaders headers)
|
||||
{
|
||||
WebHeaderCollection newHeaders = new WebHeaderCollection();
|
||||
foreach (KeyValuePair<string, IEnumerable<string>> headerPair in headers)
|
||||
{
|
||||
foreach (string headerValue in headerPair.Value)
|
||||
{
|
||||
newHeaders.Add(headerPair.Key, headerValue);
|
||||
}
|
||||
}
|
||||
return newHeaders;
|
||||
}
|
||||
|
||||
private void AddHeaders(Dictionary<string, string> headers)
|
||||
{
|
||||
_client.DefaultRequestHeaders.Clear();
|
||||
foreach (KeyValuePair<string, string> headerPair in headers)
|
||||
{
|
||||
_client.DefaultRequestHeaders.TryAddWithoutValidation(headerPair.Key, headerPair.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +1,41 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace SpotifyAPI.Web
|
||||
{
|
||||
public static class Util
|
||||
public static class Util
|
||||
{
|
||||
public static string GetStringAttribute<T>(this T en, string separator = "") where T : struct, IConvertible
|
||||
{
|
||||
public static string GetStringAttribute<T>(this T en, string separator = "") where T : struct, IConvertible
|
||||
{
|
||||
Enum e = (Enum)(object)en;
|
||||
IEnumerable<StringAttribute> attributes =
|
||||
Enum.GetValues(typeof(T))
|
||||
.Cast<T>()
|
||||
.Where(v => e.HasFlag((Enum)(object)v))
|
||||
.Select(v => typeof(T).GetField(v.ToString(CultureInfo.InvariantCulture)))
|
||||
.Select(f => f.GetCustomAttributes(typeof(StringAttribute), false)[0])
|
||||
.Cast<StringAttribute>();
|
||||
Enum e = (Enum) (object) en;
|
||||
IEnumerable<StringAttribute> attributes =
|
||||
Enum.GetValues(typeof(T))
|
||||
.Cast<T>()
|
||||
.Where(v => e.HasFlag((Enum) (object) v))
|
||||
.Select(v => typeof(T).GetField(v.ToString(CultureInfo.InvariantCulture)))
|
||||
.Select(f => f.GetCustomAttributes(typeof(StringAttribute), false) [0])
|
||||
.Cast<StringAttribute>();
|
||||
|
||||
List<string> list = new List<string>();
|
||||
attributes.ToList().ForEach(element => list.Add(element.Text));
|
||||
return string.Join(separator, list);
|
||||
}
|
||||
|
||||
public static long ToUnixTimeMillisecondsPoly(this DateTime time)
|
||||
{
|
||||
return (long)time.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
|
||||
}
|
||||
List<string> list = new List<string>();
|
||||
attributes.ToList().ForEach(element => list.Add(element.Text));
|
||||
return string.Join(separator, list);
|
||||
}
|
||||
|
||||
public sealed class StringAttribute : Attribute
|
||||
public static long ToUnixTimeMillisecondsPoly(this DateTime time)
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
public StringAttribute(string text)
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
return (long) time.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class StringAttribute : Attribute
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
public StringAttribute(string text)
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user