diff --git a/Sources/MpcNET/Commands/Database/FindAndSearchCommands.cs b/Sources/MpcNET/Commands/Database/FindAndSearchCommands.cs
index 23a06e3..1f407ed 100644
--- a/Sources/MpcNET/Commands/Database/FindAndSearchCommands.cs
+++ b/Sources/MpcNET/Commands/Database/FindAndSearchCommands.cs
@@ -7,248 +7,15 @@
namespace MpcNET.Commands.Database
{
using System.Collections.Generic;
- using System.ComponentModel;
using System.Linq;
using MpcNET.Tags;
using MpcNET.Types;
-
- ///
- /// Operators available for filters in the protocol
- ///
- public enum FilterOperator
- {
- ///
- /// Equal (==)
- ///
- [Description("==")]
- Equal,
- ///
- /// Different (!=)
- ///
- [Description("!=")]
- Different,
- ///
- /// Contains (contains)
- ///
- [Description("contains")]
- Contains,
- ///
- /// Mask (=~)
- ///
- [Description("=~")]
- Mask,
- ///
- /// None ("")
- ///
- [Description("")]
- None,
- }
-
- ///
- ///
- public static class EnumHelper
- {
- ///
- /// of the enum
- ///
- ///
- public static string GetDescription(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;
- }
- }
-
- ///
- /// Filter interface specialized by filters in the protocol
- ///
- public abstract class IFilter
- {
- ///
- /// Name of the filter
- ///
- public string Name;
- ///
- /// Operator of the filter
- ///
- public FilterOperator Operator;
- ///
- /// Value to test
- ///
- public string Value;
- ///
- /// If the expression is negated
- ///
- public bool Negation;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Name of the filter
- /// Operator of the filter
- /// Value to test
- /// If the expression is negated
- public IFilter(string Name, FilterOperator Operator, string Value, bool Negation = false)
- {
- this.Name = Name;
- this.Operator = Operator;
- this.Value = Value;
- this.Negation = Negation;
- }
-
- ///
- /// Gets the formatted command
- ///
- public string GetFormattedCommand()
- {
- string make = "";
-
- if (Negation)
- make += "(!";
-
- make += "(" + Name + " ";
- make += Operator.GetDescription();
- make += " " + "\\\"" + Value + "\\\"" + ")";
-
- if (Negation)
- make += ")";
-
- return make;
- }
- }
-
- ///
- /// Filter "file"
- ///
- public class FilterFile : IFilter
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// Operator of the filter
- /// Value to test
- /// If the expression is negated
- 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;
- }
- }
-
- ///
- /// Filter "TAG"
- ///
- public class FilterTag : IFilter
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The tag.
- /// Operator of the filter
- /// Value to test
- /// If the expression is negated
- 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;
- }
- }
-
- ///
- /// Filter "base" (not the interface base class, the base command: restrict search songs to a directory)
- ///
- public class FilterBase : IFilter
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// Operator of the filter
- /// Value to test
- /// If the expression is negated
- 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;
- }
- }
-
- ///
- /// Filter "modified-since"
- ///
- public class FilterModifiedSince : IFilter
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// Operator of the filter
- /// Value to test
- /// If the expression is negated
- 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;
- }
- }
-
- ///
- /// Filter "AudioFormat"
- ///
- public class FilterAudioFormat : IFilter
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// Operator of the filter
- /// Value to test
- /// If the expression is negated
- 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;
- }
- }
+ using MpcNET.Types.Filters;
///
/// 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
///
public class SearchCommand : BaseFilterCommand
{
@@ -256,16 +23,12 @@ namespace MpcNET.Commands.Database
///
///
public override string CommandName => "search";
- ///
- ///
- ///
- public override string Operand => "contains";
///
/// Initializes a new instance of the class.
///
/// The tag.
- /// The search text.
+ /// The search text.
/// Start of the portion of the results desired
/// End of the portion of the results desired
public SearchCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1) : base(tag, searchText, windowStart, windowEnd) { }
@@ -299,7 +62,7 @@ namespace MpcNET.Commands.Database
///
/// 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
///
public class SearchAddCommand : BaseFilterCommand
{
@@ -307,13 +70,9 @@ namespace MpcNET.Commands.Database
///
///
public override string CommandName => "searchadd";
- ///
- ///
- ///
- public override string Operand => "contains";
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The tag.
/// The search text.
@@ -322,7 +81,7 @@ namespace MpcNET.Commands.Database
public SearchAddCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1) : base(tag, searchText, windowStart, windowEnd) { }
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// List of key/value filters
/// Start of the portion of the results desired
@@ -330,7 +89,7 @@ namespace MpcNET.Commands.Database
public SearchAddCommand(List> filters, int windowStart = -1, int windowEnd = -1) : base(filters, windowStart, windowEnd) { }
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// Filter
/// Start of the portion of the results desired
@@ -338,7 +97,7 @@ namespace MpcNET.Commands.Database
public SearchAddCommand(IFilter filter, int windowStart = -1, int windowEnd = -1) : base(filter, windowStart, windowEnd) { }
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// List of filters
/// Start of the portion of the results desired
@@ -349,7 +108,7 @@ namespace MpcNET.Commands.Database
///
/// 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
///
public class FindCommand : BaseFilterCommand
{
@@ -357,10 +116,6 @@ namespace MpcNET.Commands.Database
///
///
public override string CommandName => "find";
- ///
- ///
- ///
- public override string Operand => "==";
///
/// Initializes a new instance of the class.
@@ -402,8 +157,7 @@ namespace MpcNET.Commands.Database
///
public abstract class BaseFilterCommand : IMpcCommand>
{
- private readonly List> tagFilters;
- private readonly List completeFilters;
+ private readonly List _filters;
private readonly int _start;
private readonly int _end;
@@ -411,11 +165,6 @@ namespace MpcNET.Commands.Database
/// Name of the command to use when deserializing
///
public abstract string CommandName { get; }
- ///
- /// Operand to use between tags and search text. Can be ==, !=, contains...
- ///
- public abstract string Operand { get; }
-
///
/// Initializes a new instance of the class.
@@ -424,10 +173,12 @@ namespace MpcNET.Commands.Database
/// The search text.
/// Start of the portion of the results desired
/// End of the portion of the results desired
- public BaseFilterCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1)
+ /// Operator of the filter
+ public BaseFilterCommand(ITag tag, string searchText, int windowStart = -1, int windowEnd = -1, FilterOperator operand = FilterOperator.Equal)
{
- tagFilters = new List>();
- tagFilters.Add(new KeyValuePair(tag, searchText));
+ _filters = new List();
+ FilterTag Tag = new FilterTag(tag, searchText, operand);
+ _filters.Add(Tag);
_start = windowStart;
_end = windowEnd;
@@ -439,9 +190,11 @@ namespace MpcNET.Commands.Database
/// List of key/value filters
/// Start of the portion of the results desired
/// End of the portion of the results desired
- public BaseFilterCommand(List> filters, int windowStart = -1, int windowEnd = -1)
+ /// Operator of the filter
+ public BaseFilterCommand(List> filters, int windowStart = -1, int windowEnd = -1, FilterOperator operand = FilterOperator.Equal)
{
- tagFilters = filters;
+ _filters = new List();
+ _filters.AddRange(filters.Select(filter => new FilterTag(filter.Key, filter.Value, operand)).ToList());
_start = windowStart;
_end = windowEnd;
@@ -455,8 +208,8 @@ namespace MpcNET.Commands.Database
/// End of the portion of the results desired
public BaseFilterCommand(IFilter filters, int windowStart = -1, int windowEnd = -1)
{
- completeFilters = new List();
- completeFilters.Add(filters);
+ _filters = new List();
+ _filters.Add(filters);
_start = windowStart;
_end = windowEnd;
@@ -470,7 +223,7 @@ namespace MpcNET.Commands.Database
/// End of the portion of the results desired
public BaseFilterCommand(List filters, int windowStart = -1, int windowEnd = -1)
{
- completeFilters = filters;
+ _filters = filters;
_start = windowStart;
_end = windowEnd;
@@ -486,18 +239,10 @@ namespace MpcNET.Commands.Database
{
string cmd = "";
- if (tagFilters != null)
+ if (_filters != null)
{
var serializedFilters = string.Join(" AND ",
- tagFilters.Select(x => $"({x.Key.Value} {Operand} {escape(x.Value)})")
- );
- cmd = $@"{CommandName} ""({serializedFilters})""";
- }
-
- if (completeFilters != null)
- {
- var serializedFilters = string.Join(" AND ",
- completeFilters.Select(x => $"{x.GetFormattedCommand()}")
+ _filters.Select(x => $"{x.GetFormattedCommand()}")
);
cmd = $@"{CommandName} ""({serializedFilters})""";
}
@@ -521,31 +266,6 @@ namespace MpcNET.Commands.Database
{
return MpdFile.CreateList(response.ResponseValues);
}
-
- ///
- /// 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#filter-syntax)
- ///
- /// Value to escape
- ///
- private string escape(string value)
- {
- var escapedValue = value.Replace(@"\", @"\\\\")
- .Replace("'", @"\\'")
- .Replace(@"""", @"\\\""");
-
- return $@"\""{escapedValue}\""";
- }
}
// TODO: rescan
}
diff --git a/Sources/MpcNET/Types/Filters/FilterAudioFormat.cs b/Sources/MpcNET/Types/Filters/FilterAudioFormat.cs
new file mode 100644
index 0000000..3fba471
--- /dev/null
+++ b/Sources/MpcNET/Types/Filters/FilterAudioFormat.cs
@@ -0,0 +1,25 @@
+namespace MpcNET.Types.Filters
+{
+ ///
+ /// Filter "AudioFormat"
+ ///
+ public class FilterAudioFormat : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ 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;
+ }
+ }
+}
diff --git a/Sources/MpcNET/Types/Filters/FilterBase.cs b/Sources/MpcNET/Types/Filters/FilterBase.cs
new file mode 100644
index 0000000..fd3f55e
--- /dev/null
+++ b/Sources/MpcNET/Types/Filters/FilterBase.cs
@@ -0,0 +1,25 @@
+namespace MpcNET.Types.Filters
+{
+ ///
+ /// Filter "base" (not the interface base class, the base command: restrict search songs to a directory)
+ ///
+ public class FilterBase : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ 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;
+ }
+ }
+}
diff --git a/Sources/MpcNET/Types/Filters/FilterFile.cs b/Sources/MpcNET/Types/Filters/FilterFile.cs
new file mode 100644
index 0000000..5be0aef
--- /dev/null
+++ b/Sources/MpcNET/Types/Filters/FilterFile.cs
@@ -0,0 +1,25 @@
+namespace MpcNET.Types.Filters
+{
+ ///
+ /// Filter "file"
+ ///
+ public class FilterFile : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ 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;
+ }
+ }
+}
diff --git a/Sources/MpcNET/Types/Filters/FilterModifiedSince.cs b/Sources/MpcNET/Types/Filters/FilterModifiedSince.cs
new file mode 100644
index 0000000..f7a4f68
--- /dev/null
+++ b/Sources/MpcNET/Types/Filters/FilterModifiedSince.cs
@@ -0,0 +1,25 @@
+namespace MpcNET.Types.Filters
+{
+ ///
+ /// Filter "modified-since"
+ ///
+ public class FilterModifiedSince : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ 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;
+ }
+ }
+}
diff --git a/Sources/MpcNET/Types/Filters/FilterTag.cs b/Sources/MpcNET/Types/Filters/FilterTag.cs
new file mode 100644
index 0000000..7e6f7a1
--- /dev/null
+++ b/Sources/MpcNET/Types/Filters/FilterTag.cs
@@ -0,0 +1,27 @@
+using MpcNET.Tags;
+
+namespace MpcNET.Types.Filters
+{
+ ///
+ /// Filter "TAG"
+ ///
+ public class FilterTag : IFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The tag.
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ 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;
+ }
+ }
+}
diff --git a/Sources/MpcNET/Types/IFilter.cs b/Sources/MpcNET/Types/IFilter.cs
new file mode 100644
index 0000000..78dfdb3
--- /dev/null
+++ b/Sources/MpcNET/Types/IFilter.cs
@@ -0,0 +1,135 @@
+using System.ComponentModel;
+
+namespace MpcNET.Types
+{
+ ///
+ /// Operators available for filters in the protocol
+ ///
+ public enum FilterOperator
+ {
+ ///
+ /// Equal (==)
+ ///
+ [Description("==")]
+ Equal,
+ ///
+ /// Different (!=)
+ ///
+ [Description("!=")]
+ Different,
+ ///
+ /// Contains (contains)
+ ///
+ [Description("contains")]
+ Contains,
+ ///
+ /// Mask (=~)
+ ///
+ [Description("=~")]
+ Mask,
+ ///
+ /// None ("")
+ ///
+ [Description("")]
+ None,
+ }
+
+ ///
+ ///
+ public static class EnumHelper
+ {
+ ///
+ /// of the enum
+ ///
+ ///
+ public static string GetDescription(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;
+ }
+ }
+
+ ///
+ /// Filter interface specialized by filters in the protocol
+ ///
+ public abstract class IFilter
+ {
+ ///
+ /// Name of the filter
+ ///
+ public string Name;
+ ///
+ /// Operator of the filter
+ ///
+ public FilterOperator Operator;
+ ///
+ /// Value to test
+ ///
+ public string Value;
+ ///
+ /// If the expression is negated
+ ///
+ public bool Negation;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Name of the filter
+ /// Operator of the filter
+ /// Value to test
+ /// If the expression is negated
+ public IFilter(string Name, FilterOperator Operator, string Value, bool Negation = false)
+ {
+ this.Name = Name;
+ this.Operator = Operator;
+ this.Value = Value;
+ this.Negation = Negation;
+ }
+
+ ///
+ /// Gets the formatted command
+ ///
+ /// 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)
+ ///
+ public string GetFormattedCommand()
+ {
+ string make = "";
+
+ if (Negation)
+ make += "(!";
+
+ make += "(" + Name + " ";
+ make += Operator.GetDescription();
+ make += " " + "\\\"" + Value + "\\\"" + ")";
+
+ if (Negation)
+ make += ")";
+
+ return make;
+ }
+ }
+}