From c39ccf91bd45d675dd1f7926021a94ca017b2b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Gel=C3=B3czi?= Date: Sun, 19 Dec 2021 19:21:38 +0100 Subject: [PATCH] Split AudioStationClient to multiple files --- .../AudioStationClient.Album.cs | 43 +++ .../AudioStationClient.Artist.cs | 33 ++ .../AudioStationClient.Playlist.cs | 40 +++ .../AudioStationClient.Song.cs | 136 ++++++++ .../AudioStationClient.Tag.cs | 48 +++ .../AudioStationClient.cs | 291 ++---------------- 6 files changed, 321 insertions(+), 270 deletions(-) create mode 100644 SynologyDotNet.AudioStation/AudioStationClient.Album.cs create mode 100644 SynologyDotNet.AudioStation/AudioStationClient.Artist.cs create mode 100644 SynologyDotNet.AudioStation/AudioStationClient.Playlist.cs create mode 100644 SynologyDotNet.AudioStation/AudioStationClient.Song.cs create mode 100644 SynologyDotNet.AudioStation/AudioStationClient.Tag.cs diff --git a/SynologyDotNet.AudioStation/AudioStationClient.Album.cs b/SynologyDotNet.AudioStation/AudioStationClient.Album.cs new file mode 100644 index 0000000..0148500 --- /dev/null +++ b/SynologyDotNet.AudioStation/AudioStationClient.Album.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SynologyDotNet.AudioStation.Model; +using SynologyDotNet.Core.Model; +using SynologyDotNet.Core.Responses; + +namespace SynologyDotNet.AudioStation +{ + public partial class AudioStationClient + { + /// + /// List albums + /// + /// Maximum number of items to return + /// Start position in the list (use it for paging) + /// Filter by artist name + /// Filter parameters + /// + public async Task> ListAlbumsAsync(int limit, int offset, string artist = null, params (AlbumQueryParameters, object)[] queryParameters) + { + var args = new List<(string, object)>(queryParameters.Select(f => (f.Item1.ToString(), f.Item2))); + args.Add(GetLibraryArg()); + if (!string.IsNullOrWhiteSpace(artist)) + args.Add(("artist", artist)); + + return await Client.QueryListAsync>(SYNO_AudioStation_Album, "list", limit, offset, args.ToArray()); + } + + /// + /// Download album cover image + /// + /// Artist name + /// Album title + /// + public async Task GetAlbumCoverAsync(string artist, string album) + { + return await Client.QueryByteArrayAsync(SYNO_AudioStation_Cover, "getcover", + ("album_name", album), + ("album_artist_name", artist)); + } + } +} diff --git a/SynologyDotNet.AudioStation/AudioStationClient.Artist.cs b/SynologyDotNet.AudioStation/AudioStationClient.Artist.cs new file mode 100644 index 0000000..cfbc3dc --- /dev/null +++ b/SynologyDotNet.AudioStation/AudioStationClient.Artist.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using SynologyDotNet.AudioStation.Model; +using SynologyDotNet.Core.Model; +using SynologyDotNet.Core.Responses; + +namespace SynologyDotNet.AudioStation +{ + public partial class AudioStationClient + { + /// + /// List artists + /// + /// Maximum number of items to return + /// Start position in the list (use it for paging) + /// + public async Task> ListArtistsAsync(int limit, int offset) + { + return await Client.QueryListAsync>(SYNO_AudioStation_Artist, "list", limit, offset, GetLibraryArg()); //personal + } + + /// + /// Download artist cover image + /// + /// Artist name + /// + public async Task GetArtistCoverAsync(string artist) + { + return await Client.QueryByteArrayAsync(SYNO_AudioStation_Cover, "getcover", + GetLibraryArg(), + ("artist_name", artist)); + } + } +} diff --git a/SynologyDotNet.AudioStation/AudioStationClient.Playlist.cs b/SynologyDotNet.AudioStation/AudioStationClient.Playlist.cs new file mode 100644 index 0000000..8048601 --- /dev/null +++ b/SynologyDotNet.AudioStation/AudioStationClient.Playlist.cs @@ -0,0 +1,40 @@ +using System.Linq; +using System.Threading.Tasks; +using SynologyDotNet.AudioStation.Model; +using SynologyDotNet.Core.Responses; + +namespace SynologyDotNet.AudioStation +{ + public partial class AudioStationClient + { + /// + /// Lists the playlists. + /// + /// The limit. + /// The offset. + /// + public async Task> ListPlaylistsAsync(int limit, int offset) + { + var result = await Client.QueryListAsync>(SYNO_AudioStation_Playlist, "list", limit, offset, + ("library", "all")); + return result; + } + + /// + /// Gets the songs on a specific playlist. + /// + /// The limit. + /// The offset. + /// The identifier. + /// + public async Task> GetPlaylistAsync(int limit, int offset, string id) + { + var result = await Client.QueryListAsync>(SYNO_AudioStation_Playlist, "getinfo", limit, offset, + ("library", "all"), + ("id", id), + ("additional", "songs") //("additional", "songs_song_tag,songs_song_audio,songs_song_rating,sharing_info") + ); + return new ApiDataResponse(result, result.Data?.playlists?.FirstOrDefault() ?? default); + } + } +} diff --git a/SynologyDotNet.AudioStation/AudioStationClient.Song.cs b/SynologyDotNet.AudioStation/AudioStationClient.Song.cs new file mode 100644 index 0000000..177257d --- /dev/null +++ b/SynologyDotNet.AudioStation/AudioStationClient.Song.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using SynologyDotNet.AudioStation.Model; +using SynologyDotNet.Core.Helpers; +using SynologyDotNet.Core.Model; +using SynologyDotNet.Core.Responses; + +namespace SynologyDotNet.AudioStation +{ + public partial class AudioStationClient + { + /// + /// List songs + /// + /// Maximum number of items to return + /// Start position in the list (use it for paging) + /// Additional filds to load + /// Filter parameters + /// + public async Task> ListSongsAsync(int limit, int offset, SongQueryAdditional additional, params (SongQueryParameters, object)[] queryParameters) + { + var args = new List<(string, object)>(queryParameters.Select(f => (f.Item1.ToString(), f.Item2))); + args.Add(GetLibraryArg()); + if (additional != SongQueryAdditional.None) + { + args.Add(("additional", string.Join(",", (new[] { + SongQueryAdditional.song_audio, + SongQueryAdditional.song_rating, + SongQueryAdditional.song_tag + }) + .Where(x => additional.HasFlag(x)) + .Select(x => x.ToString())))); + } + var result = await Client.QueryListAsync>(SYNO_AudioStation_Song, "list", limit, offset, args.ToArray()); + return result; + } + + /// + /// Gets a song by ID + /// + /// + /// + public async Task> GetSongByIdAsync(string id) + { + var args = new List<(string, object)>(); + args.Add(("id", id)); + args.Add(("additional", "song_tag, song_audio, song_rating")); // request detailed song info + var result = await Client.QueryObjectAsync>(SYNO_AudioStation_Song, "getinfo", args.ToArray()); + return result; + } + + /// + /// Set song rating + /// + /// + /// Value from 0 to 5 + /// + public async Task RateSongAsync(string songId, int rating) + { + if (rating < 0 || rating > 5) + throw new ArgumentOutOfRangeException(nameof(rating), "Value range: 0 - 5"); + var result = await Client.QueryObjectAsync(SYNO_AudioStation_Song, "setrating", + ("id", songId), + ("rating", rating)); + return result; + } + + /// + /// Download the song from the server. + /// + /// Cancellation token + /// Transcode method + /// ID of the song to download + /// Start position in seconds + /// The action called to download the song bytes. + /// + public async Task StreamSongAsync( + CancellationToken cancellationToken, + TranscodeMode transcode, + string songId, + double positionSeconds, + Action readStreamAction) + { + var req = CreateSongStreamRequest(SYNO_AudioStation_Stream, transcode, songId, positionSeconds); + await Client.QueryStreamAsync(req, readStreamAction, cancellationToken); + } + + private RequestBuilder CreateSongStreamRequest(string apiName, TranscodeMode transcode, string songId, double positionInSeconds) + { + string method = "stream"; + string subEndpoint = null; + string format = null; + switch (transcode) + { + case TranscodeMode.MP3_128Kbps: + case TranscodeMode.MP3_192Kbps: + case TranscodeMode.MP3_256Kbps: + case TranscodeMode.MP3_320Kbps: + subEndpoint = "/0.mp3"; + method = "transcode"; + format = "mp3"; + break; + case TranscodeMode.WAV: + subEndpoint = "/0.wav"; + method = "transcode"; + format = "wav"; + break; + } + var req = new RequestBuilder(Client.GetApiInfo(apiName), subEndpoint).Method(method).SetParam("id", songId); + if (!string.IsNullOrEmpty(format)) + req.SetParam("format", format); + if (positionInSeconds > 0) + req.SetParam("position", Math.Round(positionInSeconds, 4).ToString(CultureInfo.InvariantCulture)); + switch (transcode) + { + case TranscodeMode.MP3_128Kbps: + req.SetParam("bitrate", "128000"); + break; + case TranscodeMode.MP3_192Kbps: + req.SetParam("bitrate", "192000"); + break; + case TranscodeMode.MP3_256Kbps: + req.SetParam("bitrate", "256000"); + break; + case TranscodeMode.MP3_320Kbps: + req.SetParam("bitrate", "320000"); + break; + } + return req; + } + } +} diff --git a/SynologyDotNet.AudioStation/AudioStationClient.Tag.cs b/SynologyDotNet.AudioStation/AudioStationClient.Tag.cs new file mode 100644 index 0000000..3eccd88 --- /dev/null +++ b/SynologyDotNet.AudioStation/AudioStationClient.Tag.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using SynologyDotNet.AudioStation.Model; +using SynologyDotNet.Core.Helpers; +using SynologyDotNet.Core.Responses; + +namespace SynologyDotNet.AudioStation +{ + public partial class AudioStationClient + { + /// + /// Query song tags + /// + /// The internal path of the music file. Must contain forward slaashes '/' + /// + public async Task GetSongFileTags(params string[] paths) + { + if (paths?.Any() != true) + throw new ArgumentNullException(nameof(paths)); + if (paths.Any(p => p.Contains("\\"))) + throw new ArgumentException("Invalid path. Path must contain forward slashes '/', not back-slashes '\\'"); + var req = new RequestBuilder().SetEndpoint(TagEditorEndpoint).Action("load"); + req["audioInfos"] = JsonConvert.SerializeObject(paths.Select(p => new { path = p })); + req["requestFrom"] = string.Empty; + var result = await Client.QueryObjectAsync(req); + return result; + } + + /// + /// Batch edit music file tags + /// + /// + /// + public async Task SetSongFileTags(FileTagChange change) + { + if (change?.AudioInfos?.Any() != true) + throw new ArgumentNullException($"{nameof(change)}.{nameof(change.AudioInfos)}"); + + var req = new RequestBuilder().SetEndpoint(TagEditorEndpoint).Action("apply"); + req["data"] = JsonConvert.SerializeObject(new object[] { change }); + var result = await Client.QueryObjectAsync(req); + return result; + } + + } +} diff --git a/SynologyDotNet.AudioStation/AudioStationClient.cs b/SynologyDotNet.AudioStation/AudioStationClient.cs index 0b6b743..95feb1c 100644 --- a/SynologyDotNet.AudioStation/AudioStationClient.cs +++ b/SynologyDotNet.AudioStation/AudioStationClient.cs @@ -1,13 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading; +using System.Collections.Generic; using System.Threading.Tasks; -using Newtonsoft.Json; using SynologyDotNet.AudioStation.Model; -using SynologyDotNet.Core.Helpers; -using SynologyDotNet.Core.Model; using SynologyDotNet.Core.Responses; namespace SynologyDotNet.AudioStation @@ -15,7 +8,7 @@ namespace SynologyDotNet.AudioStation /// /// Connects to AudioStation APIs /// - public sealed class AudioStationClient : StationConnectorBase + public partial class AudioStationClient : StationConnectorBase { #region Fields @@ -73,175 +66,49 @@ public sealed class AudioStationClient : StationConnectorBase public bool PersonalMusicOnly { get; set; } #endregion Properties + + #region Constructor /// Initializes a new instance of the class. public AudioStationClient() : base() { } - #region Folder + #endregion Constructor - /// - /// List folders - /// - /// Maximum number of items to return - /// Start position in the list (use it for paging) - /// Root folder ID, the children directories will be listed in the response. Not recursive. - /// - public async Task> ListFoldersAsync(int limit, int offset, string folderId = null) - { - var args = new List<(string, object)>(); - args.Add(GetLibraryArg()); - if (!string.IsNullOrEmpty(folderId)) - args.Add(("id", folderId)); - - return await Client.QueryListAsync>(SYNO_AudioStation_Folder, "list", limit, offset, args.ToArray()); - } - - #endregion Folder - - #region Artist - - /// - /// List artists - /// - /// Maximum number of items to return - /// Start position in the list (use it for paging) - /// - public async Task> ListArtistsAsync(int limit, int offset) - { - return await Client.QueryListAsync>(SYNO_AudioStation_Artist, "list", limit, offset, GetLibraryArg()); //personal - } - - /// - /// Download artist cover image - /// - /// Artist name - /// - public async Task GetArtistCoverAsync(string artist) - { - return await Client.QueryByteArrayAsync(SYNO_AudioStation_Cover, "getcover", - GetLibraryArg(), - ("artist_name", artist)); - } - - #endregion Artist - - #region Album - - /// - /// List albums - /// - /// Maximum number of items to return - /// Start position in the list (use it for paging) - /// Filter by artist name - /// Filter parameters - /// - public async Task> ListAlbumsAsync(int limit, int offset, string artist = null, params (AlbumQueryParameters, object)[] queryParameters) - { - var args = new List<(string, object)>(queryParameters.Select(f => (f.Item1.ToString(), f.Item2))); - args.Add(GetLibraryArg()); - if (!string.IsNullOrWhiteSpace(artist)) - args.Add(("artist", artist)); - - return await Client.QueryListAsync>(SYNO_AudioStation_Album, "list", limit, offset, args.ToArray()); - } - - /// - /// Download album cover image - /// - /// Artist name - /// Album title - /// - public async Task GetAlbumCoverAsync(string artist, string album) - { - return await Client.QueryByteArrayAsync(SYNO_AudioStation_Cover, "getcover", - ("album_name", album), - ("album_artist_name", artist)); - } - - #endregion Album - - #region Song - - /// - /// List songs - /// - /// Maximum number of items to return - /// Start position in the list (use it for paging) - /// Additional filds to load - /// Filter parameters - /// - public async Task> ListSongsAsync(int limit, int offset, SongQueryAdditional additional, params (SongQueryParameters, object)[] queryParameters) - { - var args = new List<(string, object)>(queryParameters.Select(f => (f.Item1.ToString(), f.Item2))); - args.Add(GetLibraryArg()); - if (additional != SongQueryAdditional.None) - { - args.Add(("additional", string.Join(",", (new[] { - SongQueryAdditional.song_audio, - SongQueryAdditional.song_rating, - SongQueryAdditional.song_tag - }) - .Where(x => additional.HasFlag(x)) - .Select(x => x.ToString())))); - } - var result = await Client.QueryListAsync>(SYNO_AudioStation_Song, "list", limit, offset, args.ToArray()); - return result; - } + #region Public Methods /// - /// Gets a song by ID + /// Searches the music library. /// - /// + /// The text to search. /// - public async Task> GetSongByIdAsync(string id) + public async Task> SearchAsync(string keyword) { var args = new List<(string, object)>(); - args.Add(("id", id)); args.Add(("additional", "song_tag, song_audio, song_rating")); // request detailed song info - var result = await Client.QueryObjectAsync>(SYNO_AudioStation_Song, "getinfo", args.ToArray()); + args.Add(("keyword", keyword)); + var result = await Client.QueryObjectAsync>(SYNO_AudioStation_Search, "list", args.ToArray()); return result; } /// - /// Set song rating + /// List folders /// - /// - /// Value from 0 to 5 + /// Maximum number of items to return + /// Start position in the list (use it for paging) + /// Root folder ID, the children directories will be listed in the response. Not recursive. /// - public async Task RateSongAsync(string songId, int rating) + public async Task> ListFoldersAsync(int limit, int offset, string folderId = null) { - if (rating < 0 || rating > 5) - throw new ArgumentOutOfRangeException(nameof(rating), "Value range: 0 - 5"); - var result = await Client.QueryObjectAsync(SYNO_AudioStation_Song, "setrating", - ("id", songId), - ("rating", rating)); - return result; - } + var args = new List<(string, object)>(); + args.Add(GetLibraryArg()); + if (!string.IsNullOrEmpty(folderId)) + args.Add(("id", folderId)); - /// - /// Download the song from the server. - /// - /// Cancellation token - /// Transcode method - /// ID of the song to download - /// Start position in seconds - /// The action called to download the song bytes. - /// - public async Task StreamSongAsync( - CancellationToken cancellationToken, - TranscodeMode transcode, - string songId, - double positionSeconds, - Action readStreamAction) - { - var req = CreateSongStreamRequest(SYNO_AudioStation_Stream, transcode, songId, positionSeconds); - await Client.QueryStreamAsync(req, readStreamAction, cancellationToken); + return await Client.QueryListAsync>(SYNO_AudioStation_Folder, "list", limit, offset, args.ToArray()); } - #endregion - // Use tageditor instead! //#region Lyrics //public async Task> GetLyricsAsync(string songId) @@ -259,128 +126,12 @@ public async Task StreamSongAsync( //} //#endregion - #region Tags - - /// - /// Query song tags - /// - /// The internal path of the music file. Must contain forward slaashes '/' - /// - public async Task GetSongFileTags(params string[] paths) - { - if (paths?.Any() != true) - throw new ArgumentNullException(nameof(paths)); - if (paths.Any(p => p.Contains("\\"))) - throw new ArgumentException("Invalid path. Path must contain forward slashes '/', not back-slashes '\\'"); - var req = new RequestBuilder().SetEndpoint(TagEditorEndpoint).Action("load"); - req["audioInfos"] = JsonConvert.SerializeObject(paths.Select(p => new { path = p })); - req["requestFrom"] = string.Empty; - var result = await Client.QueryObjectAsync(req); - return result; - } - - /// - /// Batch edit music file tags - /// - /// - /// - public async Task SetSongFileTags(FileTagChange change) - { - if (change?.AudioInfos?.Any() != true) - throw new ArgumentNullException($"{nameof(change)}.{nameof(change.AudioInfos)}"); - - var req = new RequestBuilder().SetEndpoint(TagEditorEndpoint).Action("apply"); - req["data"] = JsonConvert.SerializeObject(new object[] { change }); - var result = await Client.QueryObjectAsync(req); - return result; - } - - #endregion - - #region Search - - /// - /// Searches the music library. - /// - /// The text to search. - /// - public async Task> SearchAsync(string keyword) - { - var args = new List<(string, object)>(); - args.Add(("additional", "song_tag, song_audio, song_rating")); // request detailed song info - args.Add(("keyword", keyword)); - var result = await Client.QueryObjectAsync>(SYNO_AudioStation_Search, "list", args.ToArray()); - return result; - } - - #endregion - - #region Playlist - public async Task> ListPlaylistsAsync(int limit, int offset) - { - var result = await Client.QueryListAsync>(SYNO_AudioStation_Playlist, "list", limit, offset, - ("library", "all")); - return result; - } - - public async Task> GetPlaylistAsync(int limit, int offset, string id) - { - var result = await Client.QueryListAsync>(SYNO_AudioStation_Playlist, "getinfo", limit, offset, - ("library", "all"), - ("id", id), - ("additional", "songs") //("additional", "songs_song_tag,songs_song_audio,songs_song_rating,sharing_info") - ); - return new ApiDataResponse(result, result.Data?.playlists?.FirstOrDefault() ?? default); - } #endregion #region Private Methods private (string, string) GetLibraryArg() => ("library", PersonalMusicOnly ? "personal" : "all"); - private RequestBuilder CreateSongStreamRequest(string apiName, TranscodeMode transcode, string songId, double positionInSeconds) - { - string method = "stream"; - string subEndpoint = null; - string format = null; - switch (transcode) - { - case TranscodeMode.MP3_128Kbps: - case TranscodeMode.MP3_192Kbps: - case TranscodeMode.MP3_256Kbps: - case TranscodeMode.MP3_320Kbps: - subEndpoint = "/0.mp3"; - method = "transcode"; - format = "mp3"; - break; - case TranscodeMode.WAV: - subEndpoint = "/0.wav"; - method = "transcode"; - format = "wav"; - break; - } - var req = new RequestBuilder(Client.GetApiInfo(apiName), subEndpoint).Method(method).SetParam("id", songId); - if (!string.IsNullOrEmpty(format)) - req.SetParam("format", format); - if (positionInSeconds > 0) - req.SetParam("position", Math.Round(positionInSeconds, 4).ToString(CultureInfo.InvariantCulture)); - switch (transcode) - { - case TranscodeMode.MP3_128Kbps: - req.SetParam("bitrate", "128000"); - break; - case TranscodeMode.MP3_192Kbps: - req.SetParam("bitrate", "192000"); - break; - case TranscodeMode.MP3_256Kbps: - req.SetParam("bitrate", "256000"); - break; - case TranscodeMode.MP3_320Kbps: - req.SetParam("bitrate", "320000"); - break; - } - return req; - } #endregion } } \ No newline at end of file