1
0
mirror of https://github.com/ZetaKebab/MpcNET.git synced 2025-07-01 00:37:37 +00:00

Updates from latest Stylophone code

This commit is contained in:
Difegue
2021-10-03 17:53:58 +02:00
parent 6ee33cd704
commit 6c473b01b7
85 changed files with 2395 additions and 952 deletions

View File

@ -52,9 +52,9 @@
return this.command;
}
public IList<string> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IList<string> Deserialize(SerializedResponse response)
{
var result = response.Select(atrb => $"{atrb.Key}: {atrb.Value}").ToList();
var result = response.ResponseValues.Select(atrb => $"{atrb.Key}: {atrb.Value}").ToList();
return result;
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyName>MpcNET.Test</AssemblyName>
<PackageId>MpcNET.Test</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>

Binary file not shown.

View File

@ -4,6 +4,7 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MpcNET.Commands;
using MpcNET.Commands.Queue;
public partial class LibMpcTest
{
@ -117,7 +118,7 @@
private async Task Check_Empty_Queue()
{
var message = await Mpc.SendAsync(new Commands.Playlist.PlaylistCommand());
var message = await Mpc.SendAsync(new PlaylistCommand());
Assert.IsTrue(message.HasSuccessResponse());
Assert.IsFalse(message.Response.Content.Any());
}
@ -130,44 +131,44 @@
private async Task Clear_Queue()
{
var message = await Mpc.SendAsync(new Commands.Playlist.ClearCommand());
var message = await Mpc.SendAsync(new ClearCommand());
Assert.IsTrue(message.HasSuccessResponse());
}
private async Task Check_Queue_HasSongs(int nrOfSongs)
{
var message = await Mpc.SendAsync(new Commands.Playlist.PlaylistCommand());
var message = await Mpc.SendAsync(new PlaylistCommand());
Assert.IsTrue(message.HasSuccessResponse());
Assert.IsTrue(message.Response.Content.Count() == nrOfSongs);
}
private async Task Add_Directory(string directory)
{
var message = await Mpc.SendAsync(new Commands.Playlist.AddCommand(directory));
var message = await Mpc.SendAsync(new AddCommand(directory));
Assert.IsTrue(message.HasSuccessResponse());
}
private async Task Add_File(string file)
{
var message = await Mpc.SendAsync(new Commands.Playlist.AddIdCommand(file));
var message = await Mpc.SendAsync(new AddIdCommand(file));
Assert.IsTrue(message.HasSuccessResponse());
}
private async Task Remove_Position(int position)
{
var message = await Mpc.SendAsync(new Commands.Playlist.DeleteCommand(position));
var message = await Mpc.SendAsync(new DeleteCommand(position));
Assert.IsTrue(message.HasSuccessResponse());
}
private async Task Remove_Id(int songId)
{
var message = await Mpc.SendAsync(new Commands.Playlist.DeleteIdCommand(songId));
var message = await Mpc.SendAsync(new DeleteIdCommand(songId));
Assert.IsTrue(message.HasSuccessResponse());
}
private async Task<int> Get_Song_Id()
{
var message = await Mpc.SendAsync(new Commands.Playlist.PlaylistInfoCommand());
var message = await Mpc.SendAsync(new PlaylistInfoCommand());
return message.Response.Content.Single().Id;
}
}

View File

@ -33,7 +33,7 @@ namespace MpcNET.Test
TestOutput.WriteLine("TagTypesTest Result:");
TestOutput.WriteLine(response);
Assert.IsTrue(response.Response.Content.Count().Equals(17));
Assert.IsTrue(response.Response.Content.Count().Equals(25));
}
[TestMethod]
@ -47,9 +47,7 @@ namespace MpcNET.Test
// Different answer from MPD on Windows and on Linux.
// Check some of the handlers.
Assert.IsTrue(response.Response.Content.Any(handler => handler.Equals("http://")));
Assert.IsTrue(response.Response.Content.Any(handler => handler.Equals("mms://")));
Assert.IsTrue(response.Response.Content.Any(handler => handler.Equals("gopher://")));
Assert.IsTrue(response.Response.Content.Any(handler => handler.Equals("rtp://")));
Assert.IsTrue(response.Response.Content.Any(handler => handler.Equals("nfs://")));
}
[TestMethod]
@ -62,7 +60,7 @@ namespace MpcNET.Test
// Different answer from MPD on Windows and on Linux.
// Check some of the decoders.
Assert.IsTrue(response.Response.Content.Any(decoder => decoder.Name.Equals("mad")));
Assert.IsTrue(response.Response.Content.Any(decoder => decoder.Name.Equals("vorbis")));
Assert.IsTrue(response.Response.Content.Any(decoder => decoder.Suffixes.Any(suffix => suffix.Equals("mp3"))));
Assert.IsTrue(response.Response.Content.Any(decoder => decoder.MediaTypes.Any(mediaType => mediaType.Equals("audio/mpeg"))));
Assert.IsTrue(response.Response.Content.Any(decoder => decoder.Name.Equals("flac")));

View File

@ -1,13 +1,8 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29324.140
# Visual Studio Version 17
VisualStudioVersion = 17.0.31710.8
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Utilities", "Solution Utilities", "{83D06F7C-1336-4AC3-82BB-FDE7940D3C10}"
ProjectSection(SolutionItems) = preProject
.travis.yml = .travis.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MpcNET", "MpcNET\MpcNET.csproj", "{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MpcNET.Test", "MpcNET.Test\MpcNET.Test.csproj", "{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}"
@ -15,22 +10,112 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|iPhone = Debug|iPhone
Debug|iPhoneSimulator = Debug|iPhoneSimulator
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|iPhone = Release|iPhone
Release|iPhoneSimulator = Release|iPhoneSimulator
Release|x64 = Release|x64
Release|x86 = Release|x86
Release-Stable|Any CPU = Release-Stable|Any CPU
Release-Stable|ARM = Release-Stable|ARM
Release-Stable|ARM64 = Release-Stable|ARM64
Release-Stable|iPhone = Release-Stable|iPhone
Release-Stable|iPhoneSimulator = Release-Stable|iPhoneSimulator
Release-Stable|x64 = Release-Stable|x64
Release-Stable|x86 = Release-Stable|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|ARM.ActiveCfg = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|ARM.Build.0 = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|ARM64.Build.0 = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|iPhone.Build.0 = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|x64.ActiveCfg = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|x64.Build.0 = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|x86.ActiveCfg = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Debug|x86.Build.0 = Debug|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|Any CPU.Build.0 = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|ARM.ActiveCfg = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|ARM.Build.0 = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|ARM64.ActiveCfg = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|ARM64.Build.0 = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|iPhone.ActiveCfg = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|iPhone.Build.0 = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|x64.ActiveCfg = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|x64.Build.0 = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|x86.ActiveCfg = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release|x86.Build.0 = Release|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|Any CPU.ActiveCfg = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|Any CPU.Build.0 = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|ARM.ActiveCfg = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|ARM.Build.0 = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|ARM64.ActiveCfg = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|ARM64.Build.0 = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|iPhone.ActiveCfg = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|iPhone.Build.0 = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|iPhoneSimulator.ActiveCfg = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|iPhoneSimulator.Build.0 = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|x64.ActiveCfg = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|x64.Build.0 = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|x86.ActiveCfg = Release-Stable|Any CPU
{8994C820-7BA9-4BB8-B9EA-C608B07C4A11}.Release-Stable|x86.Build.0 = Release-Stable|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|ARM.ActiveCfg = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|ARM.Build.0 = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|ARM64.Build.0 = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|iPhone.Build.0 = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|x64.ActiveCfg = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|x64.Build.0 = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|x86.ActiveCfg = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Debug|x86.Build.0 = Debug|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|Any CPU.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|Any CPU.ActiveCfg = Release-Stable|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|Any CPU.Build.0 = Release-Stable|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|ARM.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|ARM.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|ARM64.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|ARM64.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|iPhone.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|iPhone.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|x64.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|x64.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|x86.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release|x86.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|Any CPU.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|Any CPU.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|ARM.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|ARM.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|ARM64.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|ARM64.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|iPhone.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|iPhone.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|iPhoneSimulator.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|iPhoneSimulator.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|x64.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|x64.Build.0 = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|x86.ActiveCfg = Release|Any CPU
{69F1D68F-9CD5-4EA6-9B47-2A7A9BF8CED9}.Release-Stable|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,52 @@
using MpcNET.Types;
using System.Linq;
namespace MpcNET.Commands.Database
{
/// <summary>
/// Gets the album art for the given song.
/// https://www.musicpd.org/doc/html/protocol.html#the-music-database
/// </summary>
public class AlbumArtCommand : IMpcCommand<MpdBinaryData>
{
private readonly string path;
private readonly long binaryOffset;
/// <summary>
/// Initializes a new instance of the <see cref="AlbumArtCommand"/> class.
/// </summary>
/// <param name="path">The URI.</param>
/// <param name="offset">Binary data offset if needed</param>
public AlbumArtCommand(string path, long offset = 0)
{
this.path = path;
this.binaryOffset = offset;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => $"albumart \"{path}\" {binaryOffset}";
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public MpdBinaryData Deserialize(SerializedResponse response)
{
if (response.ResponseValues.Count == 0)
return null;
var totalSize = long.Parse(response.ResponseValues.Where(kvp => kvp.Key == "size").Select(kvp => kvp.Value).First());
var payloadSize = long.Parse(response.ResponseValues.Where(kvp => kvp.Key == "binary").Select(kvp => kvp.Value).First());
return new MpdBinaryData(totalSize, payloadSize, response.BinaryData);
}
}
}

View File

@ -0,0 +1,207 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="FindCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Database
{
using System.Collections.Generic;
using System.Linq;
using MpcNET.Tags;
using MpcNET.Types;
/// <summary>
/// Finds songs in the database that contain "searchText".
/// Since MPD 0.21, search syntax is now (TAG == 'VALUE').
/// https://www.musicpd.org/doc/html/protocol.html#filters
/// </summary>
public class SearchCommand : BaseFilterCommand
{
/// <summary>
///
/// </summary>
public override string CommandName => "search";
/// <summary>
///
/// </summary>
public override string Operand => "contains";
/// <summary>
/// Initializes a new instance of the <see cref="SearchCommand"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="searchText">The search text.</param>
/// <param name="windowStart">Start of the portion of the results desired</param>
/// <param name="windowEnd">End of the portion of the results desired</param>
public SearchCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1) : base(tag, searchText, windowStart, windowEnd) { }
/// <summary>
/// Initializes a new instance of the <see cref="SearchCommand"/> class.
/// </summary>
/// <param name="filters">List of key/value filters</param>
/// <param name="windowStart">Start of the portion of the results desired</param>
/// <param name="windowEnd">End of the portion of the results desired</param>
public SearchCommand(List<KeyValuePair<ITag, string>> filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
}
/// <summary>
/// Finds songs in the database that contain "searchText" and adds them to the queue.
/// Since MPD 0.21, search syntax is now (TAG == 'VALUE').
/// https://www.musicpd.org/doc/html/protocol.html#filters
/// </summary>
public class SearchAddCommand : BaseFilterCommand
{
/// <summary>
///
/// </summary>
public override string CommandName => "searchadd";
/// <summary>
///
/// </summary>
public override string Operand => "contains";
/// <summary>
/// Initializes a new instance of the <see cref="SearchCommand"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="searchText">The search text.</param>
/// <param name="windowStart">Start of the portion of the results desired</param>
/// <param name="windowEnd">End of the portion of the results desired</param>
public SearchAddCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1) : base(tag, searchText, windowStart, windowEnd) { }
/// <summary>
/// Initializes a new instance of the <see cref="SearchCommand"/> class.
/// </summary>
/// <param name="filters">List of key/value filters</param>
/// <param name="windowStart">Start of the portion of the results desired</param>
/// <param name="windowEnd">End of the portion of the results desired</param>
public SearchAddCommand(List<KeyValuePair<ITag, string>> filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
}
/// <summary>
/// Finds songs in the database that is exactly "searchText".
/// Since MPD 0.21, search syntax is now (TAG == 'VALUE').
/// https://www.musicpd.org/doc/html/protocol.html#filters
/// </summary>
public class FindCommand : BaseFilterCommand
{
/// <summary>
///
/// </summary>
public override string CommandName => "find";
/// <summary>
///
/// </summary>
public override string Operand => "==";
/// <summary>
/// Initializes a new instance of the <see cref="FindCommand"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="searchText">The search text.</param>
/// <param name="windowStart">Start of the portion of the results desired</param>
/// <param name="windowEnd">End of the portion of the results desired</param>
public FindCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1) : base(tag, searchText, windowStart, windowEnd) { }
/// <summary>
/// Initializes a new instance of the <see cref="FindCommand"/> class.
/// </summary>
/// <param name="filters">List of key/value filters</param>
/// <param name="windowStart">Start of the portion of the results desired</param>
/// <param name="windowEnd">End of the portion of the results desired</param>
public FindCommand(List<KeyValuePair<ITag, string>> filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
}
/// <summary>
/// Base class for find/search commands.
/// </summary>
public abstract class BaseFilterCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
private readonly List<KeyValuePair<ITag, string>> filters;
private readonly int _start;
private readonly int _end;
/// <summary>
/// Name of the command to use when deserializing
/// </summary>
public abstract string CommandName { get; }
/// <summary>
/// Operand to use between tags and search text. Can be ==, !=, contains...
/// </summary>
public abstract string Operand { get; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseFilterCommand"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="searchText">The search text.</param>
/// <param name="windowStart">Start of the portion of the results desired</param>
/// <param name="windowEnd">End of the portion of the results desired</param>
public BaseFilterCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1)
{
this.filters = new List<KeyValuePair<ITag, string>>();
this.filters.Add(new KeyValuePair<ITag, string>(tag, searchText));
_start = windowStart;
_end = windowEnd;
}
/// <summary>
/// Initializes a new instance of the <see cref="BaseFilterCommand"/> class.
/// </summary>
/// <param name="filters">List of key/value filters</param>
/// <param name="windowStart">Start of the portion of the results desired</param>
/// <param name="windowEnd">End of the portion of the results desired</param>
public BaseFilterCommand(List<KeyValuePair<ITag, string>> filters, int windowStart = -1, int windowEnd = -1)
{
this.filters = filters;
_start = windowStart;
_end = windowEnd;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize()
{
var cmd =
CommandName + " \"(" +
string.Join(" AND ",
filters.Select(x => $"({x.Key.Value} {Operand} {escape(x.Value)})")
) +
")\"";
if (_start > -1)
{
cmd += $" window {_start}:{_end}";
}
return cmd;
}
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<IMpdFile> Deserialize(SerializedResponse response)
{
return MpdFile.CreateList(response.ResponseValues);
}
private string escape(string value) => string.Format("\\\"{0}\\\"", value.Replace("\\", "\\\\\\").Replace("'", "\\\\'").Replace("\"", "\\\\\\\""));
}
// TODO: rescan
}

View File

@ -11,7 +11,7 @@ namespace MpcNET.Commands.Database
using MpcNET.Types;
/// <summary>
/// Lists all songs and directories in URI.
/// Lists all songs and directories.
/// https://www.musicpd.org/doc/protocol/database.html.
/// </summary>
public class ListAllCommand : IMpcCommand<IEnumerable<MpdDirectory>>
@ -31,14 +31,14 @@ namespace MpcNET.Commands.Database
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<MpdDirectory> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IEnumerable<MpdDirectory> Deserialize(SerializedResponse response)
{
var rootDirectory = new List<MpdDirectory>
{
new MpdDirectory("/"), // Add by default the root directory
};
foreach (var line in response)
foreach (var line in response.ResponseValues)
{
if (line.Key.Equals("file"))
{

View File

@ -63,9 +63,9 @@ namespace MpcNET.Commands.Database
/// <returns>
/// The deserialized response.
/// </returns>
public List<string> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public List<string> Deserialize(SerializedResponse response)
{
return response.Select(x => x.Value).ToList();
return response.ResponseValues.Select(x => x.Value).ToList();
}
private string escape(string value) => string.Format("\"{0}\"", value.Replace("\\", "\\\\").Replace("\"", "\\\""));

View File

@ -0,0 +1,68 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="LsInfoCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Database
{
using System;
using System.Collections.Generic;
using System.Linq;
using MpcNET.Types;
/// <summary>
/// Lists the contents of the directory URI. The response contains records starting with file, directory or playlist, each followed by metadata
/// https://www.musicpd.org/doc/protocol/database.html.
/// </summary>
public class LsInfoCommand : IMpcCommand<IEnumerable<IMpdFilePath>>
{
private readonly string uri;
/// <summary>
/// Initializes a new instance of the <see cref="LsInfoCommand"/> class.
/// </summary>
/// <param name="uri">The uri.</param>
public LsInfoCommand(string uri)
{
this.uri = uri;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => $"lsinfo \"{uri}\"";
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<IMpdFilePath> Deserialize(SerializedResponse response)
{
var rootDirectory = new List<IMpdFilePath>();
foreach (var line in response.ResponseValues)
{
// lsinfo can also return playlists, but this is a deprecated behavior I'm entirely willing to not support.
if (line.Key.Equals("file"))
{
rootDirectory.Add(new MpdFile(line.Value));
}
if (line.Key.Equals("directory"))
{
rootDirectory.Add(new MpdDirectory(line.Value));
}
}
return rootDirectory;
}
}
}

View File

@ -0,0 +1,56 @@
using MpcNET.Types;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
namespace MpcNET.Commands.Database
{
/// <summary>
/// Gets the album art for the given song, using ID3 metadata.
/// https://www.musicpd.org/doc/html/protocol.html#the-music-database
/// </summary>
public class ReadPictureCommand : IMpcCommand<MpdBinaryData>
{
private readonly string path;
private readonly long binaryOffset;
/// <summary>
/// Initializes a new instance of the <see cref="ReadPictureCommand"/> class.
/// </summary>
/// <param name="path">The URI.</param>
/// <param name="offset">Binary data offset if needed</param>
public ReadPictureCommand(string path, long offset = 0)
{
this.path = path;
this.binaryOffset = offset;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => $"readpicture \"{path}\" {binaryOffset}";
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public MpdBinaryData Deserialize(SerializedResponse response)
{
if (response.ResponseValues.Count == 0)
return null;
var totalSize = long.Parse(response.ResponseValues.Where(kvp => kvp.Key == "size").Select(kvp => kvp.Value).First());
var payloadSize = long.Parse(response.ResponseValues.Where(kvp => kvp.Key == "binary").Select(kvp => kvp.Value).First());
return new MpdBinaryData(totalSize, payloadSize, response.BinaryData);
}
}
}

View File

@ -67,9 +67,9 @@ namespace MpcNET.Commands.Database
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -40,10 +40,10 @@ namespace MpcNET.Commands.Output
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
// Response should be empty.
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -40,10 +40,10 @@ namespace MpcNET.Commands.Output
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
// Response should be empty.
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -7,6 +7,7 @@
namespace MpcNET.Commands.Output
{
using System.Collections.Generic;
using System.Linq;
using MpcNET.Types;
/// <summary>
@ -30,17 +31,21 @@ namespace MpcNET.Commands.Output
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<MpdOutput> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IEnumerable<MpdOutput> Deserialize(SerializedResponse 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";
// Strip out attributes so we can keep parsing the response by blocks of 4
var strippedResult = response.ResponseValues.Where(kvp => kvp.Key != "attribute").ToList();
result.Add(new MpdOutput(outputId, outputName, outputEnabled));
for (var i = 0; i < strippedResult.Count; i+=4)
{
var outputId = int.Parse(strippedResult[i].Value);
var outputName = strippedResult[i + 1].Value;
var outputPlugin = strippedResult[i + 2].Value;
var outputEnabled = strippedResult[i + 3].Value == "1";
result.Add(new MpdOutput(outputId, outputName, outputPlugin, outputEnabled));
}
return result;

View File

@ -40,9 +40,9 @@ namespace MpcNET.Commands.Output
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -29,9 +29,9 @@ namespace MpcNET.Commands.Playback
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -55,9 +55,9 @@ namespace MpcNET.Commands.Playback
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -49,9 +49,9 @@ namespace MpcNET.Commands.Playback
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -49,9 +49,9 @@ namespace MpcNET.Commands.Playback
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -29,9 +29,9 @@ namespace MpcNET.Commands.Playback
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,63 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PauseResumeCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Playback
{
using System.Collections.Generic;
/// <summary>
/// Command to set random state.
/// https://www.musicpd.org/doc/html/protocol.html#playback-options
/// </summary>
public class RandomCommand : IMpcCommand<string>
{
private readonly string playArgument;
/// <summary>
/// Initializes a new instance of the <see cref="RandomCommand" /> class.
/// </summary>
/// <param name="random">if set to <c>true</c> [random].</param>
public RandomCommand(bool random)
{
this.playArgument = random ? "1" : "0";
}
/// <summary>
/// Initializes a new instance of the <see cref="RandomCommand"/> class.
/// </summary>
public RandomCommand()
{
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize()
{
if (this.playArgument == null)
{
return string.Join(" ", "random");
}
return string.Join(" ", "random", this.playArgument);
}
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,63 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PauseResumeCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Playback
{
using System.Collections.Generic;
/// <summary>
/// Command to set repeat state.
/// https://www.musicpd.org/doc/html/protocol.html#status_commands
/// </summary>
public class RepeatCommand : IMpcCommand<string>
{
private readonly string playArgument;
/// <summary>
/// Initializes a new instance of the <see cref="RepeatCommand" /> class.
/// </summary>
/// <param name="repeat">if set to <c>true</c> [repeat].</param>
public RepeatCommand(bool repeat)
{
this.playArgument = repeat ? "1" : "0";
}
/// <summary>
/// Initializes a new instance of the <see cref="RepeatCommand"/> class.
/// </summary>
public RepeatCommand()
{
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize()
{
if (this.playArgument == null)
{
return string.Join(" ", "repeat");
}
return string.Join(" ", "repeat", this.playArgument);
}
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,52 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SetVolumeCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Playback
{
using System.Collections.Generic;
/// <summary>
/// Seeks to the position TIME (in seconds; fractions allowed) within the current song.
/// https://www.musicpd.org/doc/html/protocol.html#status_commands
/// </summary>
public class SeekCurCommand : IMpcCommand<string>
{
private readonly double time;
/// <summary>
/// Initializes a new instance of the <see cref="SeekCurCommand"/> class.
/// </summary>
/// <param name="time">The time.</param>
public SeekCurCommand(double time)
{
this.time = time;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize()
{
return string.Join(" ", "seekcur", this.time);
}
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -44,9 +44,9 @@ namespace MpcNET.Commands.Playback
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,64 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PauseResumeCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Playback
{
using System.Collections.Generic;
/// <summary>
/// Command to set single state.
/// When single is activated, playback is stopped after current song, or song is repeated if the repeat mode is enabled.
/// https://www.musicpd.org/doc/html/protocol.html#status_commands
/// </summary>
public class SingleCommand : IMpcCommand<string>
{
private readonly string playArgument;
/// <summary>
/// Initializes a new instance of the <see cref="SingleCommand" /> class.
/// </summary>
/// <param name="single">if set to <c>true</c> [single].</param>
public SingleCommand(bool single)
{
this.playArgument = single ? "1" : "0";
}
/// <summary>
/// Initializes a new instance of the <see cref="SingleCommand"/> class.
/// </summary>
public SingleCommand()
{
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize()
{
if (this.playArgument == null)
{
return string.Join(" ", "single");
}
return string.Join(" ", "single", this.playArgument);
}
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -29,9 +29,9 @@ namespace MpcNET.Commands.Playback
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -42,9 +42,9 @@ namespace MpcNET.Commands.Playlist
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<IMpdFilePath> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IEnumerable<IMpdFilePath> Deserialize(SerializedResponse response)
{
var results = response.Where(line => line.Key.Equals("file")).Select(line => new MpdFile(line.Value));
var results = response.ResponseValues.Where(line => line.Key.Equals("file")).Select(line => new MpdFile(line.Value));
return results;
}

View File

@ -41,9 +41,9 @@ namespace MpcNET.Commands.Playlist
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<IMpdFile> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IEnumerable<IMpdFile> Deserialize(SerializedResponse response)
{
return MpdFile.CreateList(response);
return MpdFile.CreateList(response.ResponseValues);
}
}
}

View File

@ -31,11 +31,11 @@ namespace MpcNET.Commands.Playlist
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<MpdPlaylist> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IEnumerable<MpdPlaylist> Deserialize(SerializedResponse response)
{
var result = new List<MpdPlaylist>();
foreach (var line in response)
foreach (var line in response.ResponseValues)
{
if (line.Key.Equals("playlist"))
{

View File

@ -40,9 +40,9 @@ namespace MpcNET.Commands.Playlist
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,50 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlaylistAddCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Adds URI to the playlist NAME.m3u. NAME.m3u will be created if it does not exist.
/// https://www.musicpd.org/doc/html/protocol.html#command-load
/// </summary>
public class PlaylistAddCommand : IMpcCommand<string>
{
private readonly string playlist;
private readonly string pathUri;
/// <summary>
/// Initializes a new instance of the <see cref="PlaylistAddCommand"/> class.
/// </summary>
/// <param name="playlistName">The playlistn name.</param>
/// <param name="uri">The path to add.</param>
public PlaylistAddCommand(string playlistName, string uri)
{
this.playlist = playlistName;
this.pathUri = uri;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "playlistadd", $"\"{playlist}\"", $"\"{pathUri}\"");
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,50 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlaylistDeleteCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Deletes SONGPOS from the playlist NAME.m3u.
/// https://www.musicpd.org/doc/html/protocol.html#stored-playlists
/// </summary>
public class PlaylistDeleteCommand : IMpcCommand<string>
{
private readonly string playlist;
private readonly int songpos;
/// <summary>
/// Initializes a new instance of the <see cref="PlaylistDeleteCommand"/> class.
/// </summary>
/// <param name="playlistName">The playlist name.</param>
/// <param name="songpos">Position of the song to remove</param>
public PlaylistDeleteCommand(string playlistName, int songpos)
{
this.playlist = playlistName;
this.songpos = songpos;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "playlistdelete", $"\"{playlist}\"", songpos);
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,53 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlaylistMoveCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Playlist
{
/// <summary>
/// Moves the song at position FROM in the playlist NAME.m3u to the position TO.
/// https://www.musicpd.org/doc/html/protocol.html#stored-playlists
/// </summary>
public class PlaylistMoveCommand : IMpcCommand<string>
{
private readonly string playlist;
private readonly int from;
private readonly int to;
/// <summary>
/// Initializes a new instance of the <see cref="PlaylistMoveCommand"/> class.
/// </summary>
/// <param name="playlistName">The playlist name.</param>
/// <param name="from">Position of the song to move</param>
/// <param name="to">New position of the song</param>
public PlaylistMoveCommand(string playlistName, int from, int to)
{
this.playlist = playlistName;
this.from = from;
this.to = to;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "playlistmove", $"\"{playlist}\"", from, to);
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,48 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="RmCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Playlist
{
using System.Collections.Generic;
/// <summary>
/// Removes the playlist NAME.m3u from the playlist directory.
/// https://www.musicpd.org/doc/html/protocol.html#stored-playlists
/// </summary>
public class RmCommand : IMpcCommand<string>
{
private readonly string playlistName;
/// <summary>
/// Initializes a new instance of the <see cref="RmCommand"/> class.
/// </summary>
/// <param name="playlistName">Name of the playlist.</param>
public RmCommand(string playlistName)
{
this.playlistName = playlistName;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "rm", $"\"{this.playlistName}\"");
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,48 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SaveCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Playlist
{
using System.Collections.Generic;
/// <summary>
/// Saves the queue to NAME.m3u in the playlist directory.
/// https://www.musicpd.org/doc/html/protocol.html#stored-playlists
/// </summary>
public class SaveCommand : IMpcCommand<string>
{
private readonly string playlistName;
/// <summary>
/// Initializes a new instance of the <see cref="SaveCommand"/> class.
/// </summary>
/// <param name="playlistName">Name of the playlist.</param>
public SaveCommand(string playlistName)
{
this.playlistName = playlistName;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "save", $"\"{this.playlistName}\"");
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,49 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AddCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using MpcNET;
using System.Collections.Generic;
/// <summary>
/// Adds the file URI to the playlist (directories add recursively). URI can also be a single file.
/// https://www.musicpd.org/doc/protocol/queue.html.
/// </summary>
public class AddCommand : IMpcCommand<string>
{
private readonly string uri;
/// <summary>
/// Initializes a new instance of the <see cref="AddCommand"/> class.
/// </summary>
/// <param name="uri">The URI.</param>
public AddCommand(string uri)
{
this.uri = uri;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "add", $"\"{uri}\"");
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,49 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AddIdCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using MpcNET;
using System.Collections.Generic;
/// <summary>
/// Adds a song to the playlist (non-recursive) and returns the song id.
/// https://www.musicpd.org/doc/protocol/queue.html.
/// </summary>
public class AddIdCommand : IMpcCommand<string>
{
private readonly string uri;
/// <summary>
/// Initializes a new instance of the <see cref="AddIdCommand"/> class.
/// </summary>
/// <param name="uri">The URI.</param>
public AddIdCommand(string uri)
{
this.uri = uri;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "addid", $"\"{uri}\"");
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,38 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ClearCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using System.Collections.Generic;
/// <summary>
/// Clears the current playlist.
/// https://www.musicpd.org/doc/protocol/queue.html.
/// </summary>
public class ClearCommand : IMpcCommand<string>
{
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => "clear";
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,49 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DeleteCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using MpcNET;
using System.Collections.Generic;
/// <summary>
/// Deletes a song from the playlist.
/// https://www.musicpd.org/doc/protocol/queue.html.
/// </summary>
public class DeleteCommand : IMpcCommand<string>
{
private readonly int position;
/// <summary>
/// Initializes a new instance of the <see cref="DeleteCommand"/> class.
/// </summary>
/// <param name="position">The position.</param>
public DeleteCommand(int position)
{
this.position = position;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "delete", position);
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,49 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DeleteIdCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using MpcNET;
using System.Collections.Generic;
/// <summary>
/// Deletes the song SONGID from the playlist.
/// https://www.musicpd.org/doc/protocol/queue.html.
/// </summary>
public class DeleteIdCommand : IMpcCommand<string>
{
private readonly int songId;
/// <summary>
/// Initializes a new instance of the <see cref="DeleteIdCommand"/> class.
/// </summary>
/// <param name="songId">The song identifier.</param>
public DeleteIdCommand(int songId)
{
this.songId = songId;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "deleteid", songId);
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,53 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AddCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using MpcNET;
using System.Collections.Generic;
/// <summary>
/// Moves the song with FROM (songid) to TO (playlist index) in the playlist.
/// If TO is negative, it is relative to the current song in the playlist (if there is one)
/// https://www.musicpd.org/doc/protocol/queue.html.
/// </summary>
public class MoveIdCommand : IMpcCommand<string>
{
private readonly int from;
private readonly int to;
/// <summary>
/// Initializes a new instance of the <see cref="MoveIdCommand"/> class.
/// </summary>
/// <param name="from">From (songid)</param>
/// <param name="to">To (playlist index)</param>
public MoveIdCommand(int from, int to)
{
this.from = from;
this.to = to;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "moveid", from, to);
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,50 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlChangesCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using System.Collections.Generic;
using MpcNET.Types;
/// <summary>
/// Displays changed songs currently in the playlist since VERSION.
/// https://www.musicpd.org/doc/html/protocol.html#the-queue.
/// </summary>
public class PlChangesCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
private readonly string version;
/// <summary>
/// Initializes a new instance of the <see cref="PlChangesCommand"/> class.
/// </summary>
/// <param name="version">Version to compare to the current playlist.</param>
public PlChangesCommand(int version = -1)
{
this.version = version.ToString();
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => $"plchanges {version}";
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<IMpdFile> Deserialize(SerializedResponse response)
{
return MpdFile.CreateList(response.ResponseValues);
}
}
}

View File

@ -0,0 +1,42 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlaylistCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using System.Collections.Generic;
using System.Linq;
using MpcNET.Types;
/// <summary>
/// Displays the current playlist.
/// https://www.musicpd.org/doc/protocol/queue.html.
/// </summary>
public class PlaylistCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => "playlist";
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<IMpdFile> Deserialize(SerializedResponse response)
{
var results = response.ResponseValues.Select(line => MpdFile.Create(line.Value, int.Parse(line.Key)));
return results;
}
}
}

View File

@ -0,0 +1,50 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlaylistIdCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using System.Collections.Generic;
using MpcNET;
using MpcNET.Types;
/// <summary>
/// Displays song ID in the playlist.
/// https://www.musicpd.org/doc/protocol/queue.html.
/// </summary>
public class PlaylistIdCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
private readonly int songId;
/// <summary>
/// Initializes a new instance of the <see cref="PlaylistIdCommand"/> class.
/// </summary>
/// <param name="songId">The song identifier.</param>
public PlaylistIdCommand(int songId)
{
this.songId = songId;
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => string.Join(" ", "playlistid", songId);
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<IMpdFile> Deserialize(SerializedResponse response)
{
return MpdFile.CreateList(response.ResponseValues);
}
}
}

View File

@ -0,0 +1,39 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlaylistInfoCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Queue
{
using System.Collections.Generic;
using MpcNET.Types;
/// <summary>
/// Displays a list of all songs in the playlist.
/// https://www.musicpd.org/doc/protocol/queue.html.
/// </summary>
public class PlaylistInfoCommand : IMpcCommand<IEnumerable<IMpdFile>>
{
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => "playlistinfo";
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<IMpdFile> Deserialize(SerializedResponse response)
{
return MpdFile.CreateList(response.ResponseValues);
}
}
}

View File

@ -0,0 +1,74 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CommandList.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Reflection
{
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// To facilitate faster adding of files etc. you can pass a list of commands all at once using a command list.
/// The command list begins with command_list_begin or command_list_ok_begin and ends with command_list_end.
/// https://www.musicpd.org/doc/html/protocol.html#command-lists.
/// </summary>
public class CommandList : IMpcCommand<string>
{
private readonly List<IMpcCommand<object>> commands;
/// <summary>
/// Initializes a new instance of the <see cref="CommandList"/> class.
/// </summary>
/// <param name="mpcCommands">IMpcCommand items to add to the list.</param>
public CommandList(IEnumerable<IMpcCommand<object>> mpcCommands = null)
{
commands = new List<IMpcCommand<object>>();
if (mpcCommands != null)
AddRange(mpcCommands);
}
/// <summary>
/// Add a command to the list.
/// </summary>
/// <param name="c"></param>
public void Add(IMpcCommand<object> c) => commands.Add(c);
/// <summary>
/// Add a range of commands to the list.
/// </summary>
/// <param name="r"></param>
public void AddRange(IEnumerable<IMpcCommand<object>> r) => commands.AddRange(r);
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize()
{
var serializedCommands = commands.Select(c => c.Serialize()).ToList();
serializedCommands.Insert(0, "command_list_begin");
serializedCommands.Add("command_list_end");
return string.Join("\n", serializedCommands.ToArray());
}
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -31,9 +31,9 @@ namespace MpcNET.Commands.Reflection
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<string> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IEnumerable<string> Deserialize(SerializedResponse response)
{
var result = response.Where(item => item.Key.Equals("command")).Select(item => item.Value);
var result = response.ResponseValues.Where(item => item.Key.Equals("command")).Select(item => item.Value);
return result;
}

View File

@ -30,12 +30,12 @@ namespace MpcNET.Commands.Reflection
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<MpdDecoderPlugin> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IEnumerable<MpdDecoderPlugin> Deserialize(SerializedResponse response)
{
var result = new List<MpdDecoderPlugin>();
var mpdDecoderPlugin = MpdDecoderPlugin.Empty;
foreach (var line in response)
foreach (var line in response.ResponseValues)
{
if (line.Key.Equals("plugin"))
{

View File

@ -0,0 +1,53 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PasswordCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Reflection
{
using System;
using System.Collections.Generic;
/// <summary>
/// Command to authenticate with a password.
/// https://mpd.readthedocs.io/en/stable/protocol.html#connection-settings.
/// </summary>
public class PasswordCommand : IMpcCommand<string>
{
private readonly string _password;
/// <summary>
/// Initializes a new instance of the <see cref="PasswordCommand"/> class.
/// </summary>
/// <param name="pass">The password.</param>
public PasswordCommand(string pass)
{
_password = pass;
if (_password == "")
{
throw new ArgumentException("Empty string given to PasswordCommand");
}
}
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialized command.
/// </returns>
public string Serialize() => string.Join(" ", new[] { "password", _password });
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -32,9 +32,9 @@ namespace MpcNET.Commands.Reflection
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<string> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IEnumerable<string> Deserialize(SerializedResponse response)
{
var result = response.Where(item => item.Key.Equals("tagtype")).Select(item => item.Value);
var result = response.ResponseValues.Where(item => item.Key.Equals("tagtype")).Select(item => item.Value);
return result;
}

View File

@ -30,9 +30,9 @@ namespace MpcNET.Commands.Reflection
/// <returns>
/// The deserialized response.
/// </returns>
public IEnumerable<string> Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IEnumerable<string> Deserialize(SerializedResponse response)
{
var result = response.Where(item => item.Key.Equals("handler")).Select(item => item.Value);
var result = response.ResponseValues.Where(item => item.Key.Equals("handler")).Select(item => item.Value);
return result;
}

View File

@ -30,9 +30,9 @@ namespace MpcNET.Commands.Status
/// <returns>
/// The deserialized response.
/// </returns>
public IMpdFile Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public IMpdFile Deserialize(SerializedResponse response)
{
return MpdFile.Create(response, 0).mpdFile;
return MpdFile.Create(response.ResponseValues, 0).mpdFile;
}
}
}

View File

@ -48,9 +48,9 @@ namespace MpcNET.Commands.Status
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -6,8 +6,6 @@
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Status
{
using System.Collections.Generic;
/// <summary>
/// Cancels idle command.
/// https://www.musicpd.org/doc/protocol/command_reference.html#status_commands.
@ -29,9 +27,9 @@ namespace MpcNET.Commands.Status
/// <returns>
/// The deserialized response.
/// </returns>
public string Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public string Deserialize(SerializedResponse response)
{
return string.Join(", ", response);
return string.Join(", ", response.ResponseValues);
}
}
}

View File

@ -0,0 +1,51 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="StatsCommand.cs" company="MpcNET">
// Copyright (c) MpcNET. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace MpcNET.Commands.Status
{
using System.Collections.Generic;
/// <summary>
/// Get stats from the daemon.
/// https://www.musicpd.org/doc/protocol/command_reference.html#status_commands.
/// </summary>
public class StatsCommand : IMpcCommand<Dictionary<string, string>>
{
/// <summary>
/// Serializes the command.
/// </summary>
/// <returns>
/// The serialize command.
/// </returns>
public string Serialize() => "stats";
/// <summary>
/// Deserializes the specified response text pairs.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>
/// The deserialized response.
/// </returns>
public Dictionary<string, string> Deserialize(SerializedResponse response)
{
var result = new Dictionary<string, string>();
foreach (var pair in response.ResponseValues)
{
// If a similar key has already been added to the result dictionary, add a ' to this second one so it can still be passed through.
// (It probably won't be used though...)
var key = pair.Key;
while (result.ContainsKey(key))
{
key = key + "'";
}
result.Add(key, pair.Value);
}
return result;
}
}
}

View File

@ -36,6 +36,7 @@ namespace MpcNET.Commands.Status
private const string DurationText = "duration";
private const string MixrampDbText = "mixrampdb";
private const string UpdatingDbText = "updating_db";
private const string PartitionText = "partition";
/// <summary>
/// Serializes the command.
@ -52,7 +53,7 @@ namespace MpcNET.Commands.Status
/// <returns>
/// The deserialized response.
/// </returns>
public MpdStatus Deserialize(IReadOnlyList<KeyValuePair<string, string>> response)
public MpdStatus Deserialize(SerializedResponse response)
{
int volume = -1;
bool repeat = false;
@ -75,8 +76,9 @@ namespace MpcNET.Commands.Status
TimeSpan duration;
double mixrampDb = -1;
int updatingDb = -1;
string partition = string.Empty;
string error = string.Empty;
foreach (var keyValuePair in response)
foreach (var keyValuePair in response.ResponseValues)
{
var value = keyValuePair.Value;
switch (keyValuePair.Key)
@ -143,6 +145,9 @@ namespace MpcNET.Commands.Status
case UpdatingDbText:
int.TryParse(value, out updatingDb);
break;
case PartitionText:
partition = value;
break;
default:
Debug.WriteLine($"Unprocessed status: {keyValuePair.Key} - {keyValuePair.Value}");
break;
@ -170,6 +175,7 @@ namespace MpcNET.Commands.Status
audioBits,
audioChannels,
updatingDb,
partition,
error);
}

View File

@ -4,6 +4,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Text;
namespace MpcNET
{
internal class Constants
@ -12,6 +14,13 @@ namespace MpcNET
public static readonly string Ack = "ACK";
public static readonly string Binary = "binary: ";
public static readonly string FirstLinePrefix = "OK MPD ";
/// <summary>
/// Encoding used when reading server responses.
/// </summary>
public static readonly Encoding Encoding = new UTF8Encoding();
}
}

View File

@ -25,6 +25,6 @@ namespace MpcNET
/// </summary>
/// <param name="response">The response.</param>
/// <returns>The deserialized response.</returns>
TValue Deserialize(IReadOnlyList<KeyValuePair<string, string>> response);
TValue Deserialize(SerializedResponse response);
}
}

View File

@ -8,6 +8,7 @@
namespace MpcNET
{
using System;
using System.Threading;
using System.Threading.Tasks;
using MpcNET.Message;
@ -26,7 +27,7 @@ namespace MpcNET
/// Connects asynchronously.
/// </summary>
/// <returns>The connect task.</returns>
Task ConnectAsync();
Task ConnectAsync(CancellationToken token);
/// <summary>
/// Disconnects asynchronously.

View File

@ -19,5 +19,7 @@ namespace MpcNET.Message
public IMpdResponse<T> Response { get; }
public bool IsResponseValid => false;
public override string ToString() => Response.Result.ErrorMessage;
}
}

View File

@ -14,14 +14,16 @@ namespace MpcNET.Message
internal class MpdMessage<T> : IMpdMessage<T>
{
private readonly Regex linePattern = new Regex(@"^(?<key>[\w-]*):[ ]{0,1}(?<value>.*)$");
private readonly IList<string> rawResponse;
private readonly IList<string> fullResponse;
private readonly byte[] binaryData;
public MpdMessage(IMpcCommand<T> command, bool connected, IReadOnlyCollection<string> response)
public MpdMessage(IMpcCommand<T> command, bool connected, IReadOnlyCollection<string> response, byte[] binaryData)
{
this.Request = new MpdRequest<T>(command);
this.binaryData = binaryData;
var endLine = response.Skip(response.Count - 1).Single();
this.rawResponse = response.Take(response.Count - 1).ToList();
this.fullResponse = response.Take(response.Count - 1).ToList();
var values = this.Request.Command.Deserialize(this.GetValuesFromResponse());
this.Response = new MpdResponse<T>(endLine, values, connected);
@ -38,11 +40,11 @@ namespace MpcNET.Message
return JsonConvert.SerializeObject(this, Formatting.Indented);
}
private IReadOnlyList<KeyValuePair<string, string>> GetValuesFromResponse()
private SerializedResponse GetValuesFromResponse()
{
var result = new List<KeyValuePair<string, string>>();
foreach (var line in this.rawResponse)
foreach (var line in this.fullResponse)
{
var match = this.linePattern.Match(line);
if (match.Success)
@ -55,11 +57,12 @@ namespace MpcNET.Message
{
result.Add(new KeyValuePair<string, string>(mpdKey, mpdValue));
}
}
}
}
return result;
return new SerializedResponse { ResponseValues = result, BinaryData = binaryData };
}
}
}

View File

@ -23,6 +23,7 @@ namespace MpcNET.Message
if (this.Exception != null)
{
this.Status = "EXCEPTION";
this.ErrorMessage = Exception.ToString();
}
if (!string.IsNullOrEmpty(this.endLine))
@ -60,7 +61,7 @@ namespace MpcNET.Message
if (match.Groups.Count != 5)
{
this.ErrorMessage = "Unexpected response from server.";
this.ErrorMessage = $"Unexpected response from server: {MpdError}";
}
else
{

View File

@ -13,11 +13,11 @@ namespace MpcNET
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MpcNET.Commands;
using MpcNET.Exceptions;
using MpcNET.Message;
using Sundew.Base.ControlFlow;
using Polly;
/// <summary>
/// Keeps the connection to the MPD server and handels the most basic structure of the MPD protocol.
@ -25,8 +25,7 @@ namespace MpcNET
/// </summary>
public class MpcConnection : IMpcConnection
{
private static readonly Encoding Encoding = new UTF8Encoding();
private readonly IMpcConnectionReporter mpcConnectionReporter;
private readonly Encoding Encoding = new UTF8Encoding();
private readonly IPEndPoint server;
private TcpClient tcpClient;
@ -36,11 +35,9 @@ namespace MpcNET
/// Initializes a new instance of the <see cref="MpcConnection" /> class.
/// </summary>
/// <param name="server">The server.</param>
/// <param name="mpcConnectionReporter">The MPC connection logger.</param>
public MpcConnection(IPEndPoint server, IMpcConnectionReporter mpcConnectionReporter = null)
public MpcConnection(IPEndPoint server)
{
this.mpcConnectionReporter = mpcConnectionReporter;
this.ClearConnectionFields();
ClearConnectionFields();
this.server = server ?? throw new ArgumentNullException("Server IPEndPoint not set.", nameof(server));
}
@ -49,22 +46,32 @@ namespace MpcNET
/// </summary>
public string Version { get; private set; }
/// <summary>
/// Is this connection active?
/// </summary>
public bool IsConnected => tcpClient?.Connected ?? false;
/// <summary>
/// Event emitted when the connection is cut.
/// </summary>
public event EventHandler<EventArgs> Disconnected;
/// <summary>
/// Connects asynchronously.
/// </summary>
/// <returns>The connect task.</returns>
public async Task ConnectAsync()
public async Task ConnectAsync(CancellationToken token = default)
{
if (this.tcpClient != null)
if (tcpClient != null)
{
var pingResult = await this.PingAsync();
var pingResult = await PingAsync();
if (pingResult)
{
return;
}
}
await this.ReconnectAsync(false);
await ReconnectAsync(false, token);
}
/// <summary>
@ -73,7 +80,8 @@ namespace MpcNET
/// <returns>The disconnect task.</returns>
public Task DisconnectAsync()
{
return this.DisconnectAsync(true);
Disconnect(true);
return Task.CompletedTask;
}
/// <summary>
@ -86,9 +94,9 @@ namespace MpcNET
/// </returns>
public async Task<IMpdMessage<TResponse>> SendAsync<TResponse>(IMpcCommand<TResponse> mpcCommand)
{
if (this.tcpClient == null)
if (tcpClient == null)
{
await this.ReconnectAsync(true);
await ReconnectAsync(true).ConfigureAwait(false);
}
if (mpcCommand == null)
@ -96,74 +104,74 @@ namespace MpcNET
throw new CommandNullException();
}
Exception lastException = null;
IReadOnlyList<string> response = new List<string>();
var sendAttempter = new Attempter(3);
byte[] rawResponse = null;
var commandText = mpcCommand.Serialize();
this.mpcConnectionReporter?.Sending(commandText);
while (sendAttempter.Attempt())
Exception finalException = null;
// Send the command, retrying three times in case of an exception
for (var i=0; i < 3; i++)
{
try
{
using (var writer = new StreamWriter(this.networkStream, Encoding, 512, true) { NewLine = "\n" })
using (var writer = new StreamWriter(networkStream, Encoding, 512, true) { NewLine = "\n" })
{
await writer.WriteLineAsync(commandText);
await writer.FlushAsync();
}
response = await this.ReadResponseAsync(commandText);
(rawResponse, response) = ReadResponse(commandText);
if (response.Any())
{
lastException = null;
finalException = null;
break;
}
throw new EmptyResponseException(commandText);
}
catch (Exception exception)
} catch (Exception e)
{
lastException = exception;
this.mpcConnectionReporter?.SendException(commandText, sendAttempter.CurrentAttempt, exception);
await this.ReconnectAsync(true);
this.mpcConnectionReporter?.RetrySend(commandText, sendAttempter.CurrentAttempt);
finalException = e;
await ReconnectAsync(true).ConfigureAwait(false);
}
}
if (lastException != null)
if (finalException != null)
{
try
{
await this.DisconnectAsync(false);
Disconnect(false);
}
catch (Exception)
catch
{
}
return new ErrorMpdMessage<TResponse>(mpcCommand, new ErrorMpdResponse<TResponse>(lastException));
return new ErrorMpdMessage<TResponse>(mpcCommand, new ErrorMpdResponse<TResponse>(finalException));
}
return new MpdMessage<TResponse>(mpcCommand, true, response);
return new MpdMessage<TResponse>(mpcCommand, true, response, rawResponse);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
void IDisposable.Dispose()
public void Dispose()
{
this.DisconnectAsync().Wait();
Disconnect(true);
}
private async Task<bool> PingAsync()
{
try
{
using (var writer = new StreamWriter(this.networkStream, Encoding, 512, true) { NewLine = "\n" })
using (var writer = new StreamWriter(networkStream, Encoding, 512, true) { NewLine = "\n" })
{
await writer.WriteLineAsync("ping");
await writer.FlushAsync();
}
using (var reader = new StreamReader(this.networkStream, Encoding, true, 512, true))
using (var reader = new StreamReader(networkStream, Encoding, true, 512, true))
{
var responseLine = await reader.ReadLineAsync();
return responseLine == "OK";
@ -175,81 +183,133 @@ namespace MpcNET
}
}
private async Task ReconnectAsync(bool isReconnect)
private async Task ReconnectAsync(bool isReconnect, CancellationToken token = default)
{
var connectAttempter = new Attempter(3);
while (connectAttempter.Attempt())
{
this.mpcConnectionReporter?.Connecting(isReconnect, connectAttempter.CurrentAttempt);
await this.DisconnectAsync(false);
this.tcpClient = new TcpClient();
await this.tcpClient.ConnectAsync(this.server.Address, this.server.Port);
if (this.tcpClient.Connected)
var connectResult = await Policy
.Handle<Exception>()
.RetryAsync(isReconnect ? 0 : 3)
.ExecuteAndCaptureAsync(async (t) =>
{
this.mpcConnectionReporter?.ConnectionAccepted(isReconnect, connectAttempter.CurrentAttempt);
break;
var client = new TcpClient();
using (t.Register(() => client.Close()))
{
try
{
await client.ConnectAsync(server.Address, server.Port).ConfigureAwait(false);
}
catch (ObjectDisposedException) when (t.IsCancellationRequested)
{
t.ThrowIfCancellationRequested();
}
}
if (client.Connected)
{
return client;
}
return null;
}, token, true).ConfigureAwait(false);
if (connectResult.Outcome == OutcomeType.Successful && connectResult.Result != null)
{
Disconnect(false);
tcpClient = connectResult.Result;
networkStream = tcpClient.GetStream();
using (var reader = new StreamReader(networkStream, Encoding, true, 512, true))
{
var firstLine = await reader.ReadLineAsync();
if (firstLine != null && !firstLine.StartsWith(Constants.FirstLinePrefix))
{
await DisconnectAsync();
throw new MpcConnectException("Response of mpd does not start with \"" + Constants.FirstLinePrefix + "\".");
}
Version = firstLine?.Substring(Constants.FirstLinePrefix.Length);
}
}
this.networkStream = this.tcpClient.GetStream();
using (var reader = new StreamReader(this.networkStream, Encoding, true, 512, true))
else
{
var firstLine = await reader.ReadLineAsync();
if (!firstLine.StartsWith(Constants.FirstLinePrefix))
{
await this.DisconnectAsync(false);
throw new MpcConnectException("Response of mpd does not start with \"" + Constants.FirstLinePrefix + "\".");
}
this.Version = firstLine.Substring(Constants.FirstLinePrefix.Length);
this.mpcConnectionReporter?.Connected(isReconnect, connectAttempter.CurrentAttempt, firstLine);
// We couldn't reconnect
Disconnected?.Invoke(this, new EventArgs());
throw new MpcConnectException(connectResult.FinalException?.Message);
}
}
private async Task<IReadOnlyList<string>> ReadResponseAsync(string commandText)
private Tuple<byte[], IReadOnlyList<string>> ReadResponse(string commandText)
{
var response = new List<string>();
using (var reader = new StreamReader(this.networkStream, Encoding, true, 512, true))
byte[] binaryResponse = null;
var reader = new MpdResponseReader(networkStream, Encoding);
MpdResponseReader.NextData nextData;
string responseLine;
while ((nextData = reader.ReportNextData()) != MpdResponseReader.NextData.Eof)
{
string responseLine;
do
// If the incoming data is binary, read it raw
if (nextData == MpdResponseReader.NextData.BinaryData)
{
responseLine = await reader.ReadLineAsync();
this.mpcConnectionReporter?.ReadResponse(responseLine, commandText);
// The reader already knows the length of the binary data, so we just tell it to read.
// MPD binary responses usually don't go past 8192 bytes.
byte[] buf = new byte[8192];
using (MemoryStream ms = new MemoryStream())
{
do
{
var bytesRead = reader.ReadBinaryData(buf, 0, buf.Length);
ms.Write(buf, 0, bytesRead);
} while (reader.ReportNextData() == MpdResponseReader.NextData.BinaryData);
binaryResponse = ms.ToArray();
}
}
else // else, read string as usual
{
responseLine = reader.ReadString();
if (responseLine == null)
{
break;
}
response.Add(responseLine);
if (responseLine.Equals(Constants.Ok) || responseLine.StartsWith(Constants.Ack))
{
// Stop reading the stream
break;
}
}
while (!(responseLine.Equals(Constants.Ok) || responseLine.StartsWith(Constants.Ack)));
}
return response;
return new Tuple<byte[], IReadOnlyList<string>>(binaryResponse, response);
}
private Task DisconnectAsync(bool isExplicitDisconnect)
private void Disconnect(bool isExplicitDisconnect)
{
if (this.tcpClient == null)
if (tcpClient == null)
{
return Task.CompletedTask;
return;
}
this.mpcConnectionReporter?.Disconnecting(isExplicitDisconnect);
this.ClearConnectionFields();
this.mpcConnectionReporter?.Disconnected(isExplicitDisconnect);
return Task.CompletedTask;
ClearConnectionFields();
if (isExplicitDisconnect)
Disconnected?.Invoke(this, new EventArgs());
}
private void ClearConnectionFields()
{
this.networkStream?.Dispose();
this.tcpClient?.Dispose();
this.Version = string.Empty;
this.tcpClient = null;
this.networkStream = null;
networkStream?.Dispose();
tcpClient?.Dispose();
Version = string.Empty;
tcpClient = null;
networkStream = null;
}
}
}

View File

@ -3,12 +3,12 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>MpcNET</AssemblyName>
<PackageId>MpcNET.SundewFork</PackageId>
<PackageId>MpcNET</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace>MpcNET</RootNamespace>
<Version>0.0.2</Version>
<Version>1.0.0</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AssemblyVersion>0.0.1.0</AssemblyVersion>
<FileVersion>0.0.1.0</FileVersion>
@ -44,12 +44,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Sundew.Base" Version="4.0.0" />
<PackageReference Include="Sundew.Build.Publish" Version="1.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Polly" Version="7.2.2" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>

View File

@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace MpcNET
{
/// <summary>
///
/// </summary>
public class MpdResponseReader : IDisposable
{
/// <summary>
///
/// </summary>
public enum NextData
{
#pragma warning disable CS1591
Unknown,
Eof,
String,
BinaryData
#pragma warning restore CS1591
}
/// <summary>
/// Length of the binary part of the response.
/// </summary>
public int binaryEnd;
private Stream source;
private Encoding encoding;
private byte[] byteBuffer;
private int bufferOffset;
private int bufferEnd;
private NextData nextData;
private int binaryOffset;
private char[] characterBuffer;
/// <summary>
///
/// </summary>
/// <param name="source"></param>
/// <param name="encoding"></param>
public MpdResponseReader(Stream source, Encoding encoding)
{
this.source = source;
this.encoding = encoding;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public NextData ReportNextData()
{
if (nextData != NextData.Unknown)
{
return nextData;
}
if (!PopulateBufferIfNeeded(1))
{
return (nextData = NextData.Eof);
}
return (nextData = NextData.String);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public string ReadString()
{
ReportNextData();
if (nextData == NextData.Eof)
{
throw new EndOfStreamException();
}
else if (nextData != NextData.String)
{
throw new InvalidOperationException("Attempt to read non-string data as string");
}
if (characterBuffer == null)
{
characterBuffer = new char[4];
}
StringBuilder stringBuilder = new StringBuilder();
Decoder decoder = encoding.GetDecoder();
while (nextData == NextData.String)
{
byte b = byteBuffer[bufferOffset];
if (b == '\n')
{
// Analyze the obtained string to see if it's a binary data header
if (stringBuilder.ToString().StartsWith(Constants.Binary))
{
nextData = NextData.BinaryData;
binaryEnd = int.Parse(stringBuilder.ToString().Replace(Constants.Binary, ""));
binaryOffset = 0;
} else
{
nextData = NextData.Unknown;
}
bufferOffset++;
break;
}
else
{
var decodedChars = decoder.GetChars(byteBuffer, bufferOffset++, 1, characterBuffer, 0);
if (decodedChars == 1)
{
stringBuilder.Append(characterBuffer[0]);
}
else
{
stringBuilder.Append(characterBuffer, 0, decodedChars);
}
if (bufferOffset == bufferEnd && !PopulateBufferIfNeeded(1))
{
nextData = NextData.Eof;
break;
}
}
}
return stringBuilder.ToString();
}
/// <summary>
///
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="count"></param>
/// <returns></returns>
public int ReadBinaryData(byte[] buffer, int offset, int count)
{
ReportNextData();
if (nextData == NextData.Eof)
{
throw new EndOfStreamException();
}
else if (nextData != NextData.BinaryData)
{
throw new InvalidOperationException("Attempt to read non-binary data as binary data");
}
if (count > binaryEnd - binaryOffset)
{
count = binaryEnd - binaryOffset;
//throw new EndOfStreamException();
}
int bytesRead;
if (bufferOffset < bufferEnd)
{
bytesRead = Math.Min(count, bufferEnd - bufferOffset);
Array.Copy(byteBuffer, bufferOffset, buffer, offset, bytesRead);
bufferOffset += bytesRead;
}
else if (count < byteBuffer.Length)
{
if (!PopulateBufferIfNeeded(1))
{
throw new EndOfStreamException();
}
bytesRead = Math.Min(count, bufferEnd - bufferOffset);
Array.Copy(byteBuffer, bufferOffset, buffer, offset, bytesRead);
bufferOffset += bytesRead;
}
else
{
bytesRead = source.Read(buffer, offset, count);
}
binaryOffset += bytesRead;
if (binaryOffset == binaryEnd)
{
nextData = NextData.Unknown;
}
return bytesRead;
}
private bool PopulateBufferIfNeeded(int minimumBytes)
{
if (byteBuffer == null)
{
byteBuffer = new byte[32768];
}
if (bufferEnd - bufferOffset < minimumBytes)
{
int shiftCount = bufferEnd - bufferOffset;
if (shiftCount > 0)
{
Array.Copy(byteBuffer, bufferOffset, byteBuffer, 0, shiftCount);
}
bufferOffset = 0;
bufferEnd = shiftCount;
while (bufferEnd - bufferOffset < minimumBytes)
{
int bytesRead = source.Read(byteBuffer, bufferEnd, byteBuffer.Length - bufferEnd);
if (bytesRead == 0)
{
return false;
}
bufferEnd += bytesRead;
}
}
return true;
}
/// <summary>
///
/// </summary>
public void Dispose()
{
Stream source = this.source;
this.source = null;
if (source != null)
{
source.Dispose();
}
}
}
}

View File

@ -37,6 +37,7 @@ namespace MpcNET
/// <param name="audioBits">The audio bits.</param>
/// <param name="audioChannels">The audio channels.</param>
/// <param name="updatingDb">The updating database.</param>
/// <param name="partition">The partition name.</param>
/// <param name="error">The error.</param>
public MpdStatus(
int volume,
@ -59,29 +60,31 @@ namespace MpcNET
int audioBits,
int audioChannels,
int updatingDb,
string partition,
string error)
{
this.Volume = volume;
this.Repeat = repeat;
this.Random = random;
this.Consume = consume;
this.Single = single;
this.Playlist = playlist;
this.PlaylistLength = playlistLength;
this.XFade = xFade;
this.State = state;
this.Song = song;
this.SongId = songId;
this.NextSong = nextSong;
this.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;
Volume = volume;
Repeat = repeat;
Random = random;
Consume = consume;
Single = single;
Playlist = playlist;
PlaylistLength = playlistLength;
XFade = xFade;
State = state;
Song = song;
SongId = songId;
NextSong = nextSong;
NextSongId = nextSongId;
Elapsed = elapsed;
Duration = duration;
Bitrate = bitrate;
AudioSampleRate = audioSampleRate;
AudioBits = audioBits;
AudioChannels = audioChannels;
UpdatingDb = updatingDb;
Partition = partition;
Error = error;
}
/// <summary>
@ -184,6 +187,12 @@ namespace MpcNET
/// </summary>
public int UpdatingDb { get; }
/// <summary>
/// Gets the name of the current partition
/// </summary>
public string Partition { get; }
/// <summary>
/// Gets the error message, if there is an error.
/// </summary>
@ -197,13 +206,14 @@ namespace MpcNET
{
var 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)
builder.AppendLine($"partition: {Partition}");
AppendInt(builder, "volume", Volume);
AppendBool(builder, "repeat", Repeat);
AppendBool(builder, "random", Random);
AppendInt(builder, "playlist", Playlist);
AppendInt(builder, "playlistlength", PlaylistLength);
AppendInt(builder, "xfade", XFade);
switch (State)
{
case MpdState.Play:
builder.AppendLine("state: play");
@ -216,34 +226,34 @@ namespace MpcNET
break;
}
AppendInt(builder, "song", this.Song);
AppendInt(builder, "songid", this.SongId);
if (this.Elapsed > TimeSpan.Zero || this.Duration > TimeSpan.Zero)
AppendInt(builder, "song", Song);
AppendInt(builder, "songid", SongId);
if (Elapsed > TimeSpan.Zero || Duration > TimeSpan.Zero)
{
builder.Append("time: ");
builder.Append(this.Elapsed);
builder.Append(Elapsed);
builder.Append(":");
builder.Append(this.Duration);
builder.Append(Duration);
builder.AppendLine();
}
AppendInt(builder, "bitrate", this.Bitrate);
if ((this.AudioSampleRate >= 0) || (this.AudioBits >= 0) || (this.AudioChannels >= 0))
AppendInt(builder, "bitrate", Bitrate);
if ((AudioSampleRate >= 0) || (AudioBits >= 0) || (AudioChannels >= 0))
{
builder.Append("audio: ");
builder.Append(this.AudioSampleRate);
builder.Append(AudioSampleRate);
builder.Append(":");
builder.Append(this.AudioBits);
builder.Append(AudioBits);
builder.Append(":");
builder.Append(this.AudioChannels);
builder.Append(AudioChannels);
builder.AppendLine();
}
AppendInt(builder, "updating_db", this.UpdatingDb);
if (this.Error != null)
AppendInt(builder, "updating_db", UpdatingDb);
if (Error != null)
{
builder.Append("error: ");
builder.AppendLine(this.Error);
builder.AppendLine(Error);
}
return builder.ToString();

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MpcNET
{
/// <summary>
///
/// </summary>
public class SerializedResponse
{
/// <summary>
/// Response values.
/// </summary>
public IReadOnlyList<KeyValuePair<string, string>> ResponseValues {get; set;}
/// <summary>
/// Binary Data that can be present in the response.
/// </summary>
public byte[] BinaryData { get; set; }
}
}

View File

@ -28,19 +28,27 @@ namespace MpcNET.Tags
public static ITag File { get; } = new Tag("file");
/// <summary>
/// Gets the base tag.
/// Gets the title tag.
/// </summary>
/// <value>
/// The base.
/// </value>
public static ITag Base { get; } = new Tag("base");
public static ITag Title { get; } = new Tag("title");
/// <summary>
/// Gets the modified since tag.
/// Gets the artist tag.
/// </summary>
/// <value>
/// The modified since.
/// </value>
public static ITag ModifiedSince { get; } = new Tag("modified-since");
public static ITag Artist { get; } = new Tag("artist");
/// <summary>
/// Gets the album tag.
/// </summary>
/// <value>
/// The base.
/// </value>
public static ITag Album { get; } = new Tag("album");
}
}

View File

@ -54,14 +54,6 @@ namespace MpcNET.Types
/// </value>
string Track { get; }
/// <summary>
/// Gets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
string Name { get; }
/// <summary>
/// Gets the genre.
/// </summary>
@ -160,7 +152,7 @@ namespace MpcNET.Types
bool HasTrack { get; }
/// <summary>
/// Gets a value indicating whether the MpdFile has the <see cref="Name"/> property set.
/// Gets a value indicating whether the MpdFile has the <see cref="IMpdFilePath.Name"/> property set.
/// </summary>
bool HasName { get; }

View File

@ -18,5 +18,13 @@ namespace MpcNET.Types
/// The path.
/// </value>
string Path { get; }
/// <summary>
/// Gets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
string Name { get; }
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MpcNET.Types
{
/// <summary>
///
/// </summary>
public class MpdBinaryData
{
/// <summary>
/// Initializes a new instance of the <see cref="MpdBinaryData"/> class.
/// </summary>
/// <param name="size">The total size of the binary data requested</param>
/// <param name="binary">The size of the data contained in this response</param>
/// <param name="data">The data itself.</param>
public MpdBinaryData(long size, long binary, byte[] data)
{
this.Size = size;
this.Binary = binary;
this.Data = data;
}
/// <summary>
/// Gets the total size of the binary data requeste
/// </summary>
public long Size { get; }
/// <summary>
/// Gets the size of the data contained in this response
/// </summary>
public long Binary { get; }
/// <summary>
/// Gets the data contained in this response
/// </summary>
public byte[] Data { get; }
}
}

View File

@ -12,7 +12,7 @@ namespace MpcNET.Types
/// <summary>
/// Represents a MPD directory.
/// </summary>
public class MpdDirectory
public class MpdDirectory: IMpdFilePath
{
private readonly List<IMpdFilePath> files = new List<IMpdFilePath>();

View File

@ -7,6 +7,7 @@
namespace MpcNET.Types
{
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// The MpdFile class contains all meta data for a file of the MPD.
@ -182,6 +183,11 @@ namespace MpcNET.Types
/// </summary>
public bool HasId => this.Id != NoId;
public override string ToString()
{
return Title ?? Name ?? Path.Split('/').Last();
}
internal static MpdFile Create(string path, int pos)
{
return new MpdFile(path, pos: pos);
@ -305,7 +311,15 @@ namespace MpcNET.Types
break;
default:
unknownMetadata.Add(line.Key, line.Value);
// If a similar key has already been added to unknown metadata, add a ' to this second one so it can still be passed through.
// (It certainly won't be used though)
var key = line.Key;
while (unknownMetadata.ContainsKey(key))
{
key = key + "'";
}
unknownMetadata.Add(key, line.Value);
break;
}
}

View File

@ -16,11 +16,13 @@ namespace MpcNET.Types
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="name">The name.</param>
/// <param name="plugin">The plugin name.</param>
/// <param name="enabled">if set to <c>true</c> [enabled].</param>
public MpdOutput(int id, string name, bool enabled)
public MpdOutput(int id, string name, string plugin, bool enabled)
{
this.Id = id;
this.Name = name;
this.Plugin = plugin;
this.IsEnabled = enabled;
}
@ -40,6 +42,14 @@ namespace MpcNET.Types
/// </value>
public string Name { get; }
/// <summary>
/// Gets the plugin name.
/// </summary>
/// <value>
/// The plugin name.
/// </value>
public string Plugin { get; }
/// <summary>
/// Gets a value indicating whether this instance is enabled.
/// </summary>

View File

@ -7,6 +7,7 @@
namespace MpcNET.Types
{
using System;
using System.Collections.Generic;
using System.Globalization;
/// <summary>
@ -39,6 +40,29 @@ namespace MpcNET.Types
/// </value>
public DateTime LastModified { get; private set; }
/// <summary>
/// Equals implementation.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
return obj is MpdPlaylist playlist &&
Name == playlist.Name &&
LastModified == playlist.LastModified;
}
/// <summary>
/// HashCode implementation.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
int hashCode = 1509980188;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
hashCode = hashCode * -1521134295 + LastModified.GetHashCode();
return hashCode;
}
internal void AddLastModified(string lastModified)
{
this.LastModified = DateTime.Parse(lastModified, CultureInfo.InvariantCulture);

BIN
Sources/MpcNET/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -3,4 +3,4 @@
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
</configuration>