Finished shows, started playlists

This commit is contained in:
Jonas Dellinger 2020-05-02 22:48:21 +02:00
parent be7bdb6a93
commit 870b13eb2d
26 changed files with 337 additions and 28 deletions

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace SpotifyAPI.Web
{
public interface IPlaylistsClient
{
Task<SnapshotResponse> RemoveItems(string playlistId, PlaylistRemoveItemsRequest request);
}
}

View File

@ -0,0 +1,15 @@
using System.Threading.Tasks;
namespace SpotifyAPI.Web
{
public interface IShowsClient
{
Task<FullShow> Get(string showId);
Task<FullShow> Get(string showId, ShowRequest request);
Task<ShowsResponse> GetSeveral(ShowsRequest request);
Task<Paging<SimpleEpisode>> GetEpisodes(string showId);
Task<Paging<SimpleEpisode>> GetEpisodes(string showId, ShowEpisodesRequest request);
}
}

View File

@ -5,5 +5,9 @@ namespace SpotifyAPI.Web
IUserProfileClient UserProfile { get; }
IBrowseClient Browse { get; }
IShowsClient Shows { get; }
IPlaylistsClient Playlists { get; }
}
}

View File

@ -0,0 +1,19 @@
using System.Threading.Tasks;
using SpotifyAPI.Web.Http;
using URLs = SpotifyAPI.Web.SpotifyUrls;
namespace SpotifyAPI.Web
{
public class PlaylistsClient : APIClient, IPlaylistsClient
{
public PlaylistsClient(IAPIConnector connector) : base(connector) { }
public Task<SnapshotResponse> RemoveItems(string playlistId, PlaylistRemoveItemsRequest request)
{
Ensure.ArgumentNotNullOrEmptyString(playlistId, nameof(playlistId));
Ensure.ArgumentNotNull(request, nameof(request));
return API.Delete<SnapshotResponse>(URLs.PlaylistTracks(playlistId), null, request.BuildBodyParams());
}
}
}

View File

@ -0,0 +1,48 @@
using System.Threading.Tasks;
using SpotifyAPI.Web.Http;
using URLs = SpotifyAPI.Web.SpotifyUrls;
namespace SpotifyAPI.Web
{
public class ShowsClient : APIClient, IShowsClient
{
public ShowsClient(IAPIConnector connector) : base(connector) { }
public Task<FullShow> Get(string showId)
{
Ensure.ArgumentNotNullOrEmptyString(showId, nameof(showId));
return API.Get<FullShow>(URLs.Show(showId));
}
public Task<FullShow> Get(string showId, ShowRequest request)
{
Ensure.ArgumentNotNullOrEmptyString(showId, nameof(showId));
Ensure.ArgumentNotNull(request, nameof(request));
return API.Get<FullShow>(URLs.Show(showId), request.BuildQueryParams());
}
public Task<ShowsResponse> GetSeveral(ShowsRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
return API.Get<ShowsResponse>(URLs.Shows(), request.BuildQueryParams());
}
public Task<Paging<SimpleEpisode>> GetEpisodes(string showId)
{
Ensure.ArgumentNotNullOrEmptyString(showId, nameof(showId));
return API.Get<Paging<SimpleEpisode>>(URLs.ShowEpisodes(showId));
}
public Task<Paging<SimpleEpisode>> GetEpisodes(string showId, ShowEpisodesRequest request)
{
Ensure.ArgumentNotNullOrEmptyString(showId, nameof(showId));
Ensure.ArgumentNotNull(request, nameof(request));
return API.Get<Paging<SimpleEpisode>>(URLs.ShowEpisodes(showId), request.BuildQueryParams());
}
}
}

View File

@ -17,10 +17,16 @@ namespace SpotifyAPI.Web
_apiConnector = config.CreateAPIConnector();
UserProfile = new UserProfileClient(_apiConnector);
Browse = new BrowseClient(_apiConnector);
Shows = new ShowsClient(_apiConnector);
Playlists = new PlaylistsClient(_apiConnector);
}
public IUserProfileClient UserProfile { get; }
public IBrowseClient Browse { get; }
public IShowsClient Shows { get; }
public IPlaylistsClient Playlists { get; }
}
}

View File

@ -38,11 +38,11 @@ namespace SpotifyAPI.Web
internal IAPIConnector CreateAPIConnector()
{
Ensure.PropertyNotNull(BaseAddress, nameof(BaseAddress));
Ensure.PropertyNotNull(Authenticator, nameof(Authenticator),
Ensure.ArgumentNotNull(BaseAddress, nameof(BaseAddress));
Ensure.ArgumentNotNull(Authenticator, nameof(Authenticator),
". Use WithToken or WithAuthenticator to specify a authentication");
Ensure.PropertyNotNull(JSONSerializer, nameof(JSONSerializer));
Ensure.PropertyNotNull(HTTPClient, nameof(HTTPClient));
Ensure.ArgumentNotNull(JSONSerializer, nameof(JSONSerializer));
Ensure.ArgumentNotNull(HTTPClient, nameof(HTTPClient));
return new APIConnector(BaseAddress, Authenticator, JSONSerializer, HTTPClient, RetryHandler);
}

View File

@ -135,11 +135,14 @@ namespace SpotifyAPI.Web.Http
_jsonSerializer.SerializeRequest(request);
await _authenticator.Apply(request).ConfigureAwait(false);
IResponse response = await _httpClient.DoRequest(request).ConfigureAwait(false);
if (_retryHandler != null)
{
response = await _retryHandler?.HandleRetry(request, response, async (newRequest) =>
{
await _authenticator.Apply(newRequest).ConfigureAwait(false);
return await _httpClient.DoRequest(request).ConfigureAwait(false);
});
}
ProcessErrors(response);
IAPIResponse<T> apiResponse = _jsonSerializer.DeserializeResponse<T>(response);

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace SpotifyAPI.Web
{
public class PlaylistRemoveItemsRequest : RequestParams
{
[BodyParam("tracks")]
public List<Item> Tracks { get; set; }
[BodyParam("snapshot_id")]
public string SnapshotId { get; set; }
protected override void CustomEnsure()
{
Ensure.ArgumentNotNullOrEmptyList(Tracks, nameof(Tracks));
}
public class Item
{
public Item(string uri)
{
Uri = uri;
}
[JsonProperty("uri")]
public string Uri { get; set; }
}
}
}

View File

@ -31,7 +31,7 @@ namespace SpotifyAPI.Web
public Dictionary<string, string> Max { get; set; }
public Dictionary<string, string> Target { get; set; }
protected override void Ensure()
protected override void CustomEnsure()
{
if (string.IsNullOrEmpty(SeedTracks) && string.IsNullOrEmpty(SeedGenres) && string.IsNullOrEmpty(SeedArtists))
{

View File

@ -2,14 +2,38 @@ using System.Reflection;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections;
using Newtonsoft.Json.Linq;
namespace SpotifyAPI.Web
{
public abstract class RequestParams
{
public JObject BuildBodyParams()
{
// Make sure everything is okay before building query params
CustomEnsure();
var bodyProps = GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Where(prop => prop.GetCustomAttributes(typeof(BodyParamAttribute), true).Length > 0);
var obj = new JObject();
foreach (var prop in bodyProps)
{
var attribute = prop.GetCustomAttribute(typeof(BodyParamAttribute)) as BodyParamAttribute;
object value = prop.GetValue(this);
if (value != null)
{
obj[attribute.Key ?? prop.Name] = JToken.FromObject(value);
}
}
return obj;
}
public Dictionary<string, string> BuildQueryParams()
{
// Make sure everything is okay before building query params
Ensure();
CustomEnsure();
var queryProps = GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Where(prop => prop.GetCustomAttributes(typeof(QueryParamAttribute), true).Length > 0);
@ -20,17 +44,26 @@ namespace SpotifyAPI.Web
var attribute = prop.GetCustomAttribute(typeof(QueryParamAttribute)) as QueryParamAttribute;
object value = prop.GetValue(this);
if (value != null)
{
if (value is List<string>)
{
List<string> list = value as List<string>;
var str = string.Join(",", list);
queryParams.Add(attribute.Key ?? prop.Name, str);
}
else
{
queryParams.Add(attribute.Key ?? prop.Name, value.ToString());
}
}
}
AddCustomQueryParams(queryParams);
return queryParams;
}
protected virtual void Ensure() { }
protected virtual void CustomEnsure() { }
protected virtual void AddCustomQueryParams(Dictionary<string, string> queryParams) { }
}
@ -47,6 +80,12 @@ namespace SpotifyAPI.Web
public class BodyParamAttribute : Attribute
{
public string Key { get; }
public BodyParamAttribute() { }
public BodyParamAttribute(string key)
{
Key = key;
}
}
}

View File

@ -0,0 +1,14 @@
namespace SpotifyAPI.Web
{
public class ShowEpisodesRequest : RequestParams
{
[QueryParam("limit")]
public int? Limit { get; set; }
[QueryParam("offset")]
public int? Offset { get; set; }
[QueryParam("market")]
public string Market { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace SpotifyAPI.Web
{
public class ShowRequest : RequestParams
{
[QueryParam("market")]
public string Market { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace SpotifyAPI.Web
{
public class ShowsRequest : RequestParams
{
[QueryParam("ids")]
public List<string> Ids { get; set; }
[QueryParam("market")]
public string Market { get; set; }
protected override void CustomEnsure()
{
Ensure.ArgumentNotNullOrEmptyList(Ids, nameof(Ids));
}
}
}

View File

@ -1,7 +1,10 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace SpotifyAPI.Web
{
public class FullEpisode : IPlaylistElement
public class FullEpisode : IPlaylistItem
{
public string AudioPreviewUrl { get; set; }
public string Description { get; set; }
@ -19,7 +22,9 @@ namespace SpotifyAPI.Web
public string ReleaseDatePrecision { get; set; }
public ResumePoint ResumePoint { get; set; }
public SimpleShow Show { get; set; }
public ElementType Type { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public ItemType Type { get; set; }
public string Uri { get; set; }
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace SpotifyAPI.Web
{
public class FullShow
{
public List<string> AvailableMarkets { get; set; }
public List<Copyright> Copyrights { get; set; }
public string Description { get; set; }
public Paging<SimpleEpisode> Episodes { get; set; }
public bool Explicit { get; set; }
public Dictionary<string, string> ExternalUrls { get; set; }
public string Href { get; set; }
public string Id { get; set; }
public List<Image> Images { get; set; }
public bool IsExternallyHosted { get; set; }
public List<string> Languages { get; set; }
public string MediaType { get; set; }
public string Name { get; set; }
public string Publisher { get; set; }
public string Type { get; set; }
public string Uri { get; set; }
}
}

View File

@ -1,8 +1,10 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace SpotifyAPI.Web
{
public class FullTrack : IPlaylistElement
public class FullTrack : IPlaylistItem
{
public SimpleAlbum Album { get; set; }
public List<SimpleArtist> Artists { get; set; }
@ -21,7 +23,9 @@ namespace SpotifyAPI.Web
public int Popularity { get; set; }
public string PreviewUrl { get; set; }
public int TrackNumber { get; set; }
public ElementType Type { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public ItemType Type { get; set; }
public string Uri { get; set; }
public bool IsLocal { get; set; }
}

View File

@ -3,15 +3,15 @@ using Newtonsoft.Json.Converters;
namespace SpotifyAPI.Web
{
public enum ElementType
public enum ItemType
{
Track,
Episode
}
public interface IPlaylistElement
public interface IPlaylistItem
{
[JsonConverter(typeof(StringEnumConverter))]
public ElementType Type { get; set; }
public ItemType Type { get; set; }
}
}

View File

@ -9,6 +9,6 @@ namespace SpotifyAPI.Web
public PublicUser AddedBy { get; set; }
public bool IsLocal { get; set; }
[JsonConverter(typeof(PlaylistElementConverter))]
public IPlaylistElement Track { get; set; }
public IPlaylistItem Track { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace SpotifyAPI.Web
{
public class ShowsResponse
{
public List<SimpleShow> Shows { get; set; }
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace SpotifyAPI.Web
{
public class SimpleEpisode
{
public string AudioPreviewUrl { get; set; }
public string Description { get; set; }
public int DurationMs { get; set; }
public bool Explicit { get; set; }
public Dictionary<string, string> ExternalUrls { get; set; }
public string Href { get; set; }
public string Id { get; set; }
public List<Image> Images { get; set; }
public bool IsExternallyHosted { get; set; }
public bool IsPlayable { get; set; }
[Obsolete("This field is deprecated and might be removed in the future. Please use the languages field instead")]
public string Language { get; set; }
public List<string> Languages { get; set; }
public string Name { get; set; }
public string ReleaseDate { get; set; }
public string ReleaseDatePrecision { get; set; }
public ResumePoint ResumePoint { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public ItemType Type { get; set; }
public string Uri { get; set; }
}
}

View File

@ -1,10 +1,11 @@
using System.Collections.Generic;
namespace SpotifyAPI.Web
{
public class SimpleShow
{
public List<string> AvailableMarkets { get; set; }
public Copyright Copyright { get; set; }
public List<Copyright> Copyrights { get; set; }
public string Description { get; set; }
public bool Explicit { get; set; }
public Dictionary<string, string> ExternalUrls { get; set; }
@ -16,7 +17,7 @@ namespace SpotifyAPI.Web
public string MediaType { get; set; }
public string Name { get; set; }
public string Publisher { get; set; }
public ElementType Type { get; set; }
public string Type { get; set; }
public string Uri { get; set; }
}
}

View File

@ -1,4 +1,6 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace SpotifyAPI.Web
{
@ -17,7 +19,9 @@ namespace SpotifyAPI.Web
public string Name { get; set; }
public string PreviewUrl { get; set; }
public int TrackNumber { get; set; }
public ElementType Type { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public ItemType Type { get; set; }
public string Uri { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace SpotifyAPI.Web
{
public class SnapshotResponse
{
public string SnapshotId { get; set; }
}
}

View File

@ -25,6 +25,14 @@ namespace SpotifyAPI.Web
public static Uri FeaturedPlaylists() => EUri($"browse/featured-playlists");
public static Uri Show(string showId) => EUri($"shows/{showId}");
public static Uri Shows() => EUri($"shows");
public static Uri ShowEpisodes(string showId) => EUri($"shows/{showId}/episodes");
public static Uri PlaylistTracks(string playlistId) => EUri($"playlists/{playlistId}/tracks");
private static Uri EUri(FormattableString path) => new Uri(path.ToString(_provider), UriKind.Relative);
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace SpotifyAPI.Web
{
@ -12,14 +14,15 @@ namespace SpotifyAPI.Web
/// </summary>
/// <param name = "value">The argument value to check</param>
/// <param name = "name">The name of the argument</param>
public static void ArgumentNotNull(object value, string name)
/// <param name = "additional">Additional Exception Text</param>
public static void ArgumentNotNull(object value, string name, string additional = null)
{
if (value != null)
{
return;
}
throw new ArgumentNullException(name);
throw new ArgumentNullException($"{name}{additional}");
}
/// <summary>
@ -37,14 +40,14 @@ namespace SpotifyAPI.Web
throw new ArgumentException("String is empty or null", name);
}
public static void PropertyNotNull(object value, string name, string additional = null)
public static void ArgumentNotNullOrEmptyList<T>(List<T> value, string name)
{
if (value != null)
if (value != null && value.Any())
{
return;
}
throw new InvalidOperationException($"The property {name} is null{additional}");
throw new ArgumentException("List is empty or null", name);
}
}
}