mirror of
https://github.com/ZetaKebab/MpcNET.git
synced 2025-07-01 08:47:36 +00:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
d907f9b6e3 | |||
316cfb0a31 | |||
b732457a34 | |||
6bcc61b57b | |||
28e5dbd334 | |||
585baf96ac | |||
17e41c07cb | |||
374249840b | |||
0194291779 | |||
cc0aa24324 | |||
133571023e | |||
7a0a28dbe9 | |||
1f61079811 |
4
.github/workflows/build-and-test.yml
vendored
4
.github/workflows/build-and-test.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
||||
working-directory: ./Sources
|
||||
run: |
|
||||
msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration
|
||||
|
||||
|
||||
# Build package and upload to github packages
|
||||
- name: Build package
|
||||
working-directory: ./Sources
|
||||
@ -39,7 +39,7 @@ jobs:
|
||||
dotnet nuget add source --username Difegue --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/Difegue/index.json"
|
||||
dotnet build $env:Solution_Name --configuration $env:Configuration
|
||||
dotnet pack --configuration $env:Configuration -o ./
|
||||
dotnet nuget push *.nupkg --api-key ${{ secrets.GITHUB_TOKEN }} --source "github"
|
||||
dotnet nuget push *.nupkg --api-key ${{ secrets.GITHUB_TOKEN }} --source "github" --skip-duplicate
|
||||
|
||||
# Execute all unit tests in the solution
|
||||
#- name: Execute unit tests
|
||||
|
@ -10,11 +10,12 @@ namespace MpcNET.Commands.Database
|
||||
using System.Linq;
|
||||
using MpcNET.Tags;
|
||||
using MpcNET.Types;
|
||||
using MpcNET.Types.Filters;
|
||||
|
||||
/// <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
|
||||
/// https://mpd.readthedocs.io/en/stable/protocol.html#filters
|
||||
/// </summary>
|
||||
public class SearchCommand : BaseFilterCommand
|
||||
{
|
||||
@ -22,16 +23,12 @@ namespace MpcNET.Commands.Database
|
||||
///
|
||||
/// </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="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) { }
|
||||
@ -44,12 +41,28 @@ namespace MpcNET.Commands.Database
|
||||
/// <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>
|
||||
/// Initializes a new instance of the <see cref="SearchCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filter">Filter</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(IFilter filter, int windowStart = -1, int windowEnd = -1) : base(filter, windowStart, windowEnd) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SearchCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filters">List of 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<IFilter> 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
|
||||
/// https://mpd.readthedocs.io/en/stable/protocol.html#filters
|
||||
/// </summary>
|
||||
public class SearchAddCommand : BaseFilterCommand
|
||||
{
|
||||
@ -57,13 +70,9 @@ namespace MpcNET.Commands.Database
|
||||
///
|
||||
/// </summary>
|
||||
public override string CommandName => "searchadd";
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override string Operand => "contains";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SearchCommand"/> class.
|
||||
/// Initializes a new instance of the <see cref="SearchAddCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag.</param>
|
||||
/// <param name="searchText">The search text.</param>
|
||||
@ -72,19 +81,34 @@ namespace MpcNET.Commands.Database
|
||||
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.
|
||||
/// Initializes a new instance of the <see cref="SearchAddCommand"/> 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>
|
||||
/// Initializes a new instance of the <see cref="SearchAddCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filter">Filter</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(IFilter filter, int windowStart = -1, int windowEnd = -1) : base(filter, windowStart, windowEnd) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SearchAddCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filters">List of 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<IFilter> 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
|
||||
/// https://mpd.readthedocs.io/en/stable/protocol.html#filters
|
||||
/// </summary>
|
||||
public class FindCommand : BaseFilterCommand
|
||||
{
|
||||
@ -92,10 +116,6 @@ namespace MpcNET.Commands.Database
|
||||
///
|
||||
/// </summary>
|
||||
public override string CommandName => "find";
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override string Operand => "==";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FindCommand"/> class.
|
||||
@ -114,15 +134,30 @@ namespace MpcNET.Commands.Database
|
||||
/// <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>
|
||||
/// Initializes a new instance of the <see cref="FindCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filter">Filter</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(IFilter filter, int windowStart = -1, int windowEnd = -1) : base(filter, windowStart, windowEnd) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FindCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filters">List of 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<IFilter> 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 List<IFilter> _filters;
|
||||
private readonly int _start;
|
||||
private readonly int _end;
|
||||
|
||||
@ -130,11 +165,6 @@ namespace MpcNET.Commands.Database
|
||||
/// 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.
|
||||
@ -143,10 +173,12 @@ namespace MpcNET.Commands.Database
|
||||
/// <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)
|
||||
/// <param name="operand">Operator of the filter</param>
|
||||
public BaseFilterCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1, FilterOperator operand = FilterOperator.Equal)
|
||||
{
|
||||
this.filters = new List<KeyValuePair<ITag, string>>();
|
||||
this.filters.Add(new KeyValuePair<ITag, string>(tag, searchText));
|
||||
_filters = new List<IFilter>();
|
||||
FilterTag Tag = new FilterTag(tag, searchText, operand);
|
||||
_filters.Add(Tag);
|
||||
|
||||
_start = windowStart;
|
||||
_end = windowEnd;
|
||||
@ -158,9 +190,40 @@ namespace MpcNET.Commands.Database
|
||||
/// <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)
|
||||
/// <param name="operand">Operator of the filter</param>
|
||||
public BaseFilterCommand(List<KeyValuePair<ITag, string>> filters, int windowStart = -1, int windowEnd = -1, FilterOperator operand = FilterOperator.Equal)
|
||||
{
|
||||
this.filters = filters;
|
||||
_filters = new List<IFilter>();
|
||||
_filters.AddRange(filters.Select(filter => new FilterTag(filter.Key, filter.Value, operand)).ToList());
|
||||
|
||||
_start = windowStart;
|
||||
_end = windowEnd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseFilterCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filters">Filter</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(IFilter filters, int windowStart = -1, int windowEnd = -1)
|
||||
{
|
||||
_filters = new List<IFilter>();
|
||||
_filters.Add(filters);
|
||||
|
||||
_start = windowStart;
|
||||
_end = windowEnd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseFilterCommand"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filters">List of 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<IFilter> filters, int windowStart = -1, int windowEnd = -1)
|
||||
{
|
||||
_filters = filters;
|
||||
|
||||
_start = windowStart;
|
||||
_end = windowEnd;
|
||||
@ -170,16 +233,19 @@ namespace MpcNET.Commands.Database
|
||||
/// Serializes the command.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The serialize command.
|
||||
/// The serialized command.
|
||||
/// </returns>
|
||||
public string Serialize()
|
||||
{
|
||||
var cmd =
|
||||
CommandName + " \"(" +
|
||||
string.Join(" AND ",
|
||||
filters.Select(x => $"({x.Key.Value} {Operand} {escape(x.Value)})")
|
||||
) +
|
||||
")\"";
|
||||
string cmd = "";
|
||||
|
||||
if (_filters != null)
|
||||
{
|
||||
var serializedFilters = string.Join(" AND ",
|
||||
_filters.Select(x => $"{x.GetFormattedCommand()}")
|
||||
);
|
||||
cmd = $@"{CommandName} ""({serializedFilters})""";
|
||||
}
|
||||
|
||||
if (_start > -1)
|
||||
{
|
||||
@ -200,8 +266,6 @@ namespace MpcNET.Commands.Database
|
||||
{
|
||||
return MpdFile.CreateList(response.ResponseValues);
|
||||
}
|
||||
|
||||
private string escape(string value) => string.Format("\\\"{0}\\\"", value.Replace("\\", "\\\\\\").Replace("'", "\\\\'").Replace("\"", "\\\\\\\""));
|
||||
}
|
||||
// TODO: rescan
|
||||
}
|
||||
|
64
Sources/MpcNET/Commands/Playback/ConsumeCommand.cs
Normal file
64
Sources/MpcNET/Commands/Playback/ConsumeCommand.cs
Normal file
@ -0,0 +1,64 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="ConsumeCommand.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 consume state.
|
||||
/// Sets consume state to STATE, STATE should be 0 or 1. When consume is activated, each song played is removed from playlist.
|
||||
/// https://mpd.readthedocs.io/en/stable/protocol.html#playback-options
|
||||
/// </summary>
|
||||
public class ConsumeCommand : IMpcCommand<string>
|
||||
{
|
||||
private readonly string single;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConsumeCommand" /> class.
|
||||
/// </summary>
|
||||
/// <param name="single">if set to <c>true</c> [consume].</param>
|
||||
public ConsumeCommand(bool single)
|
||||
{
|
||||
this.single = single ? "1" : "0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConsumeCommand"/> class.
|
||||
/// </summary>
|
||||
public ConsumeCommand()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the command.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The serialize command.
|
||||
/// </returns>
|
||||
public string Serialize()
|
||||
{
|
||||
if (this.single == null)
|
||||
{
|
||||
return string.Join(" ", "consume");
|
||||
}
|
||||
|
||||
return string.Join(" ", "consume", this.single);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
@ -190,7 +190,8 @@ namespace MpcNET
|
||||
.RetryAsync(isReconnect ? 0 : 3)
|
||||
.ExecuteAndCaptureAsync(async (t) =>
|
||||
{
|
||||
var client = new TcpClient();
|
||||
var client = new TcpClient(AddressFamily.InterNetworkV6);
|
||||
client.Client.DualMode = true; // Enable both IPv4 and IPv6
|
||||
using (t.Register(() => client.Close()))
|
||||
{
|
||||
try
|
||||
|
@ -24,6 +24,8 @@
|
||||
<PackageTags>mpd;client;mpcNET</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
|
25
Sources/MpcNET/Types/Filters/FilterAudioFormat.cs
Normal file
25
Sources/MpcNET/Types/Filters/FilterAudioFormat.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace MpcNET.Types.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter "AudioFormat"
|
||||
/// </summary>
|
||||
public class FilterAudioFormat : IFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FilterAudioFormat"/> class.
|
||||
/// </summary>
|
||||
/// <param name="Operator">Operator of the filter</param>
|
||||
/// <param name="Value">Value to test</param>
|
||||
/// <param name="Negation">If the expression is negated</param>
|
||||
public FilterAudioFormat(string Value, FilterOperator Operator, bool Negation = false) : base("AudioFormat", Operator, Value, Negation)
|
||||
{
|
||||
if (Operator != FilterOperator.Equal && Operator != FilterOperator.Mask)
|
||||
throw new System.ArgumentException("Operator is not compatible: for \"AudioFormat\" use FilterOperator.Equal or FilterOperator.Mask.");
|
||||
|
||||
this.Name = "AudioFormat";
|
||||
this.Value = Value;
|
||||
this.Operator = Operator;
|
||||
this.Negation = Negation;
|
||||
}
|
||||
}
|
||||
}
|
25
Sources/MpcNET/Types/Filters/FilterBase.cs
Normal file
25
Sources/MpcNET/Types/Filters/FilterBase.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace MpcNET.Types.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter "base" (not the interface base class, the base command: restrict search songs to a directory)
|
||||
/// </summary>
|
||||
public class FilterBase : IFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FilterBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="Operator">Operator of the filter</param>
|
||||
/// <param name="Value">Value to test</param>
|
||||
/// <param name="Negation">If the expression is negated</param>
|
||||
public FilterBase(string Value, FilterOperator Operator, bool Negation = false) : base("base", Operator, Value, Negation)
|
||||
{
|
||||
if (Operator != FilterOperator.None)
|
||||
throw new System.ArgumentException("Operator is not compatible: for \"base\" use FilterOperator.None.");
|
||||
|
||||
this.Name = "base";
|
||||
this.Value = Value;
|
||||
this.Operator = Operator;
|
||||
this.Negation = Negation;
|
||||
}
|
||||
}
|
||||
}
|
25
Sources/MpcNET/Types/Filters/FilterFile.cs
Normal file
25
Sources/MpcNET/Types/Filters/FilterFile.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace MpcNET.Types.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter "file"
|
||||
/// </summary>
|
||||
public class FilterFile : IFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FilterFile"/> class.
|
||||
/// </summary>
|
||||
/// <param name="Operator">Operator of the filter</param>
|
||||
/// <param name="Value">Value to test</param>
|
||||
/// <param name="Negation">If the expression is negated</param>
|
||||
public FilterFile(string Value, FilterOperator Operator, bool Negation = false) : base("file", Operator, Value, Negation)
|
||||
{
|
||||
if (Operator != FilterOperator.Equal)
|
||||
throw new System.ArgumentException("Operator is not compatible: for \"File\" use FilterOperator.Equal.");
|
||||
|
||||
this.Name = "file";
|
||||
this.Value = Value;
|
||||
this.Operator = Operator;
|
||||
this.Negation = Negation;
|
||||
}
|
||||
}
|
||||
}
|
25
Sources/MpcNET/Types/Filters/FilterModifiedSince.cs
Normal file
25
Sources/MpcNET/Types/Filters/FilterModifiedSince.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace MpcNET.Types.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter "modified-since"
|
||||
/// </summary>
|
||||
public class FilterModifiedSince : IFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FilterModifiedSince"/> class.
|
||||
/// </summary>
|
||||
/// <param name="Operator">Operator of the filter</param>
|
||||
/// <param name="Value">Value to test</param>
|
||||
/// <param name="Negation">If the expression is negated</param>
|
||||
public FilterModifiedSince(string Value, FilterOperator Operator, bool Negation = false) : base("modified-since", Operator, Value, Negation)
|
||||
{
|
||||
if (Operator != FilterOperator.None)
|
||||
throw new System.ArgumentException("Operator is not compatible: for \"ModifiedSince\" use FilterOperator.None.");
|
||||
|
||||
this.Name = "modified-since";
|
||||
this.Value = Value;
|
||||
this.Operator = Operator;
|
||||
this.Negation = Negation;
|
||||
}
|
||||
}
|
||||
}
|
27
Sources/MpcNET/Types/Filters/FilterTag.cs
Normal file
27
Sources/MpcNET/Types/Filters/FilterTag.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using MpcNET.Tags;
|
||||
|
||||
namespace MpcNET.Types.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Filter "TAG"
|
||||
/// </summary>
|
||||
public class FilterTag : IFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FilterTag"/> class.
|
||||
/// </summary>
|
||||
/// <param name="Tag">The tag.</param>
|
||||
/// <param name="Operator">Operator of the filter</param>
|
||||
/// <param name="Value">Value to test</param>
|
||||
/// <param name="Negation">If the expression is negated</param>
|
||||
public FilterTag(ITag Tag, string Value, FilterOperator Operator, bool Negation = false) : base(Tag.Value, Operator, Value, Negation)
|
||||
{
|
||||
if (Operator != FilterOperator.Equal && Operator != FilterOperator.Different && Operator != FilterOperator.Contains)
|
||||
throw new System.ArgumentException("Operator is not compatible: for \"TAG\" use FilterOperator.Equal, FilterOperator.Different or FilterOperator.Contains.");
|
||||
|
||||
this.Value = Value;
|
||||
this.Operator = Operator;
|
||||
this.Negation = Negation;
|
||||
}
|
||||
}
|
||||
}
|
140
Sources/MpcNET/Types/IFilter.cs
Normal file
140
Sources/MpcNET/Types/IFilter.cs
Normal file
@ -0,0 +1,140 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace MpcNET.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Operators available for filters in the protocol
|
||||
/// </summary>
|
||||
public enum FilterOperator
|
||||
{
|
||||
/// <summary>
|
||||
/// Equal (==)
|
||||
/// </summary>
|
||||
[Description("==")]
|
||||
Equal,
|
||||
/// <summary>
|
||||
/// Different (!=)
|
||||
/// </summary>
|
||||
[Description("!=")]
|
||||
Different,
|
||||
/// <summary>
|
||||
/// Contains (contains)
|
||||
/// </summary>
|
||||
[Description("contains")]
|
||||
Contains,
|
||||
/// <summary>
|
||||
/// Mask (=~)
|
||||
/// </summary>
|
||||
[Description("=~")]
|
||||
Mask,
|
||||
/// <summary>
|
||||
/// None ("")
|
||||
/// </summary>
|
||||
[Description("")]
|
||||
None,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public static class EnumHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="GetDescription"/> of the enum
|
||||
/// </summary>
|
||||
/// <param name="enumValue"></param>
|
||||
public static string GetDescription<T>(this T enumValue)
|
||||
where T : struct, System.IConvertible
|
||||
{
|
||||
if (!typeof(T).IsEnum)
|
||||
return null;
|
||||
|
||||
string description = enumValue.ToString();
|
||||
System.Reflection.FieldInfo fieldInfo = enumValue.GetType().GetField(enumValue.ToString());
|
||||
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
object[] attributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
|
||||
if (attributes != null && attributes.Length > 0)
|
||||
description = ((DescriptionAttribute)attributes[0]).Description;
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter interface specialized by filters in the protocol
|
||||
/// </summary>
|
||||
public abstract class IFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the filter
|
||||
/// </summary>
|
||||
public string Name;
|
||||
/// <summary>
|
||||
/// Operator of the filter
|
||||
/// </summary>
|
||||
public FilterOperator Operator;
|
||||
/// <summary>
|
||||
/// Value to test
|
||||
/// </summary>
|
||||
public string Value;
|
||||
/// <summary>
|
||||
/// If the expression is negated
|
||||
/// </summary>
|
||||
public bool Negation;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="Name">Name of the filter</param>
|
||||
/// <param name="Operator">Operator of the filter</param>
|
||||
/// <param name="Value">Value to test</param>
|
||||
/// <param name="Negation">If the expression is negated</param>
|
||||
public IFilter(string Name, FilterOperator Operator, string Value, bool Negation = false)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Operator = Operator;
|
||||
this.Value = Value;
|
||||
this.Negation = Negation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String values are quoted with single or double quotes,
|
||||
/// and special characters within those values must be escaped with the backslash (\).
|
||||
/// Keep in mind that the backslash is also the escape character on the protocol level,
|
||||
/// which means you may need to use double backslash.
|
||||
///
|
||||
/// Example expression which matches an artist named foo'bar":
|
||||
/// (Artist == "foo\'bar\"")
|
||||
///
|
||||
/// At the protocol level, the command must look like this:
|
||||
/// find "(Artist == \"foo\\'bar\\\"\")"
|
||||
///
|
||||
/// (https://mpd.readthedocs.io/en/stable/protocol.html#filters)
|
||||
/// </summary>
|
||||
/// <param name="value">Value to escape</param>
|
||||
/// <returns></returns>
|
||||
private string escape(string value)
|
||||
{
|
||||
var escapedValue = value.Replace(@"\", @"\\\\")
|
||||
.Replace("'", @"\\'")
|
||||
.Replace(@"""", @"\\\""");
|
||||
|
||||
return $@"\""{escapedValue}\""";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatted command
|
||||
/// </summary>
|
||||
public string GetFormattedCommand()
|
||||
{
|
||||
string command = $"({Name} {Operator.GetDescription()} {escape(Value)})";
|
||||
|
||||
if (Negation)
|
||||
return $"(!{command})";
|
||||
else
|
||||
return command;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user