From a2c012dd7e9ed2bb58fa0b50204a1a117b0e5758 Mon Sep 17 00:00:00 2001 From: Kim Hugener-Ohlsen Date: Fri, 5 Jan 2018 10:18:57 +0100 Subject: [PATCH] Various improvements --- .gitignore | 2 + src/MpcNET.Test/MpcNET.Test.csproj | 9 +- src/MpcNET.Test/Tests/DatabaseCommandsTest.cs | 5 +- src/MpcNET.Test/Tests/OutputCommandsTest.cs | 21 +- .../Tests/PlaylistsCommandsTest.cs | 25 +- .../Tests/ReflectionCommandsTest.cs | 9 +- src/MpcNET/Commands/Commands.Database.cs | 130 +----- src/MpcNET/Commands/Commands.Output.cs | 88 +--- src/MpcNET/Commands/Commands.Playback.cs | 24 ++ src/MpcNET/Commands/Commands.Playlists.cs | 301 +------------- src/MpcNET/Commands/Commands.Reflection.cs | 90 +--- src/MpcNET/Commands/Commands.Status.cs | 24 ++ src/MpcNET/Commands/Database/FindCommand.cs | 30 ++ .../Commands/Database/ListAllCommand.cs | 41 ++ src/MpcNET/Commands/Database/ListCommand.cs | 25 ++ src/MpcNET/Commands/Database/UpdateCommand.cs | 54 +++ .../Commands/Output/DisableOutputCommand.cs | 25 ++ .../Commands/Output/EnableOutputCommand.cs | 25 ++ src/MpcNET/Commands/Output/OutputsCommand.cs | 29 ++ .../Commands/Output/ToggleOutputCommand.cs | 24 ++ src/MpcNET/Commands/Playback/NextCommand.cs | 14 + src/MpcNET/Commands/Playback/PlayCommand.cs | 33 ++ .../Commands/Playback/PlayPauseCommand.cs | 14 + .../Commands/Playback/PreviousCommand.cs | 14 + src/MpcNET/Commands/Playback/StopCommand.cs | 14 + src/MpcNET/Commands/Playlist/AddCommand.cs | 24 ++ src/MpcNET/Commands/Playlist/AddIdCommand.cs | 24 ++ src/MpcNET/Commands/Playlist/ClearCommand.cs | 17 + src/MpcNET/Commands/Playlist/DeleteCommand.cs | 24 ++ .../Commands/Playlist/DeleteIdCommand.cs | 24 ++ .../Commands/Playlist/ListPlaylistCommand.cs | 28 ++ .../Playlist/ListPlaylistInfoCommand.cs | 25 ++ .../Commands/Playlist/ListPlaylistsCommand.cs | 33 ++ src/MpcNET/Commands/Playlist/LoadCommand.cs | 24 ++ .../Commands/Playlist/PlaylistCommand.cs | 21 + .../Commands/Playlist/PlaylistIdCommand.cs | 25 ++ .../Commands/Playlist/PlaylistInfoCommand.cs | 18 + .../Commands/Reflection/CommandsCommand.cs | 22 + .../Commands/Reflection/DecodersCommand.cs | 44 ++ .../Commands/Reflection/TagTypesCommand.cs | 22 + .../Commands/Reflection/UrlHandlersCommand.cs | 20 + .../Commands/Status/CurrentSongCommand.cs | 15 + src/MpcNET/Commands/Status/StatusCommand.cs | 132 ++++++ src/MpcNET/{Commands => }/IMpcCommand.cs | 0 src/MpcNET/Message/IMpdMessage.cs | 10 + src/MpcNET/Message/MpdMessage.cs | 9 +- src/MpcNET/MpcConnection.cs | 76 ++-- src/MpcNET/MpcNET.csproj | 10 +- src/MpcNET/MpdState.cs | 25 ++ src/MpcNET/MpdStatus.cs | 295 +++++++------ src/MpcNET/Properties/AssemblyInfo.cs | 1 - src/MpcNET/Types/IMpdFile.cs | 77 +++- src/MpcNET/Types/IMpdFilePath.cs | 7 + src/MpcNET/Types/MpdFile.cs | 390 ++++++++++++++---- 54 files changed, 1649 insertions(+), 863 deletions(-) create mode 100644 src/MpcNET/Commands/Commands.Playback.cs create mode 100644 src/MpcNET/Commands/Commands.Status.cs create mode 100644 src/MpcNET/Commands/Database/FindCommand.cs create mode 100644 src/MpcNET/Commands/Database/ListAllCommand.cs create mode 100644 src/MpcNET/Commands/Database/ListCommand.cs create mode 100644 src/MpcNET/Commands/Database/UpdateCommand.cs create mode 100644 src/MpcNET/Commands/Output/DisableOutputCommand.cs create mode 100644 src/MpcNET/Commands/Output/EnableOutputCommand.cs create mode 100644 src/MpcNET/Commands/Output/OutputsCommand.cs create mode 100644 src/MpcNET/Commands/Output/ToggleOutputCommand.cs create mode 100644 src/MpcNET/Commands/Playback/NextCommand.cs create mode 100644 src/MpcNET/Commands/Playback/PlayCommand.cs create mode 100644 src/MpcNET/Commands/Playback/PlayPauseCommand.cs create mode 100644 src/MpcNET/Commands/Playback/PreviousCommand.cs create mode 100644 src/MpcNET/Commands/Playback/StopCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/AddCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/AddIdCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/ClearCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/DeleteCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/DeleteIdCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/ListPlaylistCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/ListPlaylistInfoCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/ListPlaylistsCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/LoadCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/PlaylistCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/PlaylistIdCommand.cs create mode 100644 src/MpcNET/Commands/Playlist/PlaylistInfoCommand.cs create mode 100644 src/MpcNET/Commands/Reflection/CommandsCommand.cs create mode 100644 src/MpcNET/Commands/Reflection/DecodersCommand.cs create mode 100644 src/MpcNET/Commands/Reflection/TagTypesCommand.cs create mode 100644 src/MpcNET/Commands/Reflection/UrlHandlersCommand.cs create mode 100644 src/MpcNET/Commands/Status/CurrentSongCommand.cs create mode 100644 src/MpcNET/Commands/Status/StatusCommand.cs rename src/MpcNET/{Commands => }/IMpcCommand.cs (100%) create mode 100644 src/MpcNET/Message/IMpdMessage.cs create mode 100644 src/MpcNET/MpdState.cs create mode 100644 src/MpcNET/Types/IMpdFilePath.cs diff --git a/.gitignore b/.gitignore index c0e64d2..1bc7f48 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ /src/MpcNET.sln.DotSettings.user /src/MpcNET.Test/MpcNET.Test.csproj.DotSettings /src/MpcNET/MpcNET.csproj.DotSettings +/.vs +*.user diff --git a/src/MpcNET.Test/MpcNET.Test.csproj b/src/MpcNET.Test/MpcNET.Test.csproj index bcfcf30..1bcf408 100644 --- a/src/MpcNET.Test/MpcNET.Test.csproj +++ b/src/MpcNET.Test/MpcNET.Test.csproj @@ -1,11 +1,10 @@  - netcoreapp1.0 + netcoreapp2.0 MpcNET.Test MpcNET.Test true - 1.0.4 false false false @@ -25,9 +24,9 @@ - - - + + + diff --git a/src/MpcNET.Test/Tests/DatabaseCommandsTest.cs b/src/MpcNET.Test/Tests/DatabaseCommandsTest.cs index 972af95..2bf52c1 100644 --- a/src/MpcNET.Test/Tests/DatabaseCommandsTest.cs +++ b/src/MpcNET.Test/Tests/DatabaseCommandsTest.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using MpcNET.Commands; using MpcNET.Tags; namespace MpcNET.Test @@ -10,7 +11,7 @@ namespace MpcNET.Test [TestMethod] public async Task ListAllTest() { - var response = await Mpc.SendAsync(new Commands.Database.ListAll()); + var response = await Mpc.SendAsync(Command.Database.ListAll()); TestOutput.WriteLine("ListAllTest Result:"); TestOutput.WriteLine(response); @@ -21,7 +22,7 @@ namespace MpcNET.Test [TestMethod] public async Task FindGenreTest() { - var response = await Mpc.SendAsync(new Commands.Database.Find(MpdTags.Genre, "soundfx")); + var response = await Mpc.SendAsync(Command.Database.Find(MpdTags.Genre, "soundfx")); TestOutput.WriteLine("FindGenreTest Result:"); TestOutput.WriteLine(response); diff --git a/src/MpcNET.Test/Tests/OutputCommandsTest.cs b/src/MpcNET.Test/Tests/OutputCommandsTest.cs index b50b841..944dae2 100644 --- a/src/MpcNET.Test/Tests/OutputCommandsTest.cs +++ b/src/MpcNET.Test/Tests/OutputCommandsTest.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using MpcNET.Commands; namespace MpcNET.Test { @@ -9,10 +10,10 @@ namespace MpcNET.Test [TestMethod] public async Task DisableOutputTest() { - var responseOutputs = await Mpc.SendAsync(new Commands.Output.Outputs()); + var responseOutputs = await Mpc.SendAsync(Command.Output.Outputs()); Assert.IsTrue(responseOutputs.Response.Body.Single(output => output.Id.Equals(0)).IsEnabled); - var response = await Mpc.SendAsync(new Commands.Output.DisableOutput(0)); + var response = await Mpc.SendAsync(Command.Output.DisableOutput(0)); TestOutput.WriteLine("DisableOutputTest Result:"); TestOutput.WriteLine(response); @@ -20,18 +21,18 @@ namespace MpcNET.Test Assert.IsTrue(response.Response.Body.Equals(string.Empty)); Assert.IsTrue(response.Response.State.Status.Equals("OK")); - responseOutputs = await Mpc.SendAsync(new Commands.Output.Outputs()); + responseOutputs = await Mpc.SendAsync(Command.Output.Outputs()); Assert.IsFalse(responseOutputs.Response.Body.Single(output => output.Id.Equals(0)).IsEnabled); } [TestMethod] public async Task EnableOutputTest() { - var responseOutputs = await Mpc.SendAsync(new Commands.Output.Outputs()); + var responseOutputs = await Mpc.SendAsync(Command.Output.Outputs()); // By default should be disable from mpd.config Assert.IsFalse(responseOutputs.Response.Body.Single(output => output.Id.Equals(1)).IsEnabled); - var response = await Mpc.SendAsync(new Commands.Output.EnableOutput(1)); + var response = await Mpc.SendAsync(Command.Output.EnableOutput(1)); TestOutput.WriteLine("EnableOutputTest Result:"); TestOutput.WriteLine(response); @@ -39,17 +40,17 @@ namespace MpcNET.Test Assert.IsTrue(response.Response.Body.Equals(string.Empty)); Assert.IsTrue(response.Response.State.Status.Equals("OK")); - responseOutputs = await Mpc.SendAsync(new Commands.Output.Outputs()); + responseOutputs = await Mpc.SendAsync(Command.Output.Outputs()); Assert.IsTrue(responseOutputs.Response.Body.Single(output => output.Id.Equals(1)).IsEnabled); } [TestMethod] public async Task ToggleOutputTest() { - var responseOutputs = await Mpc.SendAsync(new Commands.Output.Outputs()); + var responseOutputs = await Mpc.SendAsync(Command.Output.Outputs()); Assert.IsTrue(responseOutputs.Response.Body.Single(output => output.Id.Equals(2)).IsEnabled); - var response = await Mpc.SendAsync(new Commands.Output.ToggleOutput(2)); + var response = await Mpc.SendAsync(Command.Output.ToggleOutput(2)); TestOutput.WriteLine("ToggleOutputTest Result:"); TestOutput.WriteLine(response); @@ -57,14 +58,14 @@ namespace MpcNET.Test Assert.IsTrue(response.Response.Body.Equals(string.Empty)); Assert.IsTrue(response.Response.State.Status.Equals("OK")); - responseOutputs = await Mpc.SendAsync(new Commands.Output.Outputs()); + responseOutputs = await Mpc.SendAsync(Command.Output.Outputs()); Assert.IsFalse(responseOutputs.Response.Body.Single(output => output.Id.Equals(2)).IsEnabled); } [TestMethod] public async Task LisOutputsTest() { - var response = await Mpc.SendAsync(new Commands.Output.Outputs()); + var response = await Mpc.SendAsync(Command.Output.Outputs()); TestOutput.WriteLine("LisOutputsTest Result:"); TestOutput.WriteLine(response); diff --git a/src/MpcNET.Test/Tests/PlaylistsCommandsTest.cs b/src/MpcNET.Test/Tests/PlaylistsCommandsTest.cs index 14ce0a0..6e1d910 100644 --- a/src/MpcNET.Test/Tests/PlaylistsCommandsTest.cs +++ b/src/MpcNET.Test/Tests/PlaylistsCommandsTest.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using MpcNET.Commands; namespace MpcNET.Test { @@ -12,7 +13,7 @@ namespace MpcNET.Test [DataRow("_My Playlist", 5)] public async Task ListPlaylistTest(string playlistName, int numberOfFiles) { - var response = await Mpc.SendAsync(Commands.Playlists.Stored.GetContent(playlistName)); + var response = await Mpc.SendAsync(Command.Playlists.Stored.GetContent(playlistName)); TestOutput.WriteLine($"ListPlaylistTest (playlistName: {playlistName}) Result:"); TestOutput.WriteLine(response); @@ -26,7 +27,7 @@ namespace MpcNET.Test [DataRow("_My Playlist", 5)] public async Task ListPlaylistInfoTest(string playlistName, int numberOfFiles) { - var response = await Mpc.SendAsync(Commands.Playlists.Stored.GetContentWithMetadata(playlistName)); + var response = await Mpc.SendAsync(Command.Playlists.Stored.GetContentWithMetadata(playlistName)); TestOutput.WriteLine($"ListPlaylistTest (playlistName: {playlistName}) Result:"); TestOutput.WriteLine(response); @@ -40,7 +41,7 @@ namespace MpcNET.Test [TestMethod] public async Task ListPlaylistsTest() { - var response = await Mpc.SendAsync(Commands.Playlists.Stored.GetAll()); + var response = await Mpc.SendAsync(Command.Playlists.Stored.GetAll()); TestOutput.WriteLine($"ListPlaylistsTest Result:"); TestOutput.WriteLine(response); @@ -116,57 +117,57 @@ namespace MpcNET.Test private async Task Check_Empty_Queue() { - var message = await Mpc.SendAsync(Commands.Playlists.Current.GetAllSongsInfo()); + var message = await Mpc.SendAsync(Command.Playlists.Current.GetAllSongsInfo()); Assert.IsTrue(message.HasSuccessResponse()); Assert.IsFalse(message.Response.Body.Any()); } private async Task Load_Playlist(string playlistName) { - var message = await Mpc.SendAsync(Commands.Playlists.Stored.Load(playlistName)); + var message = await Mpc.SendAsync(Command.Playlists.Stored.Load(playlistName)); Assert.IsTrue(message.HasSuccessResponse()); } private async Task Clear_Queue() { - var message = await Mpc.SendAsync(Commands.Playlists.Current.Clear()); + var message = await Mpc.SendAsync(Command.Playlists.Current.Clear()); Assert.IsTrue(message.HasSuccessResponse()); } private async Task Check_Queue_HasSongs(int nrOfSongs) { - var message = await Mpc.SendAsync(Commands.Playlists.Current.GetAllSongsInfo()); + var message = await Mpc.SendAsync(Command.Playlists.Current.GetAllSongsInfo()); Assert.IsTrue(message.HasSuccessResponse()); Assert.IsTrue(message.Response.Body.Count() == nrOfSongs); } private async Task Add_Directory(string directory) { - var message = await Mpc.SendAsync(Commands.Playlists.Current.AddDirectory(directory)); + var message = await Mpc.SendAsync(Command.Playlists.Current.AddDirectory(directory)); Assert.IsTrue(message.HasSuccessResponse()); } private async Task Add_File(string file) { - var message = await Mpc.SendAsync(Commands.Playlists.Current.AddSong(file)); + var message = await Mpc.SendAsync(Command.Playlists.Current.AddSong(file)); Assert.IsTrue(message.HasSuccessResponse()); } private async Task Remove_Position(int position) { - var message = await Mpc.SendAsync(Commands.Playlists.Current.RemoveSongByPosition(position)); + var message = await Mpc.SendAsync(Command.Playlists.Current.RemoveSongByPosition(position)); Assert.IsTrue(message.HasSuccessResponse()); } private async Task Remove_Id(int songId) { - var message = await Mpc.SendAsync(Commands.Playlists.Current.RemoveSongById(songId)); + var message = await Mpc.SendAsync(Command.Playlists.Current.RemoveSongById(songId)); Assert.IsTrue(message.HasSuccessResponse()); } private async Task Get_Song_Id() { - var message = await Mpc.SendAsync(Commands.Playlists.Current.GetAllSongMetadata()); + var message = await Mpc.SendAsync(Command.Playlists.Current.GetAllSongMetadata()); return message.Response.Body.Single().Id; } } diff --git a/src/MpcNET.Test/Tests/ReflectionCommandsTest.cs b/src/MpcNET.Test/Tests/ReflectionCommandsTest.cs index 169b91b..481b08d 100644 --- a/src/MpcNET.Test/Tests/ReflectionCommandsTest.cs +++ b/src/MpcNET.Test/Tests/ReflectionCommandsTest.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; +using MpcNET.Commands; namespace MpcNET.Test { @@ -9,7 +10,7 @@ namespace MpcNET.Test [TestMethod] public async Task CommandsTest() { - var response = await Mpc.SendAsync(new Commands.Reflection.Commands()); + var response = await Mpc.SendAsync(Command.Reflection.Commands()); TestOutput.WriteLine($"CommandsTest (commands: {response.Response.Body.Count()}) Result:"); TestOutput.WriteLine(response); @@ -27,7 +28,7 @@ namespace MpcNET.Test [TestMethod] public async Task TagTypesTest() { - var response = await Mpc.SendAsync(new Commands.Reflection.TagTypes()); + var response = await Mpc.SendAsync(Command.Reflection.TagTypes()); TestOutput.WriteLine("TagTypesTest Result:"); TestOutput.WriteLine(response); @@ -38,7 +39,7 @@ namespace MpcNET.Test [TestMethod] public async Task UrlHandlersTest() { - var response = await Mpc.SendAsync(new Commands.Reflection.UrlHandlers()); + var response = await Mpc.SendAsync(Command.Reflection.UrlHandlers()); TestOutput.WriteLine($"UrlHandlersTest (handlers: {response.Response.Body.Count()}) Result:"); TestOutput.WriteLine(response); @@ -54,7 +55,7 @@ namespace MpcNET.Test [TestMethod] public async Task DecodersTest() { - var response = await Mpc.SendAsync(new Commands.Reflection.Decoders()); + var response = await Mpc.SendAsync(Command.Reflection.Decoders()); TestOutput.WriteLine($"DecodersTest (decoders: {response.Response.Body.Count()}) Result:"); TestOutput.WriteLine(response); diff --git a/src/MpcNET/Commands/Commands.Database.cs b/src/MpcNET/Commands/Commands.Database.cs index f5c5a22..23a93b1 100644 --- a/src/MpcNET/Commands/Commands.Database.cs +++ b/src/MpcNET/Commands/Commands.Database.cs @@ -1,127 +1,33 @@ using System.Collections.Generic; -using System.Linq; +using MpcNET.Commands.Database; using MpcNET.Tags; using MpcNET.Types; -namespace MpcNET +namespace MpcNET.Commands { - public static partial class Commands + public static partial class Command { /// /// https://www.musicpd.org/doc/protocol/database.html /// - public class Database + public static class Database { + public static IMpcCommand> Find(ITag tag, string searchText) + { + return new FindCommand(tag, searchText); + } + + public static IMpcCommand Update(string uri = null) + { + return new UpdateCommand(uri); + } + // TODO: count - - /// - /// Finds songs in the database that is exactly "searchText". - /// - public class Find : IMpcCommand> - { - private readonly ITag _tag; - private readonly string _searchText; - - public Find(ITag tag, string searchText) - { - _tag = tag; - _searchText = searchText; - } - - public string Value => string.Join(" ", "find", _tag.Value, _searchText); - - public IEnumerable FormatResponse(IList> response) - { - var results = new List(); - - foreach (var line in response) - { - if (line.Key.Equals("file")) - { - results.Add(new MpdFile(line.Value)); - } - else - { - results.Last().AddTag(line.Key, line.Value); - } - } - - return results; - } - } - - public class List : IMpcCommand - { - private readonly ITag _tag; - - public List(ITag tag) - { - _tag = tag; - } - - public string Value => string.Join(" ", "list", _tag); - - public string FormatResponse(IList> response) - { - // TODO: - return response.ToString(); - } - } - - // TODO: findadd - - /// - /// Lists all songs and directories in URI. - /// - public class ListAll : IMpcCommand> - { - public string Value => "listall"; - - public IEnumerable FormatResponse(IList> response) - { - var rootDirectory = new List - { - new MpdDirectory("/") // Add by default the root directory - }; - - foreach (var line in response) - { - if (line.Key.Equals("file")) - { - rootDirectory.Last().AddFile(line.Value); - } - - if (line.Key.Equals("directory")) - { - rootDirectory.Add(new MpdDirectory(line.Value)); - } - } - - return rootDirectory; - } - } - - // TODO: listallinfo - // TODO: listfiles - // TODO: lsinfo - // TODO: readcomments - // TODO: search - // TODO: searchadd - // TODO: searchaddpl - - public class Update : IMpcCommand - { - // TODO: Extend command: < update [URI] > - public string Value => "update"; - - public string FormatResponse(IList> response) - { - // TODO: - return response.ToString(); - } - } - // TODO: rescan + public static IMpcCommand> ListAll() + { + return new ListAllCommand(); + } } } } \ No newline at end of file diff --git a/src/MpcNET/Commands/Commands.Output.cs b/src/MpcNET/Commands/Commands.Output.cs index 865cbc7..595c157 100644 --- a/src/MpcNET/Commands/Commands.Output.cs +++ b/src/MpcNET/Commands/Commands.Output.cs @@ -1,100 +1,34 @@ using System.Collections.Generic; +using MpcNET.Commands.Output; using MpcNET.Types; -namespace MpcNET +namespace MpcNET.Commands { - public static partial class Commands + public static partial class Command { /// /// https://www.musicpd.org/doc/protocol/output_commands.html /// public class Output { - /// - /// Turns an output off. - /// - public class DisableOutput : IMpcCommand + public static IMpcCommand> Outputs() { - private readonly int _outputId; - - public DisableOutput(int outputId) - { - _outputId = outputId; - } - - public string Value => string.Join(" ", "disableoutput", _outputId); - - public string FormatResponse(IList> response) - { - // Response should be empty. - return string.Join(", ", response); - } + return new OutputsCommand(); } - /// - /// Turns an output on. - /// - public class EnableOutput : IMpcCommand + public static IMpcCommand DisableOutput(int outputId) { - private readonly int _outputId; - - public EnableOutput(int outputId) - { - _outputId = outputId; - } - - public string Value => string.Join(" ", "enableoutput", _outputId); - - public string FormatResponse(IList> response) - { - // Response should be empty. - return string.Join(", ", response); - } + return new DisableOutputCommand(outputId); } - /// - /// Turns an output on or off, depending on the current state. - /// - public class ToggleOutput : IMpcCommand + public static IMpcCommand EnableOutput(int outputId) { - private readonly int _outputId; - - public ToggleOutput(int outputId) - { - _outputId = outputId; - } - - public string Value => string.Join(" ", "toggleoutput", _outputId); - - public string FormatResponse(IList> response) - { - // Response should be empty. - return string.Join(", ", response); - } + return new EnableOutputCommand(outputId); } - /// - /// Shows information about all outputs. - /// - public class Outputs : IMpcCommand> + public static IMpcCommand ToggleOutput(int outputId) { - public string Value => "outputs"; - - public IEnumerable FormatResponse(IList> response) - { - var result = new List(); - - for (var i = 0; i < response.Count; i += 3) - { - var outputId = int.Parse(response[i].Value); - var outputName = response[i + 1].Value; - var outputEnabled = response[i + 2].Value == "1"; - - result.Add(new MpdOutput(outputId, outputName, outputEnabled)); - } - - return result; - } + return new ToggleOutputCommand(outputId); } } } diff --git a/src/MpcNET/Commands/Commands.Playback.cs b/src/MpcNET/Commands/Commands.Playback.cs new file mode 100644 index 0000000..c0a04b9 --- /dev/null +++ b/src/MpcNET/Commands/Commands.Playback.cs @@ -0,0 +1,24 @@ +using MpcNET.Commands.Playback; +using MpcNET.Types; + +namespace MpcNET.Commands +{ + public partial class Command + { + /// + /// https://www.musicpd.org/doc/protocol/playback_commands.html + /// + public static class Playback + { + public static IMpcCommand Next() => new NextCommand(); + + public static IMpcCommand Previous() => new PreviousCommand(); + + public static IMpcCommand PlayPause() => new PlayPauseCommand(); + + public static IMpcCommand Play(IMpdFile mpdFile) => new PlayCommand(mpdFile); + + public static IMpcCommand Stop() => new StopCommand(); + } + } +} diff --git a/src/MpcNET/Commands/Commands.Playlists.cs b/src/MpcNET/Commands/Commands.Playlists.cs index b48d6f0..aa138a7 100644 --- a/src/MpcNET/Commands/Commands.Playlists.cs +++ b/src/MpcNET/Commands/Commands.Playlists.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; -using System.Linq; +using MpcNET.Commands.Playlist; using MpcNET.Types; -namespace MpcNET +namespace MpcNET.Commands { - public static partial class Commands + public static partial class Command { public static class Playlists { @@ -13,214 +13,45 @@ namespace MpcNET /// public static class Current { - /// - /// Adds the file URI to the playlist (directories add recursively). URI can also be a single file. - /// - private class AddImpl : IMpcCommand - { - private readonly string _uri; - - public AddImpl(string uri) - { - _uri = uri; - } - - public string Value => string.Join(" ", "add", $"\"{_uri}\""); - - public string FormatResponse(IList> response) - { - return string.Join(", ", response); - } - } - /// /// Command: add /// - public static IMpcCommand AddDirectory(string directory) { return new AddImpl(directory); } - - /// - /// Adds a song to the playlist (non-recursive) and returns the song id. - /// - private class AddIdImpl : IMpcCommand - { - private readonly string _uri; - - public AddIdImpl(string uri) - { - _uri = uri; - } - - public string Value => string.Join(" ", "addid", $"\"{_uri}\""); - - public string FormatResponse(IList> response) - { - return string.Join(", ", response); - } - } + public static IMpcCommand AddDirectory(string directory) { return new AddCommand(directory); } /// /// Command: addid /// - public static IMpcCommand AddSong(string songPath) { return new AddIdImpl(songPath); } - - /// - /// Clears the current playlist. - /// - private class ClearImpl : IMpcCommand - { - public string Value => "clear"; - - public string FormatResponse(IList> response) - { - return string.Join(", ", response); - } - } + public static IMpcCommand AddSong(string songPath) { return new AddIdCommand(songPath); } /// /// Command: clear /// - public static IMpcCommand Clear() { return new ClearImpl(); } - - /// - /// Displays the current playlist. - /// - private class PlaylistImpl : IMpcCommand> - { - public string Value => "playlist"; - - public IEnumerable FormatResponse(IList> response) - { - var results = response.Select(line => new MpdFile(line.Value) { Pos = int.Parse(line.Key) }); - - return results; - } - } + public static IMpcCommand Clear() { return new ClearCommand(); } /// /// Command: playlist /// - public static IMpcCommand> GetAllSongsInfo() { return new PlaylistImpl(); } - - /// - /// Deletes a song from the playlist. - /// - private class DeleteImpl : IMpcCommand - { - private readonly int _position; - - public DeleteImpl(int position) - { - _position = position; - } - - public string Value => string.Join(" ", "delete", _position); - - public string FormatResponse(IList> response) - { - return string.Join(", ", response); - } - } + public static IMpcCommand> GetAllSongsInfo() { return new PlaylistCommand(); } /// /// Command: delete /// - public static IMpcCommand RemoveSongByPosition(int position) { return new DeleteImpl(position); } - - /// - /// Deletes the song SONGID from the playlist - /// - private class DeleteIdImpl : IMpcCommand - { - private readonly int _songId; - - public DeleteIdImpl(int songId) - { - _songId = songId; - } - - public string Value => string.Join(" ", "deleteid", _songId); - - public string FormatResponse(IList> response) - { - return string.Join(", ", response); - } - } + public static IMpcCommand RemoveSongByPosition(int position) { return new DeleteCommand(position); } /// /// Command: deleteid /// - public static IMpcCommand RemoveSongById(int songId) { return new DeleteIdImpl(songId); } - - /// - /// Displays song ID in the playlist. - /// - private class PlaylistIdImpl : IMpcCommand> - { - private readonly int _songId; - - public PlaylistIdImpl(int songId) - { - _songId = songId; - } - - public string Value => string.Join(" ", "playlistid", _songId); - - public IEnumerable FormatResponse(IList> response) - { - var results = new List(); - - foreach (var line in response) - { - if (line.Key.Equals("file")) - { - results.Add(new MpdFile(line.Value)); - } - else - { - results.Last().AddTag(line.Key, line.Value); - } - } - - return results; - } - } + public static IMpcCommand RemoveSongById(int songId) { return new DeleteIdCommand(songId); } /// /// Command: playlistid /// - public static IMpcCommand> GetSongMetadata(int songId) { return new PlaylistIdImpl(songId); } - - /// - /// Displays a list of all songs in the playlist, - /// - private class PlaylistInfoImpl : IMpcCommand> - { - public string Value => "playlistinfo"; - - public IEnumerable FormatResponse(IList> response) - { - var results = new List(); - - foreach (var line in response) - { - if (line.Key.Equals("file")) - { - results.Add(new MpdFile(line.Value)); - } - else - { - results.Last().AddTag(line.Key, line.Value); - } - } - - return results; - } - } + public static IMpcCommand> GetSongMetadata(int songId) { return new PlaylistIdCommand(songId); } /// /// Command: playlistinfo /// - public static IMpcCommand> GetAllSongMetadata() { return new PlaylistInfoImpl(); } + public static IMpcCommand> GetAllSongMetadata() { return new PlaylistInfoCommand(); } } /// @@ -228,128 +59,26 @@ namespace MpcNET /// public static class Stored { - /// - /// Loads the playlist into the current queue. - /// - private class LoadImpl : IMpcCommand - { - private readonly string _playlistName; - - public LoadImpl(string playlistName) - { - _playlistName = playlistName; - } - - public string Value => string.Join(" ", "load", $"\"{_playlistName}\""); - - public string FormatResponse(IList> response) - { - return string.Join(", ", response); - } - } /// /// Command: load /// - public static IMpcCommand Load(string playlistName) { return new LoadImpl(playlistName); } - - /// - /// Lists the songs in the playlist. - /// - private class ListPlaylistImpl : IMpcCommand> - { - private readonly string _playlistName; - - public ListPlaylistImpl(string playlistName) - { - _playlistName = playlistName; - } - - public string Value => string.Join(" ", "listplaylist", $"\"{_playlistName}\""); - - public IEnumerable FormatResponse(IList> response) - { - var results = response.Where(line => line.Key.Equals("file")).Select(line => new MpdFile(line.Value)); - - return results; - } - } + public static IMpcCommand Load(string playlistName) { return new LoadCommand(playlistName); } /// /// Command: listplaylist /// - public static IMpcCommand> GetContent(string playlistName) { return new ListPlaylistImpl(playlistName); } - - /// - /// Lists the songs with metadata in the playlist. - /// - private class ListPlaylistInfoImpl : IMpcCommand> - { - private readonly string _playlistName; - - public ListPlaylistInfoImpl(string playlistName) - { - _playlistName = playlistName; - } - - public string Value => string.Join(" ", "listplaylistinfo", $"\"{_playlistName}\""); - - public IEnumerable FormatResponse(IList> response) - { - var results = new List(); - - foreach (var line in response) - { - if (line.Key.Equals("file")) - { - results.Add(new MpdFile(line.Value)); - } - else - { - results.Last().AddTag(line.Key, line.Value); - } - } - - return results; - } - } + public static IMpcCommand> GetContent(string playlistName) { return new ListPlaylistCommand(playlistName); } /// /// Command: listplaylistinfo /// - public static IMpcCommand> GetContentWithMetadata(string playlistName) { return new ListPlaylistInfoImpl(playlistName); } - - /// - /// Prints a list of the playlist directory. - /// - private class ListPlaylistsImpl : IMpcCommand> - { - public string Value => "listplaylists"; - - public IEnumerable FormatResponse(IList> response) - { - var result = new List(); - - foreach (var line in response) - { - if (line.Key.Equals("playlist")) - { - result.Add(new MpdPlaylist(line.Value)); - } - else if (line.Key.Equals("Last-Modified")) - { - result.Last().AddLastModified(line.Value); - } - } - - return result; - } - } + public static IMpcCommand> GetContentWithMetadata(string playlistName) { return new ListPlaylistInfoCommand(playlistName); } /// /// Command: listplaylists /// - public static IMpcCommand> GetAll() { return new ListPlaylistsImpl(); } + public static IMpcCommand> GetAll() { return new ListPlaylistsCommand(); } } } } diff --git a/src/MpcNET/Commands/Commands.Reflection.cs b/src/MpcNET/Commands/Commands.Reflection.cs index bb6a522..d8c3257 100644 --- a/src/MpcNET/Commands/Commands.Reflection.cs +++ b/src/MpcNET/Commands/Commands.Reflection.cs @@ -1,102 +1,34 @@ using System.Collections.Generic; -using System.Linq; +using MpcNET.Commands.Reflection; using MpcNET.Types; -namespace MpcNET +namespace MpcNET.Commands { - public static partial class Commands + public static partial class Command { /// /// https://www.musicpd.org/doc/protocol/reflection_commands.html /// public static class Reflection { - // config : This command is only permitted to "local" clients (connected via UNIX domain socket). - - /// - /// Shows which commands the current user has access to. - /// - public class Commands : IMpcCommand> + public static IMpcCommand> Commands() { - public string Value => "commands"; - - public IEnumerable FormatResponse(IList> response) - { - var result = response.Where(item => item.Key.Equals("command")).Select(item => item.Value); - - return result; - } + return new CommandsCommand(); } - // TODO: notcommands : Shows which commands the current user does not have access to. - - /// - /// Shows a list of available song metadata. - /// - public class TagTypes : IMpcCommand> + public static IMpcCommand> TagTypes() { - public string Value => "tagtypes"; - - public IEnumerable FormatResponse(IList> response) - { - var result = response.Where(item => item.Key.Equals("tagtype")).Select(item => item.Value); - - return result; - } + return new TagTypesCommand(); } - /// - /// Gets a list of available URL handlers. - /// - public class UrlHandlers : IMpcCommand> + public static IMpcCommand> UrlHandlers() { - public string Value => "urlhandlers"; - - public IEnumerable FormatResponse(IList> response) - { - var result = response.Where(item => item.Key.Equals("handler")).Select(item => item.Value); - - return result; - } + return new UrlHandlersCommand(); } - /// - /// Print a list of decoder plugins, followed by their supported suffixes and MIME types. - /// - public class Decoders : IMpcCommand> + public static IMpcCommand> Decoders() { - public string Value => "decoders"; - - public IEnumerable FormatResponse(IList> response) - { - var result = new List(); - - var mpdDecoderPlugin = MpdDecoderPlugin.Empty; - foreach (var line in response) - { - if (line.Key.Equals("plugin")) - { - if (mpdDecoderPlugin.IsInitialized) - { - result.Add(mpdDecoderPlugin); - } - - mpdDecoderPlugin = new MpdDecoderPlugin(line.Value); - } - - if (line.Key.Equals("suffix") && mpdDecoderPlugin.IsInitialized) - { - mpdDecoderPlugin.AddSuffix(line.Value); - } - - if (line.Key.Equals("mime_type") && mpdDecoderPlugin.IsInitialized) - { - mpdDecoderPlugin.AddMediaType(line.Value); - } - } - - return result; - } + return new DecodersCommand(); } } } diff --git a/src/MpcNET/Commands/Commands.Status.cs b/src/MpcNET/Commands/Commands.Status.cs new file mode 100644 index 0000000..aaa74b8 --- /dev/null +++ b/src/MpcNET/Commands/Commands.Status.cs @@ -0,0 +1,24 @@ +using MpcNET.Commands.Status; +using MpcNET.Types; + +namespace MpcNET.Commands +{ + public static partial class Command + { + /// + /// https://www.musicpd.org/doc/protocol/command_reference.html#status_commands + /// + public static class Status + { + public static IMpcCommand GetStatus() + { + return new StatusCommand(); + } + + public static IMpcCommand GetCurrentSong() + { + return new CurrentSongCommand(); + } + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Database/FindCommand.cs b/src/MpcNET/Commands/Database/FindCommand.cs new file mode 100644 index 0000000..c4974c8 --- /dev/null +++ b/src/MpcNET/Commands/Database/FindCommand.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using MpcNET.Tags; +using MpcNET.Types; + +namespace MpcNET.Commands.Database +{ + /// + /// Finds songs in the database that is exactly "searchText". + /// + internal class FindCommand : IMpcCommand> + { + private readonly ITag _tag; + private readonly string _searchText; + + public FindCommand(ITag tag, string searchText) + { + _tag = tag; + _searchText = searchText; + } + + public string Value => string.Join(" ", "find", _tag.Value, _searchText); + + public IEnumerable FormatResponse(IList> response) + { + return MpdFile.CreateList(response); + } + } + + // TODO: rescan +} diff --git a/src/MpcNET/Commands/Database/ListAllCommand.cs b/src/MpcNET/Commands/Database/ListAllCommand.cs new file mode 100644 index 0000000..727171b --- /dev/null +++ b/src/MpcNET/Commands/Database/ListAllCommand.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using MpcNET.Types; + +namespace MpcNET.Commands.Database +{ + + /// + /// Lists all songs and directories in URI. + /// + internal class ListAllCommand : IMpcCommand> + { + public string Value => "listall"; + + public IEnumerable FormatResponse(IList> response) + { + var rootDirectory = new List + { + new MpdDirectory("/") // Add by default the root directory + }; + + foreach (var line in response) + { + if (line.Key.Equals("file")) + { + rootDirectory.Last().AddFile(line.Value); + } + + if (line.Key.Equals("directory")) + { + rootDirectory.Add(new MpdDirectory(line.Value)); + } + } + + return rootDirectory; + } + } + + // TODO: findadd + // TODO: rescan +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Database/ListCommand.cs b/src/MpcNET/Commands/Database/ListCommand.cs new file mode 100644 index 0000000..6301773 --- /dev/null +++ b/src/MpcNET/Commands/Database/ListCommand.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using MpcNET.Tags; + +namespace MpcNET.Commands.Database +{ + internal class ListCommand : IMpcCommand + { + private readonly ITag _tag; + + public ListCommand(ITag tag) + { + _tag = tag; + } + + public string Value => string.Join(" ", "list", _tag); + + public string FormatResponse(IList> response) + { + // TODO: + return response.ToString(); + } + } + + // TODO: rescan +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Database/UpdateCommand.cs b/src/MpcNET/Commands/Database/UpdateCommand.cs new file mode 100644 index 0000000..ad676d2 --- /dev/null +++ b/src/MpcNET/Commands/Database/UpdateCommand.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Database +{ + // TODO: listallinfo + // TODO: listfiles + // TODO: lsinfo + // TODO: readcomments + // TODO: search + // TODO: searchadd + // TODO: searchaddpl + + public class UpdateCommand : IMpcCommand + { + private readonly string uri; + + public UpdateCommand(string uri) + { + this.uri = uri; + } + + // TODO: Extend command: < update [URI] > + public string Value + { + get + { + if (string.IsNullOrEmpty(this.uri)) + { + return "update"; + } + + var newUri = this.uri; + if (this.uri.StartsWith(@"""")) + { + newUri = @"""" + this.uri; + } + + if (this.uri.EndsWith(@"""")) + { + newUri = this.uri + @""""; + } + + return string.Join(" ", "update", newUri); + } + } + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } + + // TODO: rescan +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Output/DisableOutputCommand.cs b/src/MpcNET/Commands/Output/DisableOutputCommand.cs new file mode 100644 index 0000000..5bcca23 --- /dev/null +++ b/src/MpcNET/Commands/Output/DisableOutputCommand.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Output +{ + /// + /// Turns an output off. + /// + public class DisableOutputCommand : IMpcCommand + { + private readonly int outputId; + + public DisableOutputCommand(int outputId) + { + this.outputId = outputId; + } + + public string Value => string.Join(" ", "disableoutput", this.outputId); + + public string FormatResponse(IList> response) + { + // Response should be empty. + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Output/EnableOutputCommand.cs b/src/MpcNET/Commands/Output/EnableOutputCommand.cs new file mode 100644 index 0000000..bffdf1f --- /dev/null +++ b/src/MpcNET/Commands/Output/EnableOutputCommand.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Output +{ + /// + /// Turns an output on. + /// + internal class EnableOutputCommand : IMpcCommand + { + private readonly int _outputId; + + public EnableOutputCommand(int outputId) + { + _outputId = outputId; + } + + public string Value => string.Join(" ", "enableoutput", _outputId); + + public string FormatResponse(IList> response) + { + // Response should be empty. + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Output/OutputsCommand.cs b/src/MpcNET/Commands/Output/OutputsCommand.cs new file mode 100644 index 0000000..41b9e6d --- /dev/null +++ b/src/MpcNET/Commands/Output/OutputsCommand.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using MpcNET.Types; + +namespace MpcNET.Commands.Output +{ + /// + /// Shows information about all outputs. + /// + public class OutputsCommand : IMpcCommand> + { + public string Value => "outputs"; + + public IEnumerable FormatResponse(IList> response) + { + var result = new List(); + + for (var i = 0; i < response.Count; i += 3) + { + var outputId = int.Parse(response[i].Value); + var outputName = response[i + 1].Value; + var outputEnabled = response[i + 2].Value == "1"; + + result.Add(new MpdOutput(outputId, outputName, outputEnabled)); + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Output/ToggleOutputCommand.cs b/src/MpcNET/Commands/Output/ToggleOutputCommand.cs new file mode 100644 index 0000000..f4092e5 --- /dev/null +++ b/src/MpcNET/Commands/Output/ToggleOutputCommand.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Output +{ + /// + /// Turns an output on or off, depending on the current state. + /// + internal class ToggleOutputCommand : IMpcCommand + { + private readonly int outputId; + + public ToggleOutputCommand(int outputId) + { + this.outputId = outputId; + } + + public string Value => string.Join(" ", "toggleoutput", outputId); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playback/NextCommand.cs b/src/MpcNET/Commands/Playback/NextCommand.cs new file mode 100644 index 0000000..4ad35b6 --- /dev/null +++ b/src/MpcNET/Commands/Playback/NextCommand.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playback +{ + internal class NextCommand : IMpcCommand + { + public string Value => string.Join(" ", "next"); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} diff --git a/src/MpcNET/Commands/Playback/PlayCommand.cs b/src/MpcNET/Commands/Playback/PlayCommand.cs new file mode 100644 index 0000000..f2fd397 --- /dev/null +++ b/src/MpcNET/Commands/Playback/PlayCommand.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using MpcNET.Types; + +namespace MpcNET.Commands.Playback +{ + internal class PlayCommand : IMpcCommand + { + private readonly IMpdFile mpdFile; + + public PlayCommand(IMpdFile mpdFile) + { + this.mpdFile = mpdFile; + } + + public string Value + { + get + { + if (this.mpdFile.HasId) + { + return string.Join(" ", "playid", this.mpdFile.Id); + } + + return string.Join(" ", "play", this.mpdFile.Pos); + } + } + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playback/PlayPauseCommand.cs b/src/MpcNET/Commands/Playback/PlayPauseCommand.cs new file mode 100644 index 0000000..608fcd5 --- /dev/null +++ b/src/MpcNET/Commands/Playback/PlayPauseCommand.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playback +{ + internal class PlayPauseCommand : IMpcCommand + { + public string Value => string.Join(" ", "pause"); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playback/PreviousCommand.cs b/src/MpcNET/Commands/Playback/PreviousCommand.cs new file mode 100644 index 0000000..eca70ae --- /dev/null +++ b/src/MpcNET/Commands/Playback/PreviousCommand.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playback +{ + internal class PreviousCommand : IMpcCommand + { + public string Value => string.Join(" ", "previous"); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playback/StopCommand.cs b/src/MpcNET/Commands/Playback/StopCommand.cs new file mode 100644 index 0000000..71105a6 --- /dev/null +++ b/src/MpcNET/Commands/Playback/StopCommand.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playback +{ + internal class StopCommand : IMpcCommand + { + public string Value => string.Join(" ", "stop"); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/AddCommand.cs b/src/MpcNET/Commands/Playlist/AddCommand.cs new file mode 100644 index 0000000..3a7d220 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/AddCommand.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Adds the file URI to the playlist (directories add recursively). URI can also be a single file. + /// + internal class AddCommand : IMpcCommand + { + private readonly string _uri; + + public AddCommand(string uri) + { + _uri = uri; + } + + public string Value => string.Join(" ", "add", $"\"{_uri}\""); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} diff --git a/src/MpcNET/Commands/Playlist/AddIdCommand.cs b/src/MpcNET/Commands/Playlist/AddIdCommand.cs new file mode 100644 index 0000000..522bfb6 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/AddIdCommand.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Adds a song to the playlist (non-recursive) and returns the song id. + /// + internal class AddIdCommand : IMpcCommand + { + private readonly string _uri; + + public AddIdCommand(string uri) + { + _uri = uri; + } + + public string Value => string.Join(" ", "addid", $"\"{_uri}\""); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/ClearCommand.cs b/src/MpcNET/Commands/Playlist/ClearCommand.cs new file mode 100644 index 0000000..17ffa94 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/ClearCommand.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Clears the current playlist. + /// + internal class ClearCommand : IMpcCommand + { + public string Value => "clear"; + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/DeleteCommand.cs b/src/MpcNET/Commands/Playlist/DeleteCommand.cs new file mode 100644 index 0000000..3dac0c6 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/DeleteCommand.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Deletes a song from the playlist. + /// + internal class DeleteCommand : IMpcCommand + { + private readonly int position; + + public DeleteCommand(int position) + { + this.position = position; + } + + public string Value => string.Join(" ", "delete", this.position); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/DeleteIdCommand.cs b/src/MpcNET/Commands/Playlist/DeleteIdCommand.cs new file mode 100644 index 0000000..19a3048 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/DeleteIdCommand.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Deletes the song SONGID from the playlist + /// + internal class DeleteIdCommand : IMpcCommand + { + private readonly int songId; + + public DeleteIdCommand(int songId) + { + this.songId = songId; + } + + public string Value => string.Join(" ", "deleteid", this.songId); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/ListPlaylistCommand.cs b/src/MpcNET/Commands/Playlist/ListPlaylistCommand.cs new file mode 100644 index 0000000..79a1c88 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/ListPlaylistCommand.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using MpcNET.Types; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Lists the songs in the playlist. + /// + internal class ListPlaylistCommand : IMpcCommand> + { + private readonly string _playlistName; + + public ListPlaylistCommand(string playlistName) + { + _playlistName = playlistName; + } + + public string Value => string.Join(" ", "listplaylist", $"\"{_playlistName}\""); + + public IEnumerable FormatResponse(IList> response) + { + var results = response.Where(line => line.Key.Equals("file")).Select(line => new MpdFile(line.Value)); + + return results; + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/ListPlaylistInfoCommand.cs b/src/MpcNET/Commands/Playlist/ListPlaylistInfoCommand.cs new file mode 100644 index 0000000..583162b --- /dev/null +++ b/src/MpcNET/Commands/Playlist/ListPlaylistInfoCommand.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using MpcNET.Types; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Lists the songs with metadata in the playlist. + /// + internal class ListPlaylistInfoCommand : IMpcCommand> + { + private readonly string _playlistName; + + public ListPlaylistInfoCommand(string playlistName) + { + _playlistName = playlistName; + } + + public string Value => string.Join(" ", "listplaylistinfo", $"\"{_playlistName}\""); + + public IEnumerable FormatResponse(IList> response) + { + return MpdFile.CreateList(response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/ListPlaylistsCommand.cs b/src/MpcNET/Commands/Playlist/ListPlaylistsCommand.cs new file mode 100644 index 0000000..93b7465 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/ListPlaylistsCommand.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using MpcNET.Types; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Prints a list of the playlist directory. + /// + internal class ListPlaylistsCommand : IMpcCommand> + { + public string Value => "listplaylists"; + + public IEnumerable FormatResponse(IList> response) + { + var result = new List(); + + foreach (var line in response) + { + if (line.Key.Equals("playlist")) + { + result.Add(new MpdPlaylist(line.Value)); + } + else if (line.Key.Equals("Last-Modified")) + { + result.Last().AddLastModified(line.Value); + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/LoadCommand.cs b/src/MpcNET/Commands/Playlist/LoadCommand.cs new file mode 100644 index 0000000..0769107 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/LoadCommand.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Loads the playlist into the current queue. + /// + internal class LoadCommand : IMpcCommand + { + private readonly string _playlistName; + + public LoadCommand(string playlistName) + { + _playlistName = playlistName; + } + + public string Value => string.Join(" ", "load", $"\"{_playlistName}\""); + + public string FormatResponse(IList> response) + { + return string.Join(", ", response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/PlaylistCommand.cs b/src/MpcNET/Commands/Playlist/PlaylistCommand.cs new file mode 100644 index 0000000..65932fd --- /dev/null +++ b/src/MpcNET/Commands/Playlist/PlaylistCommand.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Linq; +using MpcNET.Types; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Displays the current playlist. + /// + internal class PlaylistCommand : IMpcCommand> + { + public string Value => "playlist"; + + public IEnumerable FormatResponse(IList> response) + { + var results = response.Select(line => MpdFile.Create(line.Value, int.Parse(line.Key))); + + return results; + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/PlaylistIdCommand.cs b/src/MpcNET/Commands/Playlist/PlaylistIdCommand.cs new file mode 100644 index 0000000..992ea33 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/PlaylistIdCommand.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using MpcNET.Types; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Displays song ID in the playlist. + /// + internal class PlaylistIdCommand : IMpcCommand> + { + private readonly int _songId; + + public PlaylistIdCommand(int songId) + { + _songId = songId; + } + + public string Value => string.Join((string) " ", new[] {"playlistid"}, _songId); + + public IEnumerable FormatResponse(IList> response) + { + return MpdFile.CreateList(response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Playlist/PlaylistInfoCommand.cs b/src/MpcNET/Commands/Playlist/PlaylistInfoCommand.cs new file mode 100644 index 0000000..9e48776 --- /dev/null +++ b/src/MpcNET/Commands/Playlist/PlaylistInfoCommand.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using MpcNET.Types; + +namespace MpcNET.Commands.Playlist +{ + /// + /// Displays a list of all songs in the playlist, + /// + internal class PlaylistInfoCommand : IMpcCommand> + { + public string Value => "playlistinfo"; + + public IEnumerable FormatResponse(IList> response) + { + return MpdFile.CreateList(response); + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Reflection/CommandsCommand.cs b/src/MpcNET/Commands/Reflection/CommandsCommand.cs new file mode 100644 index 0000000..1fbb007 --- /dev/null +++ b/src/MpcNET/Commands/Reflection/CommandsCommand.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Linq; + +namespace MpcNET.Commands.Reflection +{ + // config : This command is only permitted to "local" clients (connected via UNIX domain socket). + + /// + /// Shows which commands the current user has access to. + /// + public class CommandsCommand : IMpcCommand> + { + public string Value => "commands"; + + public IEnumerable FormatResponse(IList> response) + { + var result = response.Where(item => item.Key.Equals("command")).Select(item => item.Value); + + return result; + } + } +} diff --git a/src/MpcNET/Commands/Reflection/DecodersCommand.cs b/src/MpcNET/Commands/Reflection/DecodersCommand.cs new file mode 100644 index 0000000..7d9101a --- /dev/null +++ b/src/MpcNET/Commands/Reflection/DecodersCommand.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using MpcNET.Types; + +namespace MpcNET.Commands.Reflection +{ + /// + /// Print a list of decoder plugins, followed by their supported suffixes and MIME types. + /// + public class DecodersCommand : IMpcCommand> + { + public string Value => "decoders"; + + public IEnumerable FormatResponse(IList> response) + { + var result = new List(); + + var mpdDecoderPlugin = MpdDecoderPlugin.Empty; + foreach (var line in response) + { + if (line.Key.Equals("plugin")) + { + if (mpdDecoderPlugin.IsInitialized) + { + result.Add(mpdDecoderPlugin); + } + + mpdDecoderPlugin = new MpdDecoderPlugin(line.Value); + } + + if (line.Key.Equals("suffix") && mpdDecoderPlugin.IsInitialized) + { + mpdDecoderPlugin.AddSuffix(line.Value); + } + + if (line.Key.Equals("mime_type") && mpdDecoderPlugin.IsInitialized) + { + mpdDecoderPlugin.AddMediaType(line.Value); + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Reflection/TagTypesCommand.cs b/src/MpcNET/Commands/Reflection/TagTypesCommand.cs new file mode 100644 index 0000000..ab67f3e --- /dev/null +++ b/src/MpcNET/Commands/Reflection/TagTypesCommand.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Linq; + +namespace MpcNET.Commands.Reflection +{ + // TODO: notcommands : Shows which commands the current user does not have access to. + + /// + /// Shows a list of available song metadata. + /// + public class TagTypesCommand : IMpcCommand> + { + public string Value => "tagtypes"; + + public IEnumerable FormatResponse(IList> response) + { + var result = response.Where(item => item.Key.Equals("tagtype")).Select(item => item.Value); + + return result; + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Reflection/UrlHandlersCommand.cs b/src/MpcNET/Commands/Reflection/UrlHandlersCommand.cs new file mode 100644 index 0000000..f329fd4 --- /dev/null +++ b/src/MpcNET/Commands/Reflection/UrlHandlersCommand.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; + +namespace MpcNET.Commands.Reflection +{ + /// + /// Gets a list of available URL handlers. + /// + public class UrlHandlersCommand : IMpcCommand> + { + public string Value => "urlhandlers"; + + public IEnumerable FormatResponse(IList> response) + { + var result = response.Where(item => item.Key.Equals("handler")).Select(item => item.Value); + + return result; + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Status/CurrentSongCommand.cs b/src/MpcNET/Commands/Status/CurrentSongCommand.cs new file mode 100644 index 0000000..e0cd7a2 --- /dev/null +++ b/src/MpcNET/Commands/Status/CurrentSongCommand.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using MpcNET.Types; + +namespace MpcNET.Commands.Status +{ + internal class CurrentSongCommand : IMpcCommand + { + public string Value => "currentsong"; + + public IMpdFile FormatResponse(IList> response) + { + return MpdFile.Create(response, 0).mpdFile; + } + } +} \ No newline at end of file diff --git a/src/MpcNET/Commands/Status/StatusCommand.cs b/src/MpcNET/Commands/Status/StatusCommand.cs new file mode 100644 index 0000000..38ec8c1 --- /dev/null +++ b/src/MpcNET/Commands/Status/StatusCommand.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace MpcNET.Commands.Status +{ + internal class StatusCommand : IMpcCommand + { + public string Value => "status"; + + public MpdStatus FormatResponse(IList> response) + { + int volume = -1; + bool repeat = false; + bool random = false; + bool single = false; + bool consume = false; + int playlist = -1; + int playlistLength = 0; + int playlistSong = -1; + int playlistSongId = -1; + int playlistNextSong = -1; + int playlistNextSongId = -1; + int bitrate = 0; + int audioSampleRate = -1; + int audioBits = -1; + int audioChannels = -1; + int crossfade = -1; + MpdState mpdState = MpdState.Unknown; + TimeSpan elapsed; + TimeSpan duration; + int updatingDb = -1; + string error = string.Empty; + foreach (var keyValuePair in response) + { + var value = keyValuePair.Value; + switch (keyValuePair.Key) + { + case "volume": + int.TryParse(value, out volume); + break; + case "repeat": + repeat = "1" == value; + break; + case "random": + random = "1" == value; + break; + case "single": + single = "1" == value; + break; + case "consume": + consume = "1" == value; + break; + case "playlist": + int.TryParse(value, out playlist); + break; + case "playlistlength": + int.TryParse(value, out playlistLength); + break; + case "song": + int.TryParse(value, out playlistSong); + break; + case "songid": + int.TryParse(value, out playlistSongId); + break; + case "nextsong": + int.TryParse(value, out playlistNextSong); + break; + case "nextsongid": + int.TryParse(value, out playlistNextSongId); + break; + case "bitrate": + int.TryParse(value, out bitrate); + break; + case "audio": + var audioFormat = value.Split(':'); + int.TryParse(audioFormat[0], out audioSampleRate); + int.TryParse(audioFormat[1], out audioBits); + int.TryParse(audioFormat[2], out audioChannels); + break; + case "xfade": + int.TryParse(value, out crossfade); + break; + case "state": + Enum.TryParse(value, true, out mpdState); + break; + case "elapsed": + elapsed = ParseTime(value); + break; + case "duration": + duration = ParseTime(value); + break; + case "updating_db": + int.TryParse(value, out updatingDb); + break; + default: + Debug.WriteLine($"Unprocessed status: {keyValuePair.Key} - {keyValuePair.Value}"); + break; + } + } + + return new MpdStatus( + volume, + repeat, + random, + playlist, + playlistLength, + crossfade, + mpdState, + playlistSong, + playlistSongId, + playlistNextSong, + playlistNextSongId, + elapsed, + duration, + bitrate, + audioSampleRate, + audioBits, + audioChannels, + updatingDb, + error); + } + + private static TimeSpan ParseTime(string value) + { + var timeParts = value.Split(new[] { '.' }, 2); + int.TryParse(timeParts[0], out var seconds); + int.TryParse(timeParts[1], out var milliseconds); + return TimeSpan.FromSeconds(seconds) + TimeSpan.FromMilliseconds(milliseconds); + } + } +} diff --git a/src/MpcNET/Commands/IMpcCommand.cs b/src/MpcNET/IMpcCommand.cs similarity index 100% rename from src/MpcNET/Commands/IMpcCommand.cs rename to src/MpcNET/IMpcCommand.cs diff --git a/src/MpcNET/Message/IMpdMessage.cs b/src/MpcNET/Message/IMpdMessage.cs new file mode 100644 index 0000000..daa8e38 --- /dev/null +++ b/src/MpcNET/Message/IMpdMessage.cs @@ -0,0 +1,10 @@ +namespace MpcNET.Message +{ + public interface IMpdMessage + { + IMpdRequest Request { get; } + IMpdResponse Response { get; } + + bool IsResponseValid { get; } + } +} \ No newline at end of file diff --git a/src/MpcNET/Message/MpdMessage.cs b/src/MpcNET/Message/MpdMessage.cs index 0cb7115..9adff0b 100644 --- a/src/MpcNET/Message/MpdMessage.cs +++ b/src/MpcNET/Message/MpdMessage.cs @@ -6,12 +6,6 @@ using Newtonsoft.Json; namespace MpcNET.Message { - public interface IMpdMessage - { - IMpdRequest Request { get; } - IMpdResponse Response { get; } - } - [DebuggerDisplay("Request: {Request.Command.Value} | Response Status: {Response.State.Status}")] public class MpdMessage : IMpdMessage { @@ -30,8 +24,11 @@ namespace MpcNET.Message } public IMpdRequest Request { get; } + public IMpdResponse Response { get; } + public bool IsResponseValid => this.Response.State.Status == "OK"; + private IList> GetValuesFromResponse() { var result = new List>(); diff --git a/src/MpcNET/MpcConnection.cs b/src/MpcNET/MpcConnection.cs index 32b3ba5..a4c6e33 100644 --- a/src/MpcNET/MpcConnection.cs +++ b/src/MpcNET/MpcConnection.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; +using System.Text; using System.Threading.Tasks; using MpcNET.Message; using MpcNET.Utils; @@ -17,18 +18,20 @@ namespace MpcNET /// public class MpcConnection { + private readonly Encoding encoding = new UTF8Encoding(); private readonly IPEndPoint _server; private TcpClient _tcpClient; private NetworkStream _networkStream; - private StreamReader _reader; - private StreamWriter _writer; private string _version; public MpcConnection(IPEndPoint server) { - if (IsConnected) return; + if (this.IsConnected) + { + return; + } ClearConnectionFields(); _server = server; @@ -52,18 +55,19 @@ namespace MpcNET _networkStream = _tcpClient.GetStream(); // Encoding UTF8 has some problems with TcpClient: https://bugs.musicpd.org/view.php?id=4501 - _reader = new StreamReader(_networkStream); - _writer = new StreamWriter(_networkStream) { NewLine = "\n" }; - - var firstLine = _reader.ReadLine(); - if (!firstLine.StartsWith(Constants.FirstLinePrefix)) + using (var reader = new StreamReader(_networkStream, this.encoding, true, 512, true)) { - await DisconnectAsync(); - throw new InvalidDataException("Response of mpd does not start with \"" + Constants.FirstLinePrefix + "\"." ); + var firstLine = await reader.ReadLineAsync(); + if (!firstLine.StartsWith(Constants.FirstLinePrefix)) + { + await DisconnectAsync(); + throw new InvalidDataException("Response of mpd does not start with \"" + Constants.FirstLinePrefix + "\"."); + } + + _version = firstLine.Substring(Constants.FirstLinePrefix.Length); } - _version = firstLine.Substring(Constants.FirstLinePrefix.Length); } - + public Task DisconnectAsync() { if (_tcpClient == null) @@ -76,7 +80,7 @@ namespace MpcNET return Task.CompletedTask; } - + public async Task> SendAsync(IMpcCommand command) { @@ -87,14 +91,28 @@ namespace MpcNET try { - _writer.WriteLine(command.Value); - _writer.Flush(); + Console.WriteLine("Command: " + command.Value); + // Encoding UTF8 has some problems with TcpClient: https://bugs.musicpd.org/view.php?id=4501 + + using (var writer = new StreamWriter(_networkStream, this.encoding, 512, true) { NewLine = "\n" }) + { + writer.WriteLine(command.Value); + writer.Flush(); + } response = await ReadResponseAsync(); } - catch (Exception) + catch (Exception exception) { - try { await DisconnectAsync(); } catch (Exception) { } + Console.WriteLine("Exception:" + exception); + try + { + await DisconnectAsync(); + } + catch (Exception) + { + } + return null; // TODO: Create Null Object for MpdResponse } @@ -116,20 +134,28 @@ namespace MpcNET var response = new List(); // Read response untli reach end token (OK or ACK) - string responseLine; - do + using (var reader = new StreamReader(_networkStream, this.encoding, true, 512, true)) { - responseLine = await _reader.ReadLineAsync(); - response.Add(responseLine); - } while (!(responseLine.Equals(Constants.Ok) || responseLine.StartsWith(Constants.Ack))); + string responseLine; + do + { + responseLine = await reader.ReadLineAsync(); + Console.WriteLine("ReadLine:|" + responseLine + "|"); + if (responseLine == null) + { + Console.WriteLine("Received null"); + responseLine = string.Empty; + continue; + } + response.Add(responseLine); + } while (!(responseLine.Equals(Constants.Ok) || responseLine.StartsWith(Constants.Ack))); + } return response.Where(line => !string.IsNullOrEmpty(line)).ToArray(); } - private void ClearConnectionFields() + private void ClearConnectionFields() { - _writer?.Dispose(); - _reader?.Dispose(); _networkStream?.Dispose(); _tcpClient?.Dispose(); _version = string.Empty; diff --git a/src/MpcNET/MpcNET.csproj b/src/MpcNET/MpcNET.csproj index 2e967df..ddcf769 100644 --- a/src/MpcNET/MpcNET.csproj +++ b/src/MpcNET/MpcNET.csproj @@ -1,10 +1,9 @@  - netstandard1.6 + netstandard2.0 MpcNET MpcNET - 1.6.0 $(PackageTargetFallback);dnxcore50 false false @@ -17,7 +16,12 @@ - + + + + + + diff --git a/src/MpcNET/MpdState.cs b/src/MpcNET/MpdState.cs new file mode 100644 index 0000000..8eeb48c --- /dev/null +++ b/src/MpcNET/MpdState.cs @@ -0,0 +1,25 @@ +namespace MpcNET +{ + /// + /// The possible states of the MPD. + /// + public enum MpdState + { + /// + /// The state of the MPD could not be translated into this enumeration. + /// + Unknown, + /// + /// The MPD is playing a track. + /// + Play, + /// + /// The MPD is not playing a track. + /// + Stop, + /// + /// The playback of the MPD is currently paused. + /// + Pause + } +} \ No newline at end of file diff --git a/src/MpcNET/MpdStatus.cs b/src/MpcNET/MpdStatus.cs index 7d656a1..69d9c6c 100644 --- a/src/MpcNET/MpdStatus.cs +++ b/src/MpcNET/MpdStatus.cs @@ -1,119 +1,13 @@ -using System.Text; +using System; +using System.Text; namespace MpcNET { - /// - /// The possible states of the MPD. - /// - public enum MpdState - { - /// - /// The state of the MPD could not be translated into this enumeration. - /// - Unknown, - /// - /// The MPD is playing a track. - /// - Play, - /// - /// The MPD is not playing a track. - /// - Stop, - /// - /// The playback of the MPD is currently paused. - /// - Pause - } /// /// The MpdStatus class contains all values describing the current status of the MPD. /// public class MpdStatus { - private int volume; - private bool repeat; - private bool random; - private int playlist; - private int playlistLength; - private int xFade; - private MpdState state; - private int song; - private int songId; - private int timeElapsed; - private int timeTotal; - private int bitrate; - private int audioSampleRate; - private int audioBits; - private int audioChannels; - private int updatingDb; - private string error; - /// - /// The current volume of the output. - /// - public int Volume { get { return this.volume; } } - /// - /// If the playlist is repeated after finish. - /// - public bool Repeat { get { return this.repeat; } } - /// - /// If the playlist is played in random order. - /// - public bool Random { get { return this.random; } } - /// - /// The version number of the playlist. - /// - public int Playlist { get { return this.playlist; } } - /// - /// The length of the playlist. - /// - public int PlaylistLength { get { return this.playlistLength; } } - /// - /// The number of seconds crossfaded between song changes. - /// - public int XFade { get { return this.xFade; } } - /// - /// The state of the MPD. - /// - public MpdState State { get { return this.state; } } - /// - /// The index of the currently played song in the playlist. - /// - public int Song { get { return this.song; } } - /// - /// The id of the song currently played. - /// - public int SongId { get { return this.songId; } } - /// - /// The number of seconds already played of the current song. - /// - public int TimeElapsed { get { return this.timeElapsed; } } - /// - /// The length of the current song in seconds. - /// - public int TimeTotal { get { return this.timeTotal; } } - /// - /// The bitrate of the current song. - /// - public int Bitrate { get { return this.bitrate; } } - /// - /// The audio sample rate of the current song. - /// - public int AudioSampleRate { get { return this.audioSampleRate; } } - /// - /// The audio bits of the current song. - /// - public int AudioBits { get { return this.audioBits; } } - /// - /// The number of audio channels of the current song. - /// - public int AudioChannels { get { return this.audioChannels; } } - /// - /// The number of the update on the MPD database currently running. - /// - public int UpdatingDb { get { return this.updatingDb; } } - /// - /// An error message, if there is an error. - /// - public string Error { get { return this.error; } } /// /// Creates a new MpdStatus object. /// @@ -126,8 +20,10 @@ namespace MpcNET /// The state of the MPD. /// The index of the currently played song in the playlist. /// The id of the song currently played. - /// The number of seconds already played of the current song. - /// The length of the current song in seconds. + /// The next song. + /// The next song identifier. + /// The elapsed. + /// The duration. /// The bitrate of the current song. /// The audio sample rate of the current song. /// The audio bits of the current song. @@ -144,34 +40,126 @@ namespace MpcNET MpdState state, int song, int songId, - int timeElapsed, - int timeTotal, + int nextSong, + int nextSongId, + TimeSpan elapsed, + TimeSpan duration, int bitrate, int audioSampleRate, int audioBits, int audioChannels, int updatingDb, - string error - ) + string error) { - this.volume = volume; - this.repeat = repeat; - this.random = random; - this.playlist = playlist; - this.playlistLength = playlistLength; - this.xFade = xFade; - this.state = state; - this.song = song; - this.songId = songId; - this.timeElapsed = timeElapsed; - this.timeTotal = timeTotal; - this.bitrate = bitrate; - this.audioSampleRate = audioSampleRate; - this.audioBits = audioBits; - this.audioChannels = audioChannels; - this.updatingDb = updatingDb; - this.error = error; + this.Volume = volume; + this.Repeat = repeat; + this.Random = random; + this.Playlist = playlist; + this.PlaylistLength = playlistLength; + this.XFade = xFade; + this.State = state; + this.Song = song; + this.SongId = songId; + NextSong = nextSong; + NextSongId = nextSongId; + this.Elapsed = elapsed; + this.Duration = duration; + this.Bitrate = bitrate; + this.AudioSampleRate = audioSampleRate; + this.AudioBits = audioBits; + this.AudioChannels = audioChannels; + this.UpdatingDb = updatingDb; + this.Error = error; } + + /// + /// The current volume of the output. + /// + public int Volume { get; } + + /// + /// If the playlist is repeated after finish. + /// + public bool Repeat { get; } + + /// + /// If the playlist is played in random order. + /// + public bool Random { get; } + + /// + /// The version number of the playlist. + /// + public int Playlist { get; } + + /// + /// The length of the playlist. + /// + public int PlaylistLength { get; } + + /// + /// The number of seconds crossfaded between song changes. + /// + public int XFade { get; } + + /// + /// The state of the MPD. + /// + public MpdState State { get; } + + /// + /// The index of the currently played song in the playlist. + /// + public int Song { get; } + + /// + /// The id of the song currently played. + /// + public int SongId { get; } + + public int NextSong { get; } + public int NextSongId { get; } + + /// + /// The number of seconds already played of the current song. + /// + public TimeSpan Elapsed { get; } + + /// + /// The length of the current song in seconds. + /// + public TimeSpan Duration { get; } + + /// + /// The bitrate of the current song. + /// + public int Bitrate { get; } + + /// + /// The audio sample rate of the current song. + /// + public int AudioSampleRate { get; } + + /// + /// The audio bits of the current song. + /// + public int AudioBits { get; } + + /// + /// The number of audio channels of the current song. + /// + public int AudioChannels { get; } + + /// + /// The number of the update on the MPD database currently running. + /// + public int UpdatingDb { get; } + + /// + /// An error message, if there is an error. + /// + public string Error { get; } + /// /// Returns a string representation of the object maily for debugging purpuses. /// @@ -180,13 +168,13 @@ namespace MpcNET { StringBuilder builder = new StringBuilder(); - appendInt(builder, "volume", this.volume); - appendBool(builder, "repeat", this.repeat); - appendBool(builder, "random", this.random); - appendInt(builder, "playlist", this.playlist); - appendInt(builder, "playlistlength", this.playlistLength); - appendInt(builder, "xfade", this.xFade); - switch (this.state) + AppendInt(builder, "volume", this.Volume); + AppendBool(builder, "repeat", this.Repeat); + AppendBool(builder, "random", this.Random); + AppendInt(builder, "playlist", this.Playlist); + AppendInt(builder, "playlistlength", this.PlaylistLength); + AppendInt(builder, "xfade", this.XFade); + switch (this.State) { case MpdState.Play: builder.AppendLine("state: play"); @@ -198,38 +186,39 @@ namespace MpcNET builder.AppendLine("state: stop"); break; } - appendInt(builder, "song", this.song); - appendInt(builder, "songid", this.songId); - if ((this.timeElapsed >= 0) || (this.timeTotal >= 0)) + + AppendInt(builder, "song", this.Song); + AppendInt(builder, "songid", this.SongId); + if (this.Elapsed > TimeSpan.Zero || this.Duration > TimeSpan.Zero) { builder.Append("time: "); - builder.Append(this.timeElapsed); + builder.Append(this.Elapsed); builder.Append(":"); - builder.Append(this.timeTotal); + builder.Append(this.Duration); builder.AppendLine(); } - appendInt(builder, "bitrate", this.bitrate); - if ((this.audioSampleRate >= 0) || (this.audioBits >= 0) || (this.audioChannels >= 0)) + AppendInt(builder, "bitrate", this.Bitrate); + if ((AudioSampleRate >= 0) || (this.AudioBits >= 0) || (this.AudioChannels >= 0)) { builder.Append("audio: "); - builder.Append(this.audioSampleRate); + builder.Append(this.AudioSampleRate); builder.Append(":"); - builder.Append(this.audioBits); + builder.Append(this.AudioBits); builder.Append(":"); - builder.Append(this.audioChannels); + builder.Append(this.AudioChannels); builder.AppendLine(); } - appendInt(builder, "updating_db", this.updatingDb); - if (this.error != null) + AppendInt(builder, "updating_db", this.UpdatingDb); + if (this.Error != null) { builder.Append("error: "); - builder.AppendLine(this.error); + builder.AppendLine(this.Error); } return builder.ToString(); } - private static void appendInt(StringBuilder builder, string name, int value) + private static void AppendInt(StringBuilder builder, string name, int value) { if (value < 0) return; @@ -240,7 +229,7 @@ namespace MpcNET builder.AppendLine(); } - private static void appendBool(StringBuilder builder, string name, bool value) + private static void AppendBool(StringBuilder builder, string name, bool value) { builder.Append(name); builder.Append(": "); diff --git a/src/MpcNET/Properties/AssemblyInfo.cs b/src/MpcNET/Properties/AssemblyInfo.cs index 18f020c..7d6f01f 100644 --- a/src/MpcNET/Properties/AssemblyInfo.cs +++ b/src/MpcNET/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/MpcNET/Types/IMpdFile.cs b/src/MpcNET/Types/IMpdFile.cs index bd7366c..5876e76 100644 --- a/src/MpcNET/Types/IMpdFile.cs +++ b/src/MpcNET/Types/IMpdFile.cs @@ -2,11 +2,6 @@ namespace MpcNET.Types { - public interface IMpdFilePath - { - string Path { get; } - } - public interface IMpdFile : IMpdFilePath { int Time { get; } @@ -23,6 +18,76 @@ namespace MpcNET.Types int Disc { get; } int Pos { get; } int Id { get; } - IDictionary UnknownMetadata { get; } + IReadOnlyDictionary UnknownMetadata { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasTime { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasAlbum { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasArtist { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasTitle { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasTrack { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasName { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasGenre { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasDate { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasComposer { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasPerformer { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasComment { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasDisc { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasPos { get; } + + /// + /// If the MpdFile has the property set. + /// + bool HasId { get; } } } \ No newline at end of file diff --git a/src/MpcNET/Types/IMpdFilePath.cs b/src/MpcNET/Types/IMpdFilePath.cs new file mode 100644 index 0000000..81bd28e --- /dev/null +++ b/src/MpcNET/Types/IMpdFilePath.cs @@ -0,0 +1,7 @@ +namespace MpcNET.Types +{ + public interface IMpdFilePath + { + string Path { get; } + } +} \ No newline at end of file diff --git a/src/MpcNET/Types/MpdFile.cs b/src/MpcNET/Types/MpdFile.cs index 74803d1..6944364 100644 --- a/src/MpcNET/Types/MpdFile.cs +++ b/src/MpcNET/Types/MpdFile.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using MpcNET.Utils; namespace MpcNET.Types { @@ -8,97 +7,324 @@ namespace MpcNET.Types /// internal class MpdFile : IMpdFile { - private const string TagTime = "Time"; - private const string TagArtist = "Artist"; - private const string TagAlbum = "Album"; - private const string TagTitle = "Title"; - private const string TagTrack = "Track"; - private const string TagName = "Name"; - private const string TagGenre = "Genre"; - private const string TagDate = "Date"; - private const string TagComposer = "Composer"; - private const string TagPerformer = "Performer"; - private const string TagComment = "Comment"; - private const string TagDisc = "Disc"; - private const string TagPos = "Pos"; - private const string TagId = "Id"; + internal const string TagFile = "file"; + internal const string TagTime = "Time"; + internal const string TagArtist = "Artist"; + internal const string TagAlbum = "Album"; + internal const string TagTitle = "Title"; + internal const string TagTrack = "Track"; + internal const string TagName = "Name"; + internal const string TagGenre = "Genre"; + internal const string TagDate = "Date"; + internal const string TagComposer = "Composer"; + internal const string TagPerformer = "Performer"; + internal const string TagComment = "Comment"; + internal const string TagDisc = "Disc"; + internal const string TagPos = "Pos"; + internal const string TagId = "Id"; - private readonly IDictionary _unknownMetadata = new Dictionary(); + internal const int NoTime = -1; + internal const string NoAlbum = null; + internal const string NoArtist = null; + internal const string NoTitle = null; + internal const string NoTrack = null; + internal const string NoName = null; + internal const string NoGenre = null; + internal const string NoDate = null; + internal const string NoComposer = null; + internal const string NoPerformer = null; + internal const string NoComment = null; + internal const int NoDisc = -1; + internal const int NoPos = -1; + internal const int NoId = -1; - internal MpdFile(string file) + internal MpdFile( + string path, + int time = NoTime, + string album = NoAlbum, + string artist = NoArtist, + string title = NoTitle, + string track = NoTrack, + string name = NoName, + string genre = NoGenre, + string date = NoDate, + string composer = NoComposer, + string performer = NoPerformer, + string comment = NoComment, + int disc = NoDisc, + int pos = NoPos, + int id = NoId, + IReadOnlyDictionary unknownMetadata = null) { - file.CheckNotNull(); - - Path = file; + this.Path = path; + this.Time = time; + this.Album = album; + this.Artist = artist; + this.Title = title; + this.Track = track; + this.Name = name; + this.Genre = genre; + this.Date = date; + this.Composer = composer; + this.Performer = performer; + this.Comment = comment; + this.Disc = disc; + this.Pos = pos; + this.Id = id; + this.UnknownMetadata = unknownMetadata ?? new Dictionary(); } public string Path { get; } - public int Time { get; private set; } = -1; - public string Album { get; private set; } = string.Empty; - public string Artist { get; private set; } = string.Empty; - public string Title { get; private set; } = string.Empty; - public string Track { get; private set; } = string.Empty; - public string Name { get; private set; } = string.Empty; - public string Genre { get; private set; } = string.Empty; - public string Date { get; private set; } = string.Empty; - public string Composer { get; private set; } = string.Empty; - public string Performer { get; private set; } = string.Empty; - public string Comment { get; private set; } = string.Empty; - public int Disc { get; private set; } = -1; - public int Pos { get; set; } = -1; - public int Id { get; private set; } = -1; - public IDictionary UnknownMetadata => _unknownMetadata; + public int Time { get; } + public string Album { get; } + public string Artist { get; } + public string Title { get; } + public string Track { get; } + public string Name { get; } + public string Genre { get; } + public string Date { get; } + public string Composer { get; } + public string Performer { get; } + public string Comment { get; } + public int Disc { get; } + public int Pos { get; set; } + public int Id { get; } + public IReadOnlyDictionary UnknownMetadata { get; } - internal void AddTag(string tag, string value) + + /// + /// If the MpdFile has the property set. + /// + public bool HasTime => this.Time != NoTime; + + /// + /// If the MpdFile has the property set. + /// + public bool HasAlbum => this.Album != NoAlbum; + + /// + /// If the MpdFile has the property set. + /// + public bool HasArtist => this.Artist != NoArtist; + + /// + /// If the MpdFile has the property set. + /// + public bool HasTitle => this.Title != NoTitle; + + /// + /// If the MpdFile has the property set. + /// + public bool HasTrack => this.Track != NoTrack; + + /// + /// If the MpdFile has the property set. + /// + public bool HasName => this.Name != NoName; + + /// + /// If the MpdFile has the property set. + /// + public bool HasGenre => this.Genre != NoGenre; + + /// + /// If the MpdFile has the property set. + /// + public bool HasDate => this.Date != NoDate; + + /// + /// If the MpdFile has the property set. + /// + public bool HasComposer => this.Composer != NoComposer; + + /// + /// If the MpdFile has the property set. + /// + public bool HasPerformer => this.Performer != NoPerformer; + + /// + /// If the MpdFile has the property set. + /// + public bool HasComment => this.Comment != NoComment; + + /// + /// If the MpdFile has the property set. + /// + public bool HasDisc => this.Disc != NoDisc; + + /// + /// If the MpdFile has the property set. + /// + public bool HasPos => this.Pos != NoPos; + + /// + /// If the MpdFile has the property set. + /// + public bool HasId => this.Id != NoId; + + internal static MpdFile Create(string path, int pos) { - switch (tag) + return new MpdFile(path, pos: pos); + } + + internal static (IMpdFile mpdFile, int index) Create(IList> response, int startIndex) + { + string file; + if (response.Count <= startIndex) { - case TagTime: - Time = int.Parse(value); - break; - case TagArtist: - Artist = value; - break; - case TagAlbum: - Album = value; - break; - case TagTitle: - Title = value; - break; - case TagTrack: - Track = value; - break; - case TagName: - Name = value; - break; - case TagGenre: - Genre = value; - break; - case TagDate: - Date = value; - break; - case TagComposer: - Composer = value; - break; - case TagPerformer: - Performer = value; - break; - case TagComment: - Comment = value; - break; - case TagDisc: - Disc = int.Parse(value); - break; - case TagPos: - Pos = int.Parse(value); - break; - case TagId: - Id = int.Parse(value); - break; - default: - _unknownMetadata.Add(tag, value); - break; + return (null, -1); } + + var fileKeyValuePair = response[startIndex]; + if (fileKeyValuePair.Key == "file") + { + file = fileKeyValuePair.Value; + } + else + { + return (null, -1); + } + + int time = NoTime; + string album = NoAlbum; + string artist = NoArtist; + string title = NoTitle; + string track = NoTrack; + string name = NoName; + string genre = NoGenre; + string date = NoDate; + string composer = NoComposer; + string performer = NoPerformer; + string comment = NoComment; + int disc = NoDisc; + int pos = NoPos; + int id = NoId; + var unknownMetadata = new Dictionary(); + for (var index = startIndex + 1; index < response.Count; index++) + { + var line = response[index]; + if (line.Key != null) + { + switch (line.Key) + { + case TagFile: + return (new MpdFile( + file, + time, + album, + artist, + title, + track, + name, + genre, + date, + composer, + performer, + comment, + disc, + pos, + id), index - 1); + case TagTime: + if (int.TryParse(line.Value, out int tryTime)) + { + time = tryTime; + } + break; + case TagAlbum: + album = line.Value; + break; + case TagArtist: + artist = line.Value; + break; + case TagTitle: + title = line.Value; + break; + case TagTrack: + track = line.Value; + break; + case TagName: + name = line.Value; + break; + case TagGenre: + genre = line.Value; + break; + case TagDate: + date = line.Value; + break; + case TagComposer: + composer = line.Value; + break; + case TagPerformer: + performer = line.Value; + break; + case TagComment: + comment = line.Value; + break; + case TagDisc: + if (int.TryParse(line.Value, out var tryDisc)) + { + disc = tryDisc; + } + + break; + case TagPos: + if (int.TryParse(line.Value, out var tryPos)) + { + pos = tryPos; + } + + break; + case TagId: + if (int.TryParse(line.Value, out var tryId)) + { + id = tryId; + } + + break; + default: + unknownMetadata.Add(line.Key, line.Value); + break; + } + } + } + + return (new MpdFile( + file, + time, + album, + artist, + title, + track, + name, + genre, + date, + composer, + performer, + comment, + disc, + pos, + id), response.Count - 1); + } + + public static IEnumerable CreateList(IList> response) + { + var mpdFiles = new List(); + for (var index = 0; index < response.Count; index++) + { + var (mpdFile, lastIndex) = Create(response, index); + if (mpdFile != null) + { + mpdFiles.Add(mpdFile); + index = lastIndex; + } + else + { + break; + } + } + + return mpdFiles; } } } +