Added Player Client #451

This commit is contained in:
Jonas Dellinger 2020-05-07 18:03:20 +02:00
parent 2f09ba25ec
commit 249a535e07
39 changed files with 602 additions and 15 deletions

View File

@ -16,6 +16,8 @@ indent_size = 2
# Does not seem to work...
dotnet_diagnostic.CA1303.severity = none
dotnet_diagnostic.CA1056.severity = none
dotnet_diagnostic.CA1034.severity = none
dotnet_diagnostic.CA1054.severity = none
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
dotnet_style_require_accessibility_modifiers = always:warning

View File

@ -0,0 +1,42 @@
using System.Net;
using System.Threading.Tasks;
namespace SpotifyAPI.Web
{
public interface IPlayerClient
{
Task<bool> SkipNext();
Task<bool> SkipNext(PlayerSkipNextRequest request);
Task<bool> SetRepeat(PlayerSetRepeatRequest request);
Task<bool> TransferPlayback(PlayerTransferPlaybackRequest request);
Task<CurrentlyPlaying> GetCurrentlyPlaying(PlayerCurrentlyPlayingRequest request);
Task<CurrentlyPlayingContext> GetCurrentPlayback();
Task<CurrentlyPlayingContext> GetCurrentPlayback(PlayerCurrentPlaybackRequest request);
Task<bool> SeekTo(PlayerSeekToRequest request);
Task<bool> SkipPrevious();
Task<bool> SkipPrevious(PlayerSkipPreviousRequest request);
Task<bool> ResumePlayback();
Task<bool> ResumePlayback(PlayerResumePlaybackRequest request);
Task<bool> PausePlayback();
Task<bool> PausePlayback(PlayerPausePlaybackRequest request);
Task<bool> SetVolume(PlayerVolumeRequest request);
Task<CursorPaging<PlayHistoryItem>> GetRecentlyPlayed();
Task<CursorPaging<PlayHistoryItem>> GetRecentlyPlayed(PlayerRecentlyPlayedRequest request);
Task<DeviceResponse> GetAvailableDevices();
Task<bool> SetShuffle(PlayerShuffleRequest request);
Task<bool> AddToQueue(PlayerAddToQueueRequest request);
}
}

View File

@ -9,8 +9,8 @@ namespace SpotifyAPI.Web
Task<SnapshotResponse> AddItems(string playlistId, PlaylistAddItemsRequest request);
Task<Paging<PlaylistTrack<IPlaylistItem>>> GetItems(string playlistId);
Task<Paging<PlaylistTrack<IPlaylistItem>>> GetItems(string playlistId, PlaylistGetItemsRequest request);
Task<Paging<PlaylistTrack<IPlayableItem>>> GetItems(string playlistId);
Task<Paging<PlaylistTrack<IPlayableItem>>> GetItems(string playlistId, PlaylistGetItemsRequest request);
Task<FullPlaylist> Create(string userId, PlaylistCreateRequest request);

View File

@ -22,6 +22,8 @@ namespace SpotifyAPI.Web
ITracksClient Tracks { get; }
IPlayerClient Player { get; }
Task<List<T>> Paginate<T>(Paging<T> firstPage);
Task<List<T>> Paginate<T, TNext>(Paging<T, TNext> firstPage, Func<TNext, Paging<T, TNext>> mapper);
Task<List<T>> Paginate<T>(Func<Task<Paging<T>>> getFirstPage);

View File

@ -0,0 +1,154 @@
using System.Net;
using System.Threading.Tasks;
using SpotifyAPI.Web.Http;
using URLs = SpotifyAPI.Web.SpotifyUrls;
namespace SpotifyAPI.Web
{
public class PlayerClient : APIClient, IPlayerClient
{
public PlayerClient(IAPIConnector apiConnector) : base(apiConnector) { }
public async Task<bool> AddToQueue(PlayerAddToQueueRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API.Post(URLs.PlayerQueue(), request.BuildQueryParams(), null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public Task<DeviceResponse> GetAvailableDevices()
{
return API.Get<DeviceResponse>(URLs.PlayerDevices());
}
public Task<CurrentlyPlaying> GetCurrentlyPlaying(PlayerCurrentlyPlayingRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
return API.Get<CurrentlyPlaying>(URLs.PlayerCurrentlyPlaying(), request.BuildQueryParams());
}
public Task<CurrentlyPlayingContext> GetCurrentPlayback()
{
return API.Get<CurrentlyPlayingContext>(URLs.Player());
}
public Task<CurrentlyPlayingContext> GetCurrentPlayback(PlayerCurrentPlaybackRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
return API.Get<CurrentlyPlayingContext>(URLs.Player(), request.BuildQueryParams());
}
public Task<CursorPaging<PlayHistoryItem>> GetRecentlyPlayed()
{
return API.Get<CursorPaging<PlayHistoryItem>>(URLs.PlayerRecentlyPlayed());
}
public Task<CursorPaging<PlayHistoryItem>> GetRecentlyPlayed(PlayerRecentlyPlayedRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
return API.Get<CursorPaging<PlayHistoryItem>>(URLs.PlayerRecentlyPlayed(), request.BuildQueryParams());
}
public async Task<bool> PausePlayback()
{
var statusCode = await API.Put(URLs.PlayerPause(), null, null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> PausePlayback(PlayerPausePlaybackRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API.Put(URLs.PlayerPause(), request.BuildQueryParams(), null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> ResumePlayback()
{
var statusCode = await API.Put(URLs.PlayerResume(), null, null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> ResumePlayback(PlayerResumePlaybackRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API
.Put(URLs.PlayerResume(), request.BuildQueryParams(), request.BuildBodyParams())
.ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> SeekTo(PlayerSeekToRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API.Put(URLs.PlayerSeek(), request.BuildQueryParams(), null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> SetRepeat(PlayerSetRepeatRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API.Put(URLs.PlayerRepeat(), request.BuildQueryParams(), null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> SetShuffle(PlayerShuffleRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API.Put(URLs.PlayerShuffle(), request.BuildQueryParams(), null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> SetVolume(PlayerVolumeRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API.Put(URLs.PlayerVolume(), request.BuildQueryParams(), null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> SkipNext()
{
var statusCode = await API.Post(URLs.PlayerNext(), null, null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> SkipNext(PlayerSkipNextRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API.Post(URLs.PlayerNext(), request.BuildQueryParams(), null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> SkipPrevious()
{
var statusCode = await API.Post(URLs.PlayerPrevious(), null, null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> SkipPrevious(PlayerSkipPreviousRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API.Post(URLs.PlayerPrevious(), request.BuildQueryParams(), null).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
public async Task<bool> TransferPlayback(PlayerTransferPlaybackRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var statusCode = await API.Put(URLs.Player(), null, request.BuildBodyParams()).ConfigureAwait(false);
return statusCode == HttpStatusCode.NoContent;
}
}
}

View File

@ -26,20 +26,20 @@ namespace SpotifyAPI.Web
return API.Post<SnapshotResponse>(URLs.PlaylistTracks(playlistId), null, request.BuildBodyParams());
}
public Task<Paging<PlaylistTrack<IPlaylistItem>>> GetItems(string playlistId)
public Task<Paging<PlaylistTrack<IPlayableItem>>> GetItems(string playlistId)
{
Ensure.ArgumentNotNullOrEmptyString(playlistId, nameof(playlistId));
var request = new PlaylistGetItemsRequest();
return API.Get<Paging<PlaylistTrack<IPlaylistItem>>>(URLs.PlaylistTracks(playlistId), request.BuildQueryParams());
return API.Get<Paging<PlaylistTrack<IPlayableItem>>>(URLs.PlaylistTracks(playlistId), request.BuildQueryParams());
}
public Task<Paging<PlaylistTrack<IPlaylistItem>>> GetItems(string playlistId, PlaylistGetItemsRequest request)
public Task<Paging<PlaylistTrack<IPlayableItem>>> GetItems(string playlistId, PlaylistGetItemsRequest request)
{
Ensure.ArgumentNotNullOrEmptyString(playlistId, nameof(playlistId));
Ensure.ArgumentNotNull(request, nameof(request));
return API.Get<Paging<PlaylistTrack<IPlaylistItem>>>(URLs.PlaylistTracks(playlistId), request.BuildQueryParams());
return API.Get<Paging<PlaylistTrack<IPlayableItem>>>(URLs.PlaylistTracks(playlistId), request.BuildQueryParams());
}
public Task<FullPlaylist> Create(string userId, PlaylistCreateRequest request)

View File

@ -26,6 +26,7 @@ namespace SpotifyAPI.Web
Search = new SearchClient(_apiConnector);
Follow = new FollowClient(_apiConnector);
Tracks = new TracksClient(_apiConnector);
Player = new PlayerClient(_apiConnector);
}
public IPaginator DefaultPaginator { get; }
@ -44,6 +45,8 @@ namespace SpotifyAPI.Web
public ITracksClient Tracks { get; }
public IPlayerClient Player { get; }
public Task<List<T>> Paginate<T>(Paging<T> firstPage)
{
return DefaultPaginator.Paginate(firstPage, _apiConnector);

View File

@ -77,6 +77,14 @@ namespace SpotifyAPI.Web.Http
return SendAPIRequest<T>(uri, HttpMethod.Get, parameters);
}
public async Task<HttpStatusCode> Get(Uri uri, IDictionary<string, string> parameters, object body)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
var response = await SendAPIRequestDetailed(uri, HttpMethod.Get, parameters, body).ConfigureAwait(false);
return response.StatusCode;
}
public Task<T> Post<T>(Uri uri)
{
Ensure.ArgumentNotNull(uri, nameof(uri));

View File

@ -18,6 +18,8 @@ namespace SpotifyAPI.Web.Http
Task<T> Get<T>(Uri uri);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716")]
Task<T> Get<T>(Uri uri, IDictionary<string, string> parameters);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716")]
Task<HttpStatusCode> Get(Uri uri, IDictionary<string, string> parameters, object body);
Task<T> Post<T>(Uri uri);
Task<T> Post<T>(Uri uri, IDictionary<string, string> parameters);

View File

@ -4,7 +4,7 @@ using Newtonsoft.Json.Linq;
namespace SpotifyAPI.Web
{
public class PlaylistElementConverter : JsonConverter
public class PlayableItemConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => true;

View File

@ -0,0 +1,18 @@
namespace SpotifyAPI.Web
{
public class PlayerAddToQueueRequest : RequestParams
{
public PlayerAddToQueueRequest(string uri)
{
Ensure.ArgumentNotNullOrEmptyString(uri, nameof(uri));
Uri = uri;
}
[QueryParam("uri")]
public string Uri { get; }
[QueryParam("device_id")]
public string DeviceId { get; set; }
}
}

View File

@ -0,0 +1,34 @@
using System;
namespace SpotifyAPI.Web
{
public class PlayerCurrentPlaybackRequest : RequestParams
{
public PlayerCurrentPlaybackRequest(AdditionalTypes types = AdditionalTypes.All)
{
Ensure.ArgumentNotNull(types, nameof(types));
AdditionalTypesParam = types;
}
[QueryParam("market")]
public string Market { get; set; }
/// <summary>
/// This is set to `"track", "episode"` by default.
/// </summary>
/// <value></value>
[QueryParam("additional_types")]
public AdditionalTypes AdditionalTypesParam { get; }
[Flags]
public enum AdditionalTypes
{
[String("track")]
Track = 1,
[String("episode")]
Episode = 2,
All = Track | Episode
}
}
}

View File

@ -0,0 +1,36 @@
using System;
namespace SpotifyAPI.Web
{
public class PlayerCurrentlyPlayingRequest : RequestParams
{
public PlayerCurrentlyPlayingRequest(string market, AdditionalTypes types = AdditionalTypes.All)
{
Ensure.ArgumentNotNullOrEmptyString(market, nameof(market));
Ensure.ArgumentNotNull(types, nameof(types));
Market = market;
AdditionalTypesParam = types;
}
[QueryParam("market")]
public string Market { get; }
/// <summary>
/// This is set to `"track", "episode"` by default.
/// </summary>
/// <value></value>
[QueryParam("additional_types")]
public AdditionalTypes AdditionalTypesParam { get; }
[Flags]
public enum AdditionalTypes
{
[String("track")]
Track = 1,
[String("episode")]
Episode = 2,
All = Track | Episode
}
}
}

View File

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

View File

@ -0,0 +1,14 @@
namespace SpotifyAPI.Web
{
public class PlayerRecentlyPlayedRequest : RequestParams
{
[QueryParam("limit")]
public int? Limit { get; set; }
[QueryParam("after")]
public long? After { get; set; }
[QueryParam("before")]
public long? Before { get; set; }
}
}

View File

@ -0,0 +1,33 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace SpotifyAPI.Web
{
public class PlayerResumePlaybackRequest : RequestParams
{
[QueryParam("device_id")]
public string DeviceId { get; set; }
[BodyParam("context_uri")]
public string ContextUri { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227")]
[BodyParam("uris")]
public IList<string> Uris { get; set; }
[BodyParam("offset")]
public Offset OffsetParam { get; set; }
[BodyParam("position_ms")]
public int? PositionMs { get; set; }
public class Offset
{
[JsonProperty("uri", NullValueHandling = NullValueHandling.Ignore)]
public string Uri { get; set; }
[JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
public int? Position { get; set; }
}
}
}

View File

@ -0,0 +1,16 @@
namespace SpotifyAPI.Web
{
public class PlayerSeekToRequest : RequestParams
{
public PlayerSeekToRequest(long positionMs)
{
PositonMs = positionMs;
}
[QueryParam("position_ms")]
public long PositonMs { get; }
[QueryParam("device_id")]
public string DeviceId { get; set; }
}
}

View File

@ -0,0 +1,30 @@
namespace SpotifyAPI.Web
{
public class PlayerSetRepeatRequest : RequestParams
{
public PlayerSetRepeatRequest(State state)
{
Ensure.ArgumentNotNull(state, nameof(state));
StateParam = state;
}
[QueryParam("device_id")]
public string DeviceId { get; set; }
[QueryParam("state")]
public State StateParam { get; }
public enum State
{
[String("track")]
Track,
[String("context")]
Context,
[String("off")]
Off
}
}
}

View File

@ -0,0 +1,16 @@
namespace SpotifyAPI.Web
{
public class PlayerShuffleRequest : RequestParams
{
public PlayerShuffleRequest(bool state)
{
State = state;
}
[QueryParam("state")]
public bool State { get; }
[QueryParam("device_id")]
public string DeviceId { get; set; }
}
}

View File

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

View File

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

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace SpotifyAPI.Web
{
public class PlayerTransferPlaybackRequest : RequestParams
{
public PlayerTransferPlaybackRequest(IList<string> deviceIds)
{
Ensure.ArgumentNotNullOrEmptyList(deviceIds, nameof(deviceIds));
DeviceIds = deviceIds;
}
[BodyParam("device_ids")]
public IList<string> DeviceIds { get; }
[BodyParam("play")]
public bool? Play { get; set; }
}
}

View File

@ -0,0 +1,16 @@
namespace SpotifyAPI.Web
{
public class PlayerVolumeRequest : RequestParams
{
public PlayerVolumeRequest(int volumePercent)
{
VolumePercent = volumePercent;
}
[QueryParam("volume_percent")]
public int VolumePercent { get; }
[QueryParam("device_id")]
public string DeviceId { get; set; }
}
}

View File

@ -30,7 +30,7 @@ namespace SpotifyAPI.Web
/// </summary>
/// <value></value>
[QueryParam("additional_types")]
public AdditionalTypes AdditionalTypesParam { get; set; }
public AdditionalTypes AdditionalTypesParam { get; }
[Flags]
public enum AdditionalTypes

View File

@ -17,7 +17,7 @@ namespace SpotifyAPI.Web
/// </summary>
/// <value></value>
[QueryParam("additional_types")]
public AdditionalTypes AdditionalTypesParam { get; set; }
public AdditionalTypes AdditionalTypesParam { get; }
[Flags]
public enum AdditionalTypes

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace SpotifyAPI.Web
{
public class Actions
{
public Dictionary<string, bool> Disallows { get; private set; }
}
}

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace SpotifyAPI.Web
{
public class Context
{
public Dictionary<string, string> ExternalUrls { get; private set; }
public string Href { get; private set; }
public string Type { get; private set; }
public string Uri { get; private set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using Newtonsoft.Json;
namespace SpotifyAPI.Web
{
public class CurrentlyPlaying
{
public Context Context { get; private set; }
public string CurrentlyPlayingType { get; private set; }
public bool IsPlaying { get; private set; }
[JsonConverter(typeof(PlayableItemConverter))]
public IPlayableItem Item { get; private set; }
public int? ProgressMs { get; private set; }
public long Timestamp { get; private set; }
}
}

View File

@ -0,0 +1,21 @@
using Newtonsoft.Json;
namespace SpotifyAPI.Web
{
public class CurrentlyPlayingContext
{
public Device Device { get; private set; }
public string RepeatState { get; private set; }
public bool ShuffleState { get; private set; }
public Context Context { get; private set; }
public long Timestamp { get; private set; }
public int ProgressMs { get; private set; }
public bool IsPlaying { get; private set; }
[JsonConverter(typeof(PlayableItemConverter))]
public IPlayableItem Item { get; private set; }
public string CurrentlyPlayingType { get; private set; }
public Actions Actions { get; private set; }
}
}

View File

@ -0,0 +1,13 @@
namespace SpotifyAPI.Web
{
public class Device
{
public string Id { get; private set; }
public bool IsActive { get; private set; }
public bool IsPrivateSession { get; private set; }
public bool IsRestricted { get; private set; }
public string Name { get; private set; }
public string Type { get; private set; }
public int? VolumePercent { get; private set; }
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace SpotifyAPI.Web
{
public class DeviceResponse
{
public List<Device> Devices { get; private set; }
}
}

View File

@ -4,7 +4,7 @@ using Newtonsoft.Json.Converters;
namespace SpotifyAPI.Web
{
public class FullEpisode : IPlaylistItem
public class FullEpisode : IPlayableItem
{
public string AudioPreviewUrl { get; private set; }
public string Description { get; private set; }

View File

@ -12,7 +12,7 @@ namespace SpotifyAPI.Web
public PublicUser Owner { get; private set; }
public bool Public { get; private set; }
public string SnapshotId { get; private set; }
public Paging<PlaylistTrack<IPlaylistItem>> Tracks { get; private set; }
public Paging<PlaylistTrack<IPlayableItem>> Tracks { get; private set; }
public string Type { get; private set; }
public string Uri { get; private set; }
}

View File

@ -4,7 +4,7 @@ using Newtonsoft.Json.Converters;
namespace SpotifyAPI.Web
{
public class FullTrack : IPlaylistItem
public class FullTrack : IPlayableItem
{
public SimpleAlbum Album { get; private set; }
public List<SimpleArtist> Artists { get; private set; }

View File

@ -9,7 +9,7 @@ namespace SpotifyAPI.Web
Episode
}
public interface IPlaylistItem
public interface IPlayableItem
{
[JsonConverter(typeof(StringEnumConverter))]
public ItemType Type { get; }

View File

@ -0,0 +1,10 @@
using System;
namespace SpotifyAPI.Web
{
public class PlayHistoryItem
{
public SimpleTrack Track { get; private set; }
public DateTime PlayedAt { get; private set; }
public Context Context { get; private set; }
}
}

View File

@ -9,7 +9,7 @@ namespace SpotifyAPI.Web
public PublicUser AddedBy { get; private set; }
public bool IsLocal { get; private set; }
[JsonConverter(typeof(PlaylistElementConverter))]
[JsonConverter(typeof(PlayableItemConverter))]
public T Track { get; private set; }
}
}

View File

@ -16,7 +16,7 @@ namespace SpotifyAPI.Web
public PublicUser Owner { get; private set; }
public bool? Public { get; private set; }
public string SnapshotId { get; private set; }
public Paging<PlaylistTrack<IPlaylistItem>> Tracks { get; private set; }
public Paging<PlaylistTrack<IPlayableItem>> Tracks { get; private set; }
public string Type { get; private set; }
public string Uri { get; private set; }
}

View File

@ -61,6 +61,32 @@ namespace SpotifyAPI.Web
public static Uri AudioFeatures() => EUri($"audio-features");
public static Uri Player() => EUri($"me/player");
public static Uri PlayerQueue() => EUri($"me/player/queue");
public static Uri PlayerDevices() => EUri($"me/player/devices");
public static Uri PlayerCurrentlyPlaying() => EUri($"me/player/currently-playing");
public static Uri PlayerRecentlyPlayed() => EUri($"me/player/recently-played");
public static Uri PlayerPause() => EUri($"me/player/pause");
public static Uri PlayerResume() => EUri($"me/player/play");
public static Uri PlayerSeek() => EUri($"me/player/seek");
public static Uri PlayerRepeat() => EUri($"me/player/repeat");
public static Uri PlayerShuffle() => EUri($"me/player/shuffle");
public static Uri PlayerVolume() => EUri($"me/player/volume");
public static Uri PlayerNext() => EUri($"me/player/next");
public static Uri PlayerPrevious() => EUri($"me/player/previous");
private static Uri EUri(FormattableString path) => new Uri(path.ToString(_provider), UriKind.Relative);
}
}