1
0
mirror of https://github.com/ZetaKebab/MpcNET.git synced 2024-09-16 05:30:09 +00:00

Various improvements

This commit is contained in:
Kim Hugener-Ohlsen 2018-01-05 10:18:57 +01:00
parent 6426d98fc5
commit a2c012dd7e
54 changed files with 1649 additions and 863 deletions

2
.gitignore vendored
View File

@ -14,3 +14,5 @@
/src/MpcNET.sln.DotSettings.user
/src/MpcNET.Test/MpcNET.Test.csproj.DotSettings
/src/MpcNET/MpcNET.csproj.DotSettings
/.vs
*.user

View File

@ -1,11 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>MpcNET.Test</AssemblyName>
<PackageId>MpcNET.Test</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<RuntimeFrameworkVersion>1.0.4</RuntimeFrameworkVersion>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@ -25,9 +24,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.1.14" />
<PackageReference Include="MSTest.TestFramework" Version="1.1.14" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
</ItemGroup>
<ItemGroup>

View File

@ -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);

View File

@ -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);

View File

@ -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<int> 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;
}
}

View File

@ -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);

View File

@ -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
{
/// <summary>
/// https://www.musicpd.org/doc/protocol/database.html
/// </summary>
public class Database
public static class Database
{
public static IMpcCommand<IEnumerable<IMpdFile>> Find(ITag tag, string searchText)
{
return new FindCommand(tag, searchText);
}
public static IMpcCommand<string> Update(string uri = null)
{
return new UpdateCommand(uri);
}
// TODO: count
/// <summary>
/// Finds songs in the database that is exactly "searchText".
/// </summary>
public class Find : IMpcCommand<IEnumerable<IMpdFile>>
{
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<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var results = new List<MpdFile>();
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<string>
{
private readonly ITag _tag;
public List(ITag tag)
{
_tag = tag;
}
public string Value => string.Join(" ", "list", _tag);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
// TODO:
return response.ToString();
}
}
// TODO: findadd
/// <summary>
/// Lists all songs and directories in URI.
/// </summary>
public class ListAll : IMpcCommand<IEnumerable<MpdDirectory>>
{
public string Value => "listall";
public IEnumerable<MpdDirectory> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var rootDirectory = new List<MpdDirectory>
{
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<string>
{
// TODO: Extend command: < update [URI] >
public string Value => "update";
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
// TODO:
return response.ToString();
}
}
// TODO: rescan
public static IMpcCommand<IEnumerable<MpdDirectory>> ListAll()
{
return new ListAllCommand();
}
}
}
}

View File

@ -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
{
/// <summary>
/// https://www.musicpd.org/doc/protocol/output_commands.html
/// </summary>
public class Output
{
/// <summary>
/// Turns an output off.
/// </summary>
public class DisableOutput : IMpcCommand<string>
public static IMpcCommand<IEnumerable<MpdOutput>> Outputs()
{
private readonly int _outputId;
public DisableOutput(int outputId)
{
_outputId = outputId;
}
public string Value => string.Join(" ", "disableoutput", _outputId);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
// Response should be empty.
return string.Join(", ", response);
}
return new OutputsCommand();
}
/// <summary>
/// Turns an output on.
/// </summary>
public class EnableOutput : IMpcCommand<string>
public static IMpcCommand<string> DisableOutput(int outputId)
{
private readonly int _outputId;
public EnableOutput(int outputId)
{
_outputId = outputId;
}
public string Value => string.Join(" ", "enableoutput", _outputId);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
// Response should be empty.
return string.Join(", ", response);
}
return new DisableOutputCommand(outputId);
}
/// <summary>
/// Turns an output on or off, depending on the current state.
/// </summary>
public class ToggleOutput : IMpcCommand<string>
public static IMpcCommand<string> EnableOutput(int outputId)
{
private readonly int _outputId;
public ToggleOutput(int outputId)
{
_outputId = outputId;
}
public string Value => string.Join(" ", "toggleoutput", _outputId);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
// Response should be empty.
return string.Join(", ", response);
}
return new EnableOutputCommand(outputId);
}
/// <summary>
/// Shows information about all outputs.
/// </summary>
public class Outputs : IMpcCommand<IEnumerable<MpdOutput>>
public static IMpcCommand<string> ToggleOutput(int outputId)
{
public string Value => "outputs";
public IEnumerable<MpdOutput> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = new List<MpdOutput>();
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);
}
}
}

View File

@ -0,0 +1,24 @@
using MpcNET.Commands.Playback;
using MpcNET.Types;
namespace MpcNET.Commands
{
public partial class Command
{
/// <summary>
/// https://www.musicpd.org/doc/protocol/playback_commands.html
/// </summary>
public static class Playback
{
public static IMpcCommand<string> Next() => new NextCommand();
public static IMpcCommand<string> Previous() => new PreviousCommand();
public static IMpcCommand<string> PlayPause() => new PlayPauseCommand();
public static IMpcCommand<string> Play(IMpdFile mpdFile) => new PlayCommand(mpdFile);
public static IMpcCommand<string> Stop() => new StopCommand();
}
}
}

View File

@ -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
/// </summary>
public static class Current
{
/// <summary>
/// Adds the file URI to the playlist (directories add recursively). URI can also be a single file.
/// </summary>
private class AddImpl : IMpcCommand<string>
{
private readonly string _uri;
public AddImpl(string uri)
{
_uri = uri;
}
public string Value => string.Join(" ", "add", $"\"{_uri}\"");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
/// <summary>
/// Command: add
/// </summary>
public static IMpcCommand<string> AddDirectory(string directory) { return new AddImpl(directory); }
/// <summary>
/// Adds a song to the playlist (non-recursive) and returns the song id.
/// </summary>
private class AddIdImpl : IMpcCommand<string>
{
private readonly string _uri;
public AddIdImpl(string uri)
{
_uri = uri;
}
public string Value => string.Join(" ", "addid", $"\"{_uri}\"");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
public static IMpcCommand<string> AddDirectory(string directory) { return new AddCommand(directory); }
/// <summary>
/// Command: addid
/// </summary>
public static IMpcCommand<string> AddSong(string songPath) { return new AddIdImpl(songPath); }
/// <summary>
/// Clears the current playlist.
/// </summary>
private class ClearImpl : IMpcCommand<string>
{
public string Value => "clear";
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
public static IMpcCommand<string> AddSong(string songPath) { return new AddIdCommand(songPath); }
/// <summary>
/// Command: clear
/// </summary>
public static IMpcCommand<string> Clear() { return new ClearImpl(); }
/// <summary>
/// Displays the current playlist.
/// </summary>
private class PlaylistImpl : IMpcCommand<IEnumerable<IMpdFile>>
{
public string Value => "playlist";
public IEnumerable<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var results = response.Select(line => new MpdFile(line.Value) { Pos = int.Parse(line.Key) });
return results;
}
}
public static IMpcCommand<string> Clear() { return new ClearCommand(); }
/// <summary>
/// Command: playlist
/// </summary>
public static IMpcCommand<IEnumerable<IMpdFile>> GetAllSongsInfo() { return new PlaylistImpl(); }
/// <summary>
/// Deletes a song from the playlist.
/// </summary>
private class DeleteImpl : IMpcCommand<string>
{
private readonly int _position;
public DeleteImpl(int position)
{
_position = position;
}
public string Value => string.Join(" ", "delete", _position);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
public static IMpcCommand<IEnumerable<IMpdFile>> GetAllSongsInfo() { return new PlaylistCommand(); }
/// <summary>
/// Command: delete
/// </summary>
public static IMpcCommand<string> RemoveSongByPosition(int position) { return new DeleteImpl(position); }
/// <summary>
/// Deletes the song SONGID from the playlist
/// </summary>
private class DeleteIdImpl : IMpcCommand<string>
{
private readonly int _songId;
public DeleteIdImpl(int songId)
{
_songId = songId;
}
public string Value => string.Join(" ", "deleteid", _songId);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
public static IMpcCommand<string> RemoveSongByPosition(int position) { return new DeleteCommand(position); }
/// <summary>
/// Command: deleteid
/// </summary>
public static IMpcCommand<string> RemoveSongById(int songId) { return new DeleteIdImpl(songId); }
/// <summary>
/// Displays song ID in the playlist.
/// </summary>
private class PlaylistIdImpl : IMpcCommand<IEnumerable<IMpdFile>>
{
private readonly int _songId;
public PlaylistIdImpl(int songId)
{
_songId = songId;
}
public string Value => string.Join(" ", "playlistid", _songId);
public IEnumerable<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var results = new List<MpdFile>();
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<string> RemoveSongById(int songId) { return new DeleteIdCommand(songId); }
/// <summary>
/// Command: playlistid
/// </summary>
public static IMpcCommand<IEnumerable<IMpdFile>> GetSongMetadata(int songId) { return new PlaylistIdImpl(songId); }
/// <summary>
/// Displays a list of all songs in the playlist,
/// </summary>
private class PlaylistInfoImpl : IMpcCommand<IEnumerable<IMpdFile>>
{
public string Value => "playlistinfo";
public IEnumerable<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var results = new List<MpdFile>();
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<IEnumerable<IMpdFile>> GetSongMetadata(int songId) { return new PlaylistIdCommand(songId); }
/// <summary>
/// Command: playlistinfo
/// </summary>
public static IMpcCommand<IEnumerable<IMpdFile>> GetAllSongMetadata() { return new PlaylistInfoImpl(); }
public static IMpcCommand<IEnumerable<IMpdFile>> GetAllSongMetadata() { return new PlaylistInfoCommand(); }
}
/// <summary>
@ -228,128 +59,26 @@ namespace MpcNET
/// </summary>
public static class Stored
{
/// <summary>
/// Loads the playlist into the current queue.
/// </summary>
private class LoadImpl : IMpcCommand<string>
{
private readonly string _playlistName;
public LoadImpl(string playlistName)
{
_playlistName = playlistName;
}
public string Value => string.Join(" ", "load", $"\"{_playlistName}\"");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
/// <summary>
/// Command: load
/// </summary>
public static IMpcCommand<string> Load(string playlistName) { return new LoadImpl(playlistName); }
/// <summary>
/// Lists the songs in the playlist.
/// </summary>
private class ListPlaylistImpl : IMpcCommand<IEnumerable<IMpdFilePath>>
{
private readonly string _playlistName;
public ListPlaylistImpl(string playlistName)
{
_playlistName = playlistName;
}
public string Value => string.Join(" ", "listplaylist", $"\"{_playlistName}\"");
public IEnumerable<IMpdFilePath> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var results = response.Where(line => line.Key.Equals("file")).Select(line => new MpdFile(line.Value));
return results;
}
}
public static IMpcCommand<string> Load(string playlistName) { return new LoadCommand(playlistName); }
/// <summary>
/// Command: listplaylist
/// </summary>
public static IMpcCommand<IEnumerable<IMpdFilePath>> GetContent(string playlistName) { return new ListPlaylistImpl(playlistName); }
/// <summary>
/// Lists the songs with metadata in the playlist.
/// </summary>
private class ListPlaylistInfoImpl : IMpcCommand<IEnumerable<IMpdFile>>
{
private readonly string _playlistName;
public ListPlaylistInfoImpl(string playlistName)
{
_playlistName = playlistName;
}
public string Value => string.Join(" ", "listplaylistinfo", $"\"{_playlistName}\"");
public IEnumerable<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var results = new List<MpdFile>();
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<IEnumerable<IMpdFilePath>> GetContent(string playlistName) { return new ListPlaylistCommand(playlistName); }
/// <summary>
/// Command: listplaylistinfo
/// </summary>
public static IMpcCommand<IEnumerable<IMpdFile>> GetContentWithMetadata(string playlistName) { return new ListPlaylistInfoImpl(playlistName); }
/// <summary>
/// Prints a list of the playlist directory.
/// </summary>
private class ListPlaylistsImpl : IMpcCommand<IEnumerable<MpdPlaylist>>
{
public string Value => "listplaylists";
public IEnumerable<MpdPlaylist> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = new List<MpdPlaylist>();
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<IEnumerable<IMpdFile>> GetContentWithMetadata(string playlistName) { return new ListPlaylistInfoCommand(playlistName); }
/// <summary>
/// Command: listplaylists
/// </summary>
public static IMpcCommand<IEnumerable<MpdPlaylist>> GetAll() { return new ListPlaylistsImpl(); }
public static IMpcCommand<IEnumerable<MpdPlaylist>> GetAll() { return new ListPlaylistsCommand(); }
}
}
}

View File

@ -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
{
/// <summary>
/// https://www.musicpd.org/doc/protocol/reflection_commands.html
/// </summary>
public static class Reflection
{
// config : This command is only permitted to "local" clients (connected via UNIX domain socket).
/// <summary>
/// Shows which commands the current user has access to.
/// </summary>
public class Commands : IMpcCommand<IEnumerable<string>>
public static IMpcCommand<IEnumerable<string>> Commands()
{
public string Value => "commands";
public IEnumerable<string> FormatResponse(IList<KeyValuePair<string, string>> 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.
/// <summary>
/// Shows a list of available song metadata.
/// </summary>
public class TagTypes : IMpcCommand<IEnumerable<string>>
public static IMpcCommand<IEnumerable<string>> TagTypes()
{
public string Value => "tagtypes";
public IEnumerable<string> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = response.Where(item => item.Key.Equals("tagtype")).Select(item => item.Value);
return result;
}
return new TagTypesCommand();
}
/// <summary>
/// Gets a list of available URL handlers.
/// </summary>
public class UrlHandlers : IMpcCommand<IEnumerable<string>>
public static IMpcCommand<IEnumerable<string>> UrlHandlers()
{
public string Value => "urlhandlers";
public IEnumerable<string> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = response.Where(item => item.Key.Equals("handler")).Select(item => item.Value);
return result;
}
return new UrlHandlersCommand();
}
/// <summary>
/// Print a list of decoder plugins, followed by their supported suffixes and MIME types.
/// </summary>
public class Decoders : IMpcCommand<IEnumerable<MpdDecoderPlugin>>
public static IMpcCommand<IEnumerable<MpdDecoderPlugin>> Decoders()
{
public string Value => "decoders";
public IEnumerable<MpdDecoderPlugin> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = new List<MpdDecoderPlugin>();
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();
}
}
}

View File

@ -0,0 +1,24 @@
using MpcNET.Commands.Status;
using MpcNET.Types;
namespace MpcNET.Commands
{
public static partial class Command
{
/// <summary>
/// https://www.musicpd.org/doc/protocol/command_reference.html#status_commands
/// </summary>
public static class Status
{
public static IMpcCommand<MpdStatus> GetStatus()
{
return new StatusCommand();
}
public static IMpcCommand<IMpdFile> GetCurrentSong()
{
return new CurrentSongCommand();
}
}
}
}

View File

@ -0,0 +1,30 @@
using System.Collections.Generic;
using MpcNET.Tags;
using MpcNET.Types;
namespace MpcNET.Commands.Database
{
/// <summary>
/// Finds songs in the database that is exactly "searchText".
/// </summary>
internal class FindCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
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<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
return MpdFile.CreateList(response);
}
}
// TODO: rescan
}

View File

@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Linq;
using MpcNET.Types;
namespace MpcNET.Commands.Database
{
/// <summary>
/// Lists all songs and directories in URI.
/// </summary>
internal class ListAllCommand : IMpcCommand<IEnumerable<MpdDirectory>>
{
public string Value => "listall";
public IEnumerable<MpdDirectory> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var rootDirectory = new List<MpdDirectory>
{
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
}

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
using MpcNET.Tags;
namespace MpcNET.Commands.Database
{
internal class ListCommand : IMpcCommand<string>
{
private readonly ITag _tag;
public ListCommand(ITag tag)
{
_tag = tag;
}
public string Value => string.Join(" ", "list", _tag);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
// TODO:
return response.ToString();
}
}
// TODO: rescan
}

View File

@ -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<string>
{
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<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
// TODO: rescan
}

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Output
{
/// <summary>
/// Turns an output off.
/// </summary>
public class DisableOutputCommand : IMpcCommand<string>
{
private readonly int outputId;
public DisableOutputCommand(int outputId)
{
this.outputId = outputId;
}
public string Value => string.Join(" ", "disableoutput", this.outputId);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
// Response should be empty.
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Output
{
/// <summary>
/// Turns an output on.
/// </summary>
internal class EnableOutputCommand : IMpcCommand<string>
{
private readonly int _outputId;
public EnableOutputCommand(int outputId)
{
_outputId = outputId;
}
public string Value => string.Join(" ", "enableoutput", _outputId);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
// Response should be empty.
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
using MpcNET.Types;
namespace MpcNET.Commands.Output
{
/// <summary>
/// Shows information about all outputs.
/// </summary>
public class OutputsCommand : IMpcCommand<IEnumerable<MpdOutput>>
{
public string Value => "outputs";
public IEnumerable<MpdOutput> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = new List<MpdOutput>();
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;
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Output
{
/// <summary>
/// Turns an output on or off, depending on the current state.
/// </summary>
internal class ToggleOutputCommand : IMpcCommand<string>
{
private readonly int outputId;
public ToggleOutputCommand(int outputId)
{
this.outputId = outputId;
}
public string Value => string.Join(" ", "toggleoutput", outputId);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playback
{
internal class NextCommand : IMpcCommand<string>
{
public string Value => string.Join(" ", "next");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,33 @@
using System.Collections.Generic;
using MpcNET.Types;
namespace MpcNET.Commands.Playback
{
internal class PlayCommand : IMpcCommand<string>
{
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<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playback
{
internal class PlayPauseCommand : IMpcCommand<string>
{
public string Value => string.Join(" ", "pause");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playback
{
internal class PreviousCommand : IMpcCommand<string>
{
public string Value => string.Join(" ", "previous");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playback
{
internal class StopCommand : IMpcCommand<string>
{
public string Value => string.Join(" ", "stop");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Adds the file URI to the playlist (directories add recursively). URI can also be a single file.
/// </summary>
internal class AddCommand : IMpcCommand<string>
{
private readonly string _uri;
public AddCommand(string uri)
{
_uri = uri;
}
public string Value => string.Join(" ", "add", $"\"{_uri}\"");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Adds a song to the playlist (non-recursive) and returns the song id.
/// </summary>
internal class AddIdCommand : IMpcCommand<string>
{
private readonly string _uri;
public AddIdCommand(string uri)
{
_uri = uri;
}
public string Value => string.Join(" ", "addid", $"\"{_uri}\"");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Clears the current playlist.
/// </summary>
internal class ClearCommand : IMpcCommand<string>
{
public string Value => "clear";
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Deletes a song from the playlist.
/// </summary>
internal class DeleteCommand : IMpcCommand<string>
{
private readonly int position;
public DeleteCommand(int position)
{
this.position = position;
}
public string Value => string.Join(" ", "delete", this.position);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Deletes the song SONGID from the playlist
/// </summary>
internal class DeleteIdCommand : IMpcCommand<string>
{
private readonly int songId;
public DeleteIdCommand(int songId)
{
this.songId = songId;
}
public string Value => string.Join(" ", "deleteid", this.songId);
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.Linq;
using MpcNET.Types;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Lists the songs in the playlist.
/// </summary>
internal class ListPlaylistCommand : IMpcCommand<IEnumerable<IMpdFilePath>>
{
private readonly string _playlistName;
public ListPlaylistCommand(string playlistName)
{
_playlistName = playlistName;
}
public string Value => string.Join(" ", "listplaylist", $"\"{_playlistName}\"");
public IEnumerable<IMpdFilePath> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var results = response.Where(line => line.Key.Equals("file")).Select(line => new MpdFile(line.Value));
return results;
}
}
}

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
using MpcNET.Types;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Lists the songs with metadata in the playlist.
/// </summary>
internal class ListPlaylistInfoCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
private readonly string _playlistName;
public ListPlaylistInfoCommand(string playlistName)
{
_playlistName = playlistName;
}
public string Value => string.Join(" ", "listplaylistinfo", $"\"{_playlistName}\"");
public IEnumerable<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
return MpdFile.CreateList(response);
}
}
}

View File

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Linq;
using MpcNET.Types;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Prints a list of the playlist directory.
/// </summary>
internal class ListPlaylistsCommand : IMpcCommand<IEnumerable<MpdPlaylist>>
{
public string Value => "listplaylists";
public IEnumerable<MpdPlaylist> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = new List<MpdPlaylist>();
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;
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Loads the playlist into the current queue.
/// </summary>
internal class LoadCommand : IMpcCommand<string>
{
private readonly string _playlistName;
public LoadCommand(string playlistName)
{
_playlistName = playlistName;
}
public string Value => string.Join(" ", "load", $"\"{_playlistName}\"");
public string FormatResponse(IList<KeyValuePair<string, string>> response)
{
return string.Join(", ", response);
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Linq;
using MpcNET.Types;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Displays the current playlist.
/// </summary>
internal class PlaylistCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
public string Value => "playlist";
public IEnumerable<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var results = response.Select(line => MpdFile.Create(line.Value, int.Parse(line.Key)));
return results;
}
}
}

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
using MpcNET.Types;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Displays song ID in the playlist.
/// </summary>
internal class PlaylistIdCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
private readonly int _songId;
public PlaylistIdCommand(int songId)
{
_songId = songId;
}
public string Value => string.Join((string) " ", new[] {"playlistid"}, _songId);
public IEnumerable<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
return MpdFile.CreateList(response);
}
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using MpcNET.Types;
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Displays a list of all songs in the playlist,
/// </summary>
internal class PlaylistInfoCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
public string Value => "playlistinfo";
public IEnumerable<IMpdFile> FormatResponse(IList<KeyValuePair<string, string>> response)
{
return MpdFile.CreateList(response);
}
}
}

View File

@ -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).
/// <summary>
/// Shows which commands the current user has access to.
/// </summary>
public class CommandsCommand : IMpcCommand<IEnumerable<string>>
{
public string Value => "commands";
public IEnumerable<string> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = response.Where(item => item.Key.Equals("command")).Select(item => item.Value);
return result;
}
}
}

View File

@ -0,0 +1,44 @@
using System.Collections.Generic;
using MpcNET.Types;
namespace MpcNET.Commands.Reflection
{
/// <summary>
/// Print a list of decoder plugins, followed by their supported suffixes and MIME types.
/// </summary>
public class DecodersCommand : IMpcCommand<IEnumerable<MpdDecoderPlugin>>
{
public string Value => "decoders";
public IEnumerable<MpdDecoderPlugin> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = new List<MpdDecoderPlugin>();
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;
}
}
}

View File

@ -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.
/// <summary>
/// Shows a list of available song metadata.
/// </summary>
public class TagTypesCommand : IMpcCommand<IEnumerable<string>>
{
public string Value => "tagtypes";
public IEnumerable<string> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = response.Where(item => item.Key.Equals("tagtype")).Select(item => item.Value);
return result;
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Linq;
namespace MpcNET.Commands.Reflection
{
/// <summary>
/// Gets a list of available URL handlers.
/// </summary>
public class UrlHandlersCommand : IMpcCommand<IEnumerable<string>>
{
public string Value => "urlhandlers";
public IEnumerable<string> FormatResponse(IList<KeyValuePair<string, string>> response)
{
var result = response.Where(item => item.Key.Equals("handler")).Select(item => item.Value);
return result;
}
}
}

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
using MpcNET.Types;
namespace MpcNET.Commands.Status
{
internal class CurrentSongCommand : IMpcCommand<IMpdFile>
{
public string Value => "currentsong";
public IMpdFile FormatResponse(IList<KeyValuePair<string, string>> response)
{
return MpdFile.Create(response, 0).mpdFile;
}
}
}

View File

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace MpcNET.Commands.Status
{
internal class StatusCommand : IMpcCommand<MpdStatus>
{
public string Value => "status";
public MpdStatus FormatResponse(IList<KeyValuePair<string, string>> 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);
}
}
}

View File

@ -0,0 +1,10 @@
namespace MpcNET.Message
{
public interface IMpdMessage<T>
{
IMpdRequest<T> Request { get; }
IMpdResponse<T> Response { get; }
bool IsResponseValid { get; }
}
}

View File

@ -6,12 +6,6 @@ using Newtonsoft.Json;
namespace MpcNET.Message
{
public interface IMpdMessage<T>
{
IMpdRequest<T> Request { get; }
IMpdResponse<T> Response { get; }
}
[DebuggerDisplay("Request: {Request.Command.Value} | Response Status: {Response.State.Status}")]
public class MpdMessage<T> : IMpdMessage<T>
{
@ -30,8 +24,11 @@ namespace MpcNET.Message
}
public IMpdRequest<T> Request { get; }
public IMpdResponse<T> Response { get; }
public bool IsResponseValid => this.Response.State.Status == "OK";
private IList<KeyValuePair<string, string>> GetValuesFromResponse()
{
var result = new List<KeyValuePair<string, string>>();

View File

@ -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
/// </summary>
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<IMpdMessage<T>> SendAsync<T>(IMpcCommand<T> 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<string>();
// 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;

View File

@ -1,10 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>MpcNET</AssemblyName>
<PackageId>MpcNET</PackageId>
<NetStandardImplicitPackageVersion>1.6.0</NetStandardImplicitPackageVersion>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
@ -17,7 +16,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<None Include="Commands\Commands.Status.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
</ItemGroup>
</Project>

25
src/MpcNET/MpdState.cs Normal file
View File

@ -0,0 +1,25 @@
namespace MpcNET
{
/// <summary>
/// The possible states of the MPD.
/// </summary>
public enum MpdState
{
/// <summary>
/// The state of the MPD could not be translated into this enumeration.
/// </summary>
Unknown,
/// <summary>
/// The MPD is playing a track.
/// </summary>
Play,
/// <summary>
/// The MPD is not playing a track.
/// </summary>
Stop,
/// <summary>
/// The playback of the MPD is currently paused.
/// </summary>
Pause
}
}

View File

@ -1,119 +1,13 @@
using System.Text;
using System;
using System.Text;
namespace MpcNET
{
/// <summary>
/// The possible states of the MPD.
/// </summary>
public enum MpdState
{
/// <summary>
/// The state of the MPD could not be translated into this enumeration.
/// </summary>
Unknown,
/// <summary>
/// The MPD is playing a track.
/// </summary>
Play,
/// <summary>
/// The MPD is not playing a track.
/// </summary>
Stop,
/// <summary>
/// The playback of the MPD is currently paused.
/// </summary>
Pause
}
/// <summary>
/// The MpdStatus class contains all values describing the current status of the MPD.
/// </summary>
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;
/// <summary>
/// The current volume of the output.
/// </summary>
public int Volume { get { return this.volume; } }
/// <summary>
/// If the playlist is repeated after finish.
/// </summary>
public bool Repeat { get { return this.repeat; } }
/// <summary>
/// If the playlist is played in random order.
/// </summary>
public bool Random { get { return this.random; } }
/// <summary>
/// The version number of the playlist.
/// </summary>
public int Playlist { get { return this.playlist; } }
/// <summary>
/// The length of the playlist.
/// </summary>
public int PlaylistLength { get { return this.playlistLength; } }
/// <summary>
/// The number of seconds crossfaded between song changes.
/// </summary>
public int XFade { get { return this.xFade; } }
/// <summary>
/// The state of the MPD.
/// </summary>
public MpdState State { get { return this.state; } }
/// <summary>
/// The index of the currently played song in the playlist.
/// </summary>
public int Song { get { return this.song; } }
/// <summary>
/// The id of the song currently played.
/// </summary>
public int SongId { get { return this.songId; } }
/// <summary>
/// The number of seconds already played of the current song.
/// </summary>
public int TimeElapsed { get { return this.timeElapsed; } }
/// <summary>
/// The length of the current song in seconds.
/// </summary>
public int TimeTotal { get { return this.timeTotal; } }
/// <summary>
/// The bitrate of the current song.
/// </summary>
public int Bitrate { get { return this.bitrate; } }
/// <summary>
/// The audio sample rate of the current song.
/// </summary>
public int AudioSampleRate { get { return this.audioSampleRate; } }
/// <summary>
/// The audio bits of the current song.
/// </summary>
public int AudioBits { get { return this.audioBits; } }
/// <summary>
/// The number of audio channels of the current song.
/// </summary>
public int AudioChannels { get { return this.audioChannels; } }
/// <summary>
/// The number of the update on the MPD database currently running.
/// </summary>
public int UpdatingDb { get { return this.updatingDb; } }
/// <summary>
/// An error message, if there is an error.
/// </summary>
public string Error { get { return this.error; } }
/// <summary>
/// Creates a new MpdStatus object.
/// </summary>
@ -126,8 +20,10 @@ namespace MpcNET
/// <param name="state">The state of the MPD.</param>
/// <param name="song">The index of the currently played song in the playlist.</param>
/// <param name="songId">The id of the song currently played.</param>
/// <param name="timeElapsed">The number of seconds already played of the current song.</param>
/// <param name="timeTotal">The length of the current song in seconds.</param>
/// <param name="nextSong">The next song.</param>
/// <param name="nextSongId">The next song identifier.</param>
/// <param name="elapsed">The elapsed.</param>
/// <param name="duration">The duration.</param>
/// <param name="bitrate">The bitrate of the current song.</param>
/// <param name="audioSampleRate">The audio sample rate of the current song.</param>
/// <param name="audioBits">The audio bits of the current song.</param>
@ -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;
}
/// <summary>
/// The current volume of the output.
/// </summary>
public int Volume { get; }
/// <summary>
/// If the playlist is repeated after finish.
/// </summary>
public bool Repeat { get; }
/// <summary>
/// If the playlist is played in random order.
/// </summary>
public bool Random { get; }
/// <summary>
/// The version number of the playlist.
/// </summary>
public int Playlist { get; }
/// <summary>
/// The length of the playlist.
/// </summary>
public int PlaylistLength { get; }
/// <summary>
/// The number of seconds crossfaded between song changes.
/// </summary>
public int XFade { get; }
/// <summary>
/// The state of the MPD.
/// </summary>
public MpdState State { get; }
/// <summary>
/// The index of the currently played song in the playlist.
/// </summary>
public int Song { get; }
/// <summary>
/// The id of the song currently played.
/// </summary>
public int SongId { get; }
public int NextSong { get; }
public int NextSongId { get; }
/// <summary>
/// The number of seconds already played of the current song.
/// </summary>
public TimeSpan Elapsed { get; }
/// <summary>
/// The length of the current song in seconds.
/// </summary>
public TimeSpan Duration { get; }
/// <summary>
/// The bitrate of the current song.
/// </summary>
public int Bitrate { get; }
/// <summary>
/// The audio sample rate of the current song.
/// </summary>
public int AudioSampleRate { get; }
/// <summary>
/// The audio bits of the current song.
/// </summary>
public int AudioBits { get; }
/// <summary>
/// The number of audio channels of the current song.
/// </summary>
public int AudioChannels { get; }
/// <summary>
/// The number of the update on the MPD database currently running.
/// </summary>
public int UpdatingDb { get; }
/// <summary>
/// An error message, if there is an error.
/// </summary>
public string Error { get; }
/// <summary>
/// Returns a string representation of the object maily for debugging purpuses.
/// </summary>
@ -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(": ");

View File

@ -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

View File

@ -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<string, string> UnknownMetadata { get; }
IReadOnlyDictionary<string, string> UnknownMetadata { get; }
/// <summary>
/// If the MpdFile has the <see cref="Time"/> property set.
/// </summary>
bool HasTime { get; }
/// <summary>
/// If the MpdFile has the <see cref="Album"/> property set.
/// </summary>
bool HasAlbum { get; }
/// <summary>
/// If the MpdFile has the <see cref="Artist"/> property set.
/// </summary>
bool HasArtist { get; }
/// <summary>
/// If the MpdFile has the <see cref="Title"/> property set.
/// </summary>
bool HasTitle { get; }
/// <summary>
/// If the MpdFile has the <see cref="Track"/> property set.
/// </summary>
bool HasTrack { get; }
/// <summary>
/// If the MpdFile has the <see cref="Name"/> property set.
/// </summary>
bool HasName { get; }
/// <summary>
/// If the MpdFile has the <see cref="Genre"/> property set.
/// </summary>
bool HasGenre { get; }
/// <summary>
/// If the MpdFile has the <see cref="Date"/> property set.
/// </summary>
bool HasDate { get; }
/// <summary>
/// If the MpdFile has the <see cref="Composer"/> property set.
/// </summary>
bool HasComposer { get; }
/// <summary>
/// If the MpdFile has the <see cref="Performer"/> property set.
/// </summary>
bool HasPerformer { get; }
/// <summary>
/// If the MpdFile has the <see cref="Comment"/> property set.
/// </summary>
bool HasComment { get; }
/// <summary>
/// If the MpdFile has the <see cref="Disc"/> property set.
/// </summary>
bool HasDisc { get; }
/// <summary>
/// If the MpdFile has the <see cref="Pos"/> property set.
/// </summary>
bool HasPos { get; }
/// <summary>
/// If the MpdFile has the <see cref="Id"/> property set.
/// </summary>
bool HasId { get; }
}
}

View File

@ -0,0 +1,7 @@
namespace MpcNET.Types
{
public interface IMpdFilePath
{
string Path { get; }
}
}

View File

@ -1,5 +1,4 @@
using System.Collections.Generic;
using MpcNET.Utils;
namespace MpcNET.Types
{
@ -8,97 +7,324 @@ namespace MpcNET.Types
/// </summary>
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<string, string> _unknownMetadata = new Dictionary<string, string>();
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<string, string> 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<string, string>();
}
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<string, string> 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<string, string> UnknownMetadata { get; }
internal void AddTag(string tag, string value)
/// <summary>
/// If the MpdFile has the <see cref="Time"/> property set.
/// </summary>
public bool HasTime => this.Time != NoTime;
/// <summary>
/// If the MpdFile has the <see cref="Album"/> property set.
/// </summary>
public bool HasAlbum => this.Album != NoAlbum;
/// <summary>
/// If the MpdFile has the <see cref="Artist"/> property set.
/// </summary>
public bool HasArtist => this.Artist != NoArtist;
/// <summary>
/// If the MpdFile has the <see cref="Title"/> property set.
/// </summary>
public bool HasTitle => this.Title != NoTitle;
/// <summary>
/// If the MpdFile has the <see cref="Track"/> property set.
/// </summary>
public bool HasTrack => this.Track != NoTrack;
/// <summary>
/// If the MpdFile has the <see cref="Name"/> property set.
/// </summary>
public bool HasName => this.Name != NoName;
/// <summary>
/// If the MpdFile has the <see cref="Genre"/> property set.
/// </summary>
public bool HasGenre => this.Genre != NoGenre;
/// <summary>
/// If the MpdFile has the <see cref="Date"/> property set.
/// </summary>
public bool HasDate => this.Date != NoDate;
/// <summary>
/// If the MpdFile has the <see cref="Composer"/> property set.
/// </summary>
public bool HasComposer => this.Composer != NoComposer;
/// <summary>
/// If the MpdFile has the <see cref="Performer"/> property set.
/// </summary>
public bool HasPerformer => this.Performer != NoPerformer;
/// <summary>
/// If the MpdFile has the <see cref="Comment"/> property set.
/// </summary>
public bool HasComment => this.Comment != NoComment;
/// <summary>
/// If the MpdFile has the <see cref="Disc"/> property set.
/// </summary>
public bool HasDisc => this.Disc != NoDisc;
/// <summary>
/// If the MpdFile has the <see cref="Pos"/> property set.
/// </summary>
public bool HasPos => this.Pos != NoPos;
/// <summary>
/// If the MpdFile has the <see cref="Id"/> property set.
/// </summary>
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<KeyValuePair<string, string>> 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<string, string>();
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<IMpdFile> CreateList(IList<KeyValuePair<string, string>> response)
{
var mpdFiles = new List<IMpdFile>();
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;
}
}
}