diff --git a/LibMpc/Enum.cs b/LibMpc/Enum.cs
new file mode 100644
index 0000000..769d85a
--- /dev/null
+++ b/LibMpc/Enum.cs
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+namespace Libmpc
+{
+ ///
+ /// The scope specifier for search commands.
+ ///
+ public enum ScopeSpecifier
+ {
+ ///
+ /// Any attribute of a file.
+ ///
+ Any,
+ ///
+ /// The path and name of the file.
+ ///
+ Filename,
+ ///
+ /// The artist of the track.
+ ///
+ Artist,
+ ///
+ /// The album the track appears on.
+ ///
+ Album,
+ ///
+ /// The title of the track.
+ ///
+ Title,
+ ///
+ /// The index of the track on its album.
+ ///
+ Track,
+ ///
+ /// The name of the track.
+ ///
+ Name,
+ ///
+ /// The genre of the song.
+ ///
+ Genre,
+ ///
+ /// The date the track was released.
+ ///
+ Date,
+ ///
+ /// The composer of the song.
+ ///
+ Composer,
+ ///
+ /// The performer of the song.
+ ///
+ Performer,
+ ///
+ /// A comment for the track.
+ ///
+ Comment,
+ ///
+ /// The disc of a multidisc album the track is on.
+ ///
+ Disc
+ }
+
+}
\ No newline at end of file
diff --git a/LibMpc/Exception.cs b/LibMpc/Exception.cs
new file mode 100644
index 0000000..221ad1b
--- /dev/null
+++ b/LibMpc/Exception.cs
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace Libmpc
+{
+ ///
+ /// Is thrown when a command is to be executed on a disconnected
+ /// where the property is set to false.
+ ///
+ public class NotConnectedException : InvalidOperationException
+ {
+ ///
+ /// Creates a new NotConnectedException.
+ ///
+ public NotConnectedException() : base("Not connected.") {}
+ }
+ ///
+ /// Is thrown when the connect method is invoked on an already connected .
+ ///
+ public class AlreadyConnectedException : InvalidOperationException
+ {
+ ///
+ /// Creates a new AlreadyConnectedException.
+ ///
+ public AlreadyConnectedException() : base("Connected already established.") { }
+ }
+ ///
+ /// Is thrown if the response from a MPD server is not as expected. This should never happen when
+ /// working with a tested version of the MPD server.
+ ///
+ public class InvalidMpdResponseException : Exception
+ {
+ ///
+ /// Creates a new InvalidMpdResponseException.
+ ///
+ public InvalidMpdResponseException() : base( "Invalid Mpd Response." ) {}
+ ///
+ /// Creates a new InvalidMpdResponseException.
+ ///
+ /// A message describing the error.
+ public InvalidMpdResponseException(string message) : base("Invalid Mpd Response: " + message) { }
+ }
+ ///
+ /// Is thrown when the MPD server returns an error to a command.
+ ///
+ public class MpdResponseException : Exception
+ {
+ private int errorCode;
+ private string errorMessage;
+ ///
+ /// The error code of the mpd server.
+ ///
+ public int ErrorCode { get { return this.errorCode; } }
+ ///
+ /// A message describing what went wrong.
+ ///
+ public string ErrorMessage { get { return this.errorMessage; } }
+ ///
+ /// Creates a new MpdResponseException.
+ ///
+ /// The error code of the mpd server.
+ /// A message describing what went wrong.
+ public MpdResponseException(int errorCode, string errorMessage)
+ : base("MPD" + errorCode + " " + errorMessage)
+ {
+ this.errorCode = errorCode;
+ this.errorMessage = errorMessage;
+ }
+ }
+}
diff --git a/LibMpc/Mpc.cs b/LibMpc/Mpc.cs
new file mode 100644
index 0000000..4bb5f22
--- /dev/null
+++ b/LibMpc/Mpc.cs
@@ -0,0 +1,1337 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Libmpc
+{
+ ///
+ /// The delegate for the and events.
+ ///
+ /// The connection firing the event.
+ public delegate void MpcEventDelegate(Mpc connection);
+ ///
+ /// The Mpc class implements all commands for the MPD. It takes care of command building
+ /// and parsing the response into .net objects.
+ ///
+ public class Mpc
+ {
+ private const string TAG_ANY = "any";
+ private const string TAG_FILENAME = "filename";
+
+ private const string TAG_ARTIST = "artist";
+ private const string TAG_ALBUM = "album";
+ private const string TAG_TITLE = "title";
+ private const string TAG_TRACK = "track";
+ private const string TAG_NAME = "name";
+ private const string TAG_GENRE = "genre";
+ private const string TAG_DATE = "date";
+ private const string TAG_COMPOSER = "composer";
+ private const string TAG_PERFORMER = "performer";
+ private const string TAG_COMMENT = "comment";
+ private const string TAG_DISC = "disc";
+
+ private static readonly Regex STATUS_AUDIO_REGEX = new Regex("^(?[0-9]*):(?[0-9]*):(?[0-9]*)$");
+
+ private readonly MpcConnectionEventDelegate onMpcConnectionConnectedDelegate;
+ private readonly MpcConnectionEventDelegate onMpcConnectionDisconnectedDelegate;
+
+ private MpcConnection connection = null;
+
+ ///
+ /// Creates a new Mpc.
+ ///
+ public Mpc()
+ {
+ this.onMpcConnectionConnectedDelegate = new MpcConnectionEventDelegate(this.onMpcConnectionConnected);
+ this.onMpcConnectionDisconnectedDelegate = new MpcConnectionEventDelegate(this.onMpcConnectionDisconnected);
+ }
+ ///
+ /// Is fired when a connection to a MPD server is established.
+ ///
+ public event MpcEventDelegate OnConnected;
+ ///
+ /// Is fired when the connection to the MPD server is closed.
+ ///
+ public event MpcEventDelegate OnDisconnected;
+ ///
+ /// If the Mpc object has a connection that is connected to an MPD.
+ ///
+ public bool Connected
+ {
+ get { return this.connection == null ? false : this.connection.Connected; }
+ }
+ ///
+ /// The MpcConnection used to talk to the server.
+ ///
+ public MpcConnection Connection
+ {
+ get { return this.connection; }
+ set
+ {
+ if (this.connection != null)
+ {
+ this.connection.OnConnected -= this.onMpcConnectionConnectedDelegate;
+ this.connection.OnDisconnected -= this.onMpcConnectionDisconnectedDelegate;
+
+ if (this.connection.Connected)
+ this.onMpcConnectionDisconnected( this.connection );
+ }
+
+ this.connection = value;
+
+ if (this.connection != null)
+ {
+ this.connection.OnConnected += this.onMpcConnectionConnectedDelegate;
+ this.connection.OnDisconnected += this.onMpcConnectionDisconnectedDelegate;
+
+ if (this.connection.Connected)
+ this.onMpcConnectionConnected(this.connection);
+ }
+ }
+ }
+
+ private MpcConnection getConnection()
+ {
+ MpcConnection ret = this.connection;
+ if (ret == null)
+ throw new NotConnectedException();
+ else
+ return ret;
+ }
+
+
+ private void onMpcConnectionConnected(MpcConnection connection)
+ {
+ if (this.OnConnected != null)
+ this.OnConnected.Invoke(this);
+ }
+
+ private void onMpcConnectionDisconnected(MpcConnection connection)
+ {
+ if (this.OnDisconnected != null)
+ this.OnDisconnected.Invoke(this);
+ }
+
+ #region Admin Commands
+ ///
+ /// Disables an MPD output.
+ ///
+ /// The id of the output.
+ /// If the action was successful.
+ public bool DisableOutput(int id)
+ {
+ return !this.getConnection().Exec("disableoutput", new string[] { id.ToString() }).IsError;
+ }
+ ///
+ /// Enables an MPD output.
+ ///
+ /// The id of the output.
+ /// If the action was successful.
+ public bool EnableOutput(int id)
+ {
+ return !this.getConnection().Exec("enableoutput", new string[] { id.ToString() }).IsError;
+ }
+ ///
+ /// Lists all outputs of the MPD.
+ ///
+ /// The list of all MPD outputs.
+ public MpdOutput[] Outputs()
+ {
+ MpdResponse response = this.getConnection().Exec("outputs");
+ if (response.Message.Count % 3 != 0)
+ throw new InvalidMpdResponseException();
+
+ MpdOutput[] ret = new MpdOutput[response.Message.Count / 3];
+
+ for (int i = 0; i < ret.Length; i++)
+ {
+ int id;
+ string name;
+ int enabled;
+
+ KeyValuePair idLine = response[i * 3];
+ if (idLine.Key == null)
+ throw new InvalidMpdResponseException("Invalid form of line " + (i * 3));
+ if (!idLine.Key.Equals("outputid"))
+ throw new InvalidMpdResponseException("Key of line " + (i * 3) + " is not 'outputid'");
+ if( !int.TryParse(idLine.Value, out id) )
+ throw new InvalidMpdResponseException("Value of line " + (i * 3) + " is not a number");
+
+ KeyValuePair nameLine = response[i * 3 + 1];
+ if (nameLine.Key == null)
+ throw new InvalidMpdResponseException("Invalid form of line " + (i * 3 + 1));
+ if (!nameLine.Key.Equals("outputname"))
+ throw new InvalidMpdResponseException("Key of line " + (i * 3 + 1) + " is not 'outputname'");
+ name = nameLine.Value;
+
+ KeyValuePair enabledLine = response[i * 3 + 2];
+ if (enabledLine.Key == null)
+ throw new InvalidMpdResponseException("Invalid form of line " + (i * 3 + 2));
+ if (!enabledLine.Key.Equals("outputenabled"))
+ throw new InvalidMpdResponseException("Key of line " + (i * 3 + 2) + " is not 'outputenabled'");
+ if (!int.TryParse(enabledLine.Value, out enabled))
+ throw new InvalidMpdResponseException("Value of line " + (i * 3 + 2) + " is not a number");
+
+ ret[i] = new MpdOutput(id, name, enabled > 0);
+ }
+
+ return ret;
+ }
+ ///
+ /// Returns the list of tag types the MPD supports.
+ ///
+ /// The list of tag types the MPD supports.
+ public string[] TagTypes()
+ {
+ MpdResponse response = this.getConnection().Exec("tagtypes");
+
+ string[] ret = new string[response.Message.Count];
+
+ for (int i = 0; i < ret.Length; i++)
+ {
+ KeyValuePair line = response[i];
+ if (!line.Key.Equals("tagtype"))
+ throw new InvalidMpdResponseException("Key of line " + (i) + " is not 'tagtype'");
+ ret[i] = line.Value;
+ }
+
+ return ret;
+ }
+ ///
+ /// Starts an update of the MPD database.
+ ///
+ /// An sequential number of the update process.
+ public int Update()
+ {
+ MpdResponse response = this.getConnection().Exec("update");
+
+ if( response.Message.Count != 1 )
+ throw new InvalidMpdResponseException("Respose message has more than one line.");
+
+ int ret;
+
+ KeyValuePair line = response[0];
+ if (!line.Key.Equals("updating_db"))
+ throw new InvalidMpdResponseException("Key of line 0 is not 'updating_db'");
+ if (!int.TryParse(line.Value, out ret))
+ throw new InvalidMpdResponseException("Value of line 0 is not a number");
+
+ return ret;
+ }
+
+ #endregion
+
+ #region Database Commands
+ ///
+ /// Returns all files in the database who's attribute matches the given token. Works like the Search command but is case sensitive.
+ ///
+ /// Specifies the attribute to search for.
+ /// The value the files attribute must have to be included in the result.
+ /// All files in the database who's attribute matches the given token.
+ public List Find(ScopeSpecifier scopeSpecifier, string token)
+ {
+ if (token == null)
+ throw new ArgumentNullException("token");
+
+ MpdResponse response = this.getConnection().Exec("find", new string[] { this.toTag(scopeSpecifier), token });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.buildList(response);
+ }
+ ///
+ /// Returns all values found in files of the MPD for the given attribute.
+ ///
+ /// The attribute who's values are requested.
+ /// All values found in files of the MPD for the given attribute.
+ public List List(ScopeSpecifier scopeSpecifier)
+ {
+ MpdResponse response = this.getConnection().Exec("list", new string[] { this.toTag(scopeSpecifier) });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return response.getValueList();
+ }
+ ///
+ /// Returns all values for the given attribute found in files of the MPD where another attribute matches a given value.
+ ///
+ /// The attribute whos values are returns.
+ /// The attribute whos value should match a given value for the file to be included in the result.
+ /// The value the searchTag attribute must match for the file to be included in the result.
+ /// All values found in files of the MPD for the given attribute.
+ public List List(ScopeSpecifier resultTag, ScopeSpecifier searchTag, string searchValue)
+ {
+ if (searchValue == null)
+ throw new ArgumentNullException("searchValue");
+
+ MpdResponse response = this.getConnection().Exec("list", new string[] { this.toTag(resultTag), this.toTag(searchTag), searchValue });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return response.getValueList();
+ }
+ ///
+ /// Returns the names of all files and directory found under the given path.
+ ///
+ /// The path whos subdirectories and their files are requested.
+ /// The names of all files and directory found under the given path.
+ public List ListAll(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException("path");
+
+ MpdResponse response = this.getConnection().Exec("listall", new string[] { path });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return response.getValueList();
+ }
+ ///
+ /// Returns the information of all files found in the given path and its subdirectories.
+ ///
+ /// The path of which the file information is requested.
+ /// The information of all files found in the given path and its subdirectories.
+ public List ListAllInfo(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException("path");
+
+ MpdResponse response = this.getConnection().Exec("listallinfo", new string[] { path });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.buildList(response);
+ }
+ ///
+ /// Returns the directory listing of the root directory.
+ ///
+ /// The listing of the root directory.
+ public MpdDirectoryListing LsInfo()
+ {
+ return this.LsInfo(null);
+ }
+ ///
+ /// Returns the directory listing of the given path.
+ ///
+ /// The path whos listing is requested.
+ /// The directory listing of the given path.
+ public MpdDirectoryListing LsInfo(string path)
+ {
+ MpdResponse response;
+ if (path == null)
+ response = this.getConnection().Exec("lsinfo");
+ else
+ response = this.getConnection().Exec("lsinfo", new string[] { path });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return new MpdDirectoryListing(
+ MpdFile.buildList(response),
+ response.getAttributeValueList("directory"),
+ response.getAttributeValueList("playlist"));
+ }
+ ///
+ /// Returns all files in the database who's attribute matches the given token. Works like the Find command but is case insensitive.
+ ///
+ /// Specifies the attribute to search for.
+ /// The value the files attribute must have to be included in the result.
+ /// All files in the database who's attribute matches the given token.
+ public List Search(ScopeSpecifier scopeSpecifier, string token)
+ {
+ if (token == null)
+ throw new ArgumentNullException("token");
+
+ MpdResponse response = this.getConnection().Exec("search", new string[] { this.toTag(scopeSpecifier), token });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.buildList(response);
+ }
+
+ #endregion
+
+ #region Playlist Commands
+ ///
+ /// Adds a file to the playlist.
+ ///
+ /// The name and path of the file to add.
+ public void Add(string filename)
+ {
+ if (filename == null)
+ throw new ArgumentNullException("filename");
+
+ MpdResponse response = this.getConnection().Exec("add", new string[] { filename });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Adds a file to the playlist and returns the id.
+ ///
+ /// The name and path of the file to add.
+ /// The id of the file in the playlist.
+ public int AddId(string filename)
+ {
+ if (filename == null)
+ throw new ArgumentNullException("filename");
+
+ MpdResponse response = this.getConnection().Exec("add", new string[] { filename });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ if (response.Count != 1)
+ throw new InvalidMpdResponseException("Returned more than one line for command addid.");
+
+ string id = response["Id"];
+ if( id == null )
+ throw new InvalidMpdResponseException("Tag Id missing in response to command addid.");
+ int tryId = -1;
+ if( !int.TryParse(id, out tryId) )
+ throw new InvalidMpdResponseException("Tag Id in response to command addid does not contain an number.");
+
+ return tryId;
+ }
+ ///
+ /// Clears the playlist.
+ ///
+ public void Clear()
+ {
+ MpdResponse response = this.getConnection().Exec("clear");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Returns the information of the current song.
+ ///
+ /// The information of the current song.
+ public MpdFile CurrentSong()
+ {
+ MpdResponse response = this.getConnection().Exec("currentsong");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.build(response);
+ }
+ ///
+ /// Deletes the track with the given index from the current playlist.
+ ///
+ /// The index of the track to remove from the playlist.
+ public void Delete(int nr)
+ {
+ MpdResponse response = this.getConnection().Exec("delete", new string[] { nr.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Deletes the track with the given id from the current playlist.
+ ///
+ /// The id of the track to remove from the playlist.
+ public void DeleteId(int id)
+ {
+ MpdResponse response = this.getConnection().Exec("deleteid", new string[] { id.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Loads the playlist with the given name.
+ ///
+ /// The name of the playlist to load.
+ public void Load( string name )
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ MpdResponse response = this.getConnection().Exec("load", new string[] { name });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Renames a playlist.
+ ///
+ /// The old name of the playlist.
+ /// The new name of the playlist.
+ public void Rename(string oldName, string newName)
+ {
+ if (oldName == null)
+ throw new ArgumentNullException("oldName");
+ if (newName == null)
+ throw new ArgumentNullException("newName");
+
+ MpdResponse response = this.getConnection().Exec("rename", new string[] { oldName, newName });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Moves a track within the playlist.
+ ///
+ /// The old index of the track in the playlist.
+ /// The new index of the track in the playlist.
+ public void Move(int oldNr, int newNr)
+ {
+ MpdResponse response = this.getConnection().Exec("move", new string[] { oldNr.ToString(), newNr.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Moves a track within the playlist.
+ ///
+ /// The id of the track to move.
+ /// The new index of the track in the playlist.
+ public void MoveId(int id, int nr)
+ {
+ MpdResponse response = this.getConnection().Exec("moveid", new string[] { id.ToString(), nr.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Returns the meta data of the items in the current playlist.
+ ///
+ /// The meta data of the items in the current playlist.
+ public List PlaylistInfo()
+ {
+ MpdResponse response = this.getConnection().Exec("playlistinfo");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.buildList(response);
+ }
+ ///
+ /// Returns the meta data of a track in the current playlist.
+ ///
+ /// The index of the track in the playlist.
+ /// The meta data of the track in the current playlist.
+ public MpdFile PlaylistInfo( int nr )
+ {
+ MpdResponse response = this.getConnection().Exec("playlistinfo", new string[] { nr.ToString() } );
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.build(response);
+ }
+ ///
+ /// Returns the meta data of the items in the current playlist.
+ ///
+ /// The meta data of the items in the current playlist.
+ public List PlaylistId()
+ {
+ MpdResponse response = this.getConnection().Exec("playlistid");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.buildList(response);
+ }
+ ///
+ /// Returns the meta data of a track in the current playlist.
+ ///
+ /// The id of the track in the playlist.
+ /// The meta data of the track in the current playlist.
+ public MpdFile PlaylistId(int id)
+ {
+ MpdResponse response = this.getConnection().Exec("playlistid", new string[] { id.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.build(response);
+ }
+ ///
+ /// Returns all changed tracks in the playlist since the given version.
+ ///
+ /// The version number.
+ /// All changed songs in the playlist since the given version.
+ public List Plchanges( int version )
+ {
+ MpdResponse response = this.getConnection().Exec("plchanges", new string[] {version.ToString()});
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.buildList(response);
+ }
+ ///
+ /// Returns the ids and positions of the changed tracks in the playlist since the given version.
+ ///
+ ///
+ ///
+ /// The ids and positions of the changed tracks in the playlist since the given version as KeyValuePairs.
+ /// The key is the index and the id is the value.
+ ///
+ public List> PlChangesPosId( int version )
+ {
+ MpdResponse response = this.getConnection().Exec("plchangesposid", new string[] { version.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ if (response.Count % 2 != 0)
+ throw new InvalidMpdResponseException("Response to command plchangesposid contains an odd number of lines!");
+
+ List> ret = new List>();
+
+ for (int i = 0; i < response.Count; i += 2)
+ {
+ KeyValuePair posLine = response[i];
+ KeyValuePair idLine = response[i+1];
+
+ if ((posLine.Key == null) || (posLine.Value == null))
+ throw new InvalidMpdResponseException("Invalid format of line " + i + "!");
+ if ((idLine.Key == null) || (idLine.Value == null))
+ throw new InvalidMpdResponseException("Invalid format of line " + (i + 1) + "!");
+
+ if (!posLine.Key.Equals("cpos"))
+ throw new InvalidMpdResponseException("Line " + i + " does not start with \"cpos\"!");
+ if (!idLine.Key.Equals("Id"))
+ throw new InvalidMpdResponseException("Line " + (i + 1) + " does not start with \"Id\"!");
+
+ int tryPos = -1;
+ if (!int.TryParse(posLine.Value, out tryPos))
+ throw new InvalidMpdResponseException("Tag value on line " + i + " is not a number.");
+ int tryId = -1;
+ if (!int.TryParse(idLine.Value, out tryId))
+ throw new InvalidMpdResponseException("Tag value on line " + (i + 1) + " is not a number.");
+
+ ret.Add(new KeyValuePair(tryPos, tryId));
+ }
+
+
+ return ret;
+ }
+ ///
+ /// Removes the playlist with the given name.
+ ///
+ /// The name of the playlist to remove.
+ public void Rm(string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ MpdResponse response = this.getConnection().Exec("rm", new string[] { name });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Saves the current playlist with the given name.
+ ///
+ /// The name to the save the currenty playlist.
+ public void Save(string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ MpdResponse response = this.getConnection().Exec("save", new string[] { name });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Shuffles the current playlist.
+ ///
+ public void Shuffle()
+ {
+ MpdResponse response = this.getConnection().Exec("shuffle");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Swaps the to tracks in the current playlist.
+ ///
+ /// The index of the first track.
+ /// The index of the second track.
+ public void Swap(int nr1, int nr2)
+ {
+ MpdResponse response = this.getConnection().Exec("swap", new string[] { nr1.ToString(), nr2.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Swaps the to tracks in the current playlist.
+ ///
+ /// The id of the first track.
+ /// The id of the second track.
+ public void SwapId(int id1, int id2)
+ {
+ MpdResponse response = this.getConnection().Exec("swapid", new string[] { id1.ToString(), id2.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Returns the filenames of the tracks in the given playlist.
+ ///
+ /// The playlist whos filename are requested.
+ /// The filenames of the tracks in the given playlist.
+ public List ListPlaylist(string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ MpdResponse response = this.getConnection().Exec("listplaylist", new string[] { name });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return response.getValueList();
+ }
+ ///
+ /// Return the meta data of the tracks in the given playlist.
+ ///
+ /// The playlist whos files meta data are requested.
+ /// The meta data of the tracks in the given playlist.
+ public List ListPlaylistInfo(string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ MpdResponse response = this.getConnection().Exec("listplaylistinfo", new string[] { name });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.buildList(response);
+ }
+ ///
+ /// Add a file to a playlist.
+ ///
+ /// The name of the playlist.
+ /// The path and name of the file to add.
+ public void PlaylistAdd(string name, string file)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+ if (file == null)
+ throw new ArgumentNullException("file");
+
+ MpdResponse response = this.getConnection().Exec("playlistadd", new string[] { name, file });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Clears all tracks from a playlist.
+ ///
+ /// The name of the playlist to clear.
+ public void PlaylistClear(string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ MpdResponse response = this.getConnection().Exec("playlistclear", new string[] { name });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Delete a file from a playlist.
+ ///
+ /// The name of the playlist
+ /// The id of the track to delete.
+ public void PlaylistDelete(string name, int id)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ MpdResponse response = this.getConnection().Exec("playlistdelete", new string[] { name, id.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Moves a track in a playlist.
+ ///
+ /// The name of the playlist.
+ /// The id of the track to move.
+ /// The position to move the track to.
+ public void PlaylistMove(string name, int id, int nr)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ MpdResponse response = this.getConnection().Exec("playlistmove", new string[] { id.ToString(), nr.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Returns the meta data for all tracks in the current playlist whos attribute equals the given value.
+ ///
+ /// The attribute to search for the given value.
+ /// The value to search for in the given attribute.
+ /// The meta data for all tracks in the current playlist whos attribute equals the given value.
+ public List PlaylistFind(ScopeSpecifier scopeSpecifier, string token)
+ {
+ if (token == null)
+ throw new ArgumentNullException("token");
+
+ MpdResponse response = this.getConnection().Exec("playlistfind", new string[] { this.toTag(scopeSpecifier), token });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.buildList(response);
+ }
+ ///
+ /// Returns the meta data for all tracks in the current playlist whos attribute contains the given value.
+ ///
+ /// The attribute to search for the given value.
+ /// The value to search for in the given attribute.
+ /// The meta data for all tracks in the current playlist whos attribute contains the given value.
+ public List PlaylistSearch(ScopeSpecifier scopeSpecifier, string token)
+ {
+ if (token == null)
+ throw new ArgumentNullException("token");
+
+ MpdResponse response = this.getConnection().Exec("playlistsearch", new string[] { this.toTag(scopeSpecifier), token });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return MpdFile.buildList(response);
+ }
+ #endregion
+
+ #region Playback Commands
+ ///
+ /// Sets the seconds to crossfade between songs.
+ ///
+ /// The seconds to crossfade between songs.
+ public void Crossfade(int seconds)
+ {
+ MpdResponse response = this.getConnection().Exec("crossfade", new string[] { seconds.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Starts the playback of the next song in the playlist-
+ ///
+ public void Next()
+ {
+ MpdResponse response = this.getConnection().Exec("next");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Sets the MPD to pause or resume the playback.
+ ///
+ /// If the playback should be paused or resumed.
+ public void Pause(bool pause)
+ {
+ MpdResponse response = this.getConnection().Exec("pause", new string[] { pause ? "1" : "0" });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Starts the playback of the current item in the playlist.
+ ///
+ public void Play()
+ {
+ MpdResponse response = this.getConnection().Exec("play");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Starts the playback of the item with the given index in the playlist.
+ ///
+ /// The index of the track in the playlist to start playing.
+ public void Play(int nr)
+ {
+ MpdResponse response = this.getConnection().Exec("play", new string[] { nr.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Starts the playback of the track in the playlist with the id 0.
+ ///
+ public void PlayId()
+ {
+ MpdResponse response = this.getConnection().Exec("playid");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Starts the playback of the track in the playlist with the given id.
+ ///
+ /// The id of the track to start playing.
+ public void PlayId(int id)
+ {
+ MpdResponse response = this.getConnection().Exec("playid", new string[] { id.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Starts the playback of the previous track in the playlist.
+ ///
+ public void Previous()
+ {
+ MpdResponse response = this.getConnection().Exec("previous");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Sets the MPD to random or sequential playback.
+ ///
+ /// If the MPD playlist should be played randomly.
+ public void Random(bool random)
+ {
+ MpdResponse response = this.getConnection().Exec("random", new string[] { random ? "1" : "0" });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Sets if the MPD should repeat the playlist.
+ ///
+ /// If the MPD should repeat the playlist.
+ public void Repeat(bool repeat)
+ {
+ MpdResponse response = this.getConnection().Exec("repeat", new string[] { repeat ? "1" : "0" });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Starts playback of a given song at the give position.
+ ///
+ /// The index of the song in the playlist.
+ /// The number of seconds to start playback on.
+ public void Seek(int nr, int time)
+ {
+ MpdResponse response = this.getConnection().Exec("seek", new string[] { nr.ToString(), time.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Starts playback of a given song at the give position.
+ ///
+ /// The id of the song in the playlist.
+ /// The number of seconds to start playback on.
+ public void SeekId(int id, int time)
+ {
+ MpdResponse response = this.getConnection().Exec("seekid", new string[] { id.ToString(), time.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Sets the output volume of the MPD.
+ ///
+ /// The output volume of the MPD between 0 and 100.
+ public void SetVol(int vol)
+ {
+ if (vol < 0)
+ throw new ArgumentException("vol < 0");
+ if (vol > 100)
+ throw new ArgumentException("vol > 100");
+
+ MpdResponse response = this.getConnection().Exec("setvol", new string[] { vol.ToString() });
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+ ///
+ /// Stops the playback of the MPD.
+ ///
+ public void Stop()
+ {
+ MpdResponse response = this.getConnection().Exec("stop");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+ }
+
+ #endregion
+
+ #region Misc Commands
+ ///
+ /// Clears the error message set in the MPD.
+ ///
+ public void ClearError()
+ {
+ this.getConnection().Exec("clearerror");
+ }
+ ///
+ /// Returns which commands the current user has access to.
+ ///
+ /// The commands the current user has access to.
+ public List Commands()
+ {
+ MpdResponse response = this.getConnection().Exec("commands");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return response.getValueList();
+ }
+ ///
+ /// Returns which commands the current user does has access to.
+ ///
+ /// The commands the current user does has access to.
+ public List NotCommands()
+ {
+ MpdResponse response = this.getConnection().Exec("notcommands");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ return response.getValueList();
+ }
+ ///
+ /// Send the password to the server allow access to the server if enabled in the MPD.
+ ///
+ /// The password to authorize to the server.
+ /// If the password is valid.
+ public bool Password(string password)
+ {
+ if (password == null)
+ throw new ArgumentNullException("password");
+
+ return this.getConnection().Exec("password", new string[] { password }).IsError;
+ }
+ ///
+ /// Sends a ping command to the server and waits for the response.
+ ///
+ public void Ping()
+ {
+ this.getConnection().Exec("ping");
+ }
+ ///
+ /// Requests the current statistics from the MPD,
+ ///
+ /// The current statistics fromt the MPD.
+ public MpdStatistics Stats()
+ {
+ MpdResponse response = this.getConnection().Exec("stats");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ int artists = -1;
+ int albums = -1;
+ int songs = -1;
+ int uptime = -1;
+ int playtime = -1;
+ int db_playtime = -1;
+ int db_update = -1;
+
+ foreach (KeyValuePair line in response)
+ {
+ if( ( line.Key != null ) && ( line.Value!=null ) )
+ switch (line.Key)
+ {
+ case "artists":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ artists = tryValue;
+ }
+ break;
+ case "albums":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ albums = tryValue;
+ }
+ break;
+ case "songs":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ songs = tryValue;
+ }
+ break;
+ case "uptime":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ uptime = tryValue;
+ }
+ break;
+ case "playtime":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ playtime = tryValue;
+ }
+ break;
+ case "db_playtime":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ db_playtime = tryValue;
+ }
+ break;
+ case "db_update":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ db_update = tryValue;
+ }
+ break;
+ }
+ }
+
+ return new MpdStatistics(artists, albums, songs, uptime, playtime, db_playtime, db_update);
+ }
+ ///
+ /// Returns the current status of the MPD.
+ ///
+ /// The current status of the MPD.
+ public MpdStatus Status()
+ {
+ MpdResponse response = this.getConnection().Exec("status");
+
+ if (response.IsError)
+ throw new MpdResponseException(response.ErrorCode, response.ErrorMessage);
+
+ int volume = -1;
+ bool repeat = false;
+ bool random = false;
+ int playlist = -1;
+ int playlistLength = -1;
+ int playlistQueue = -1;
+ int xFade = -1;
+ MpdState state = MpdState.Unknown;
+ int song = -1;
+ int songId = -1;
+ int timeElapsed = -1;
+ int timeTotal = -1;
+ int bitrate = -1;
+ int audioSampleRate = -1;
+ int audioBits = -1;
+ int audioChannels = -1;
+ int updatingDb = -1;
+ string error = null;
+
+ foreach (KeyValuePair line in response)
+ {
+ if ( (line.Key != null) && (line.Value!=null) )
+ switch (line.Key)
+ {
+ case "volume":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ {
+ volume = tryValue;
+ if (volume < 0)
+ volume = 0;
+ if (volume > 100)
+ volume = 100;
+ }
+ }
+ break;
+ case "repeat":
+ repeat = (line.Value != null) && (line.Value.Equals("1"));
+ break;
+ case "random":
+ random = (line.Value != null) && (line.Value.Equals("1"));
+ break;
+ case "playlist":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ playlist = tryValue;
+ }
+ break;
+ case "playlistlength":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ playlistLength = tryValue;
+ }
+ break;
+ case "playlistqueue":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ playlistQueue = tryValue;
+ }
+ break;
+ case "xfade":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ xFade = tryValue;
+ }
+ break;
+ case "state":
+ switch (line.Value)
+ {
+ case "play":
+ state = MpdState.Play;
+ break;
+ case "pause":
+ state = MpdState.Pause;
+ break;
+ case "stop":
+ state = MpdState.Stop;
+ break;
+ }
+ break;
+ case "song":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ song = tryValue;
+ }
+ break;
+ case "songid":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ songId = tryValue;
+ }
+ break;
+ case "time":
+ int index = line.Value.IndexOf(':');
+ if (index >= 0)
+ {
+ int tryValue;
+ if (int.TryParse(line.Value.Substring(0, index), out tryValue))
+ timeElapsed = tryValue;
+ if (int.TryParse(line.Value.Substring(index+1), out tryValue))
+ timeTotal = tryValue;
+ }
+ break;
+ case "bitrate":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ bitrate = tryValue;
+ }
+ break;
+ case "audio":
+ Match match = STATUS_AUDIO_REGEX.Match(line.Value);
+ if (match.Success)
+ {
+ int tryValue;
+ if (int.TryParse(match.Result("$sampleRate"), out tryValue))
+ audioSampleRate = tryValue;
+ if (int.TryParse(match.Result("$bits"), out tryValue))
+ audioBits = tryValue;
+ if (int.TryParse(match.Result("$channels"), out tryValue))
+ audioChannels = tryValue;
+ }
+ break;
+ case "updating_db":
+ {
+ int tryValue;
+ if (int.TryParse(line.Value, out tryValue))
+ updatingDb = tryValue;
+ }
+ break;
+ case "error":
+ error = line.Value;
+ break;
+ }
+ }
+
+ return new MpdStatus(
+ volume,
+ repeat,
+ random,
+ playlist,
+ playlistLength,
+ xFade,
+ state,
+ song,
+ songId,
+ timeElapsed,
+ timeTotal,
+ bitrate,
+ audioSampleRate,
+ audioBits,
+ audioChannels,
+ updatingDb,
+ error
+ );
+ }
+
+ #endregion
+
+ private string toTag(ScopeSpecifier scopeSpecifier)
+ {
+ switch (scopeSpecifier)
+ {
+ default:
+ throw new ArgumentException("scopeSpecifier");
+ case ScopeSpecifier.Any:
+ return TAG_ANY;
+ case ScopeSpecifier.Filename:
+ return TAG_FILENAME;
+ case ScopeSpecifier.Artist:
+ return TAG_ARTIST;
+ case ScopeSpecifier.Album:
+ return TAG_ALBUM;
+ case ScopeSpecifier.Title:
+ return TAG_TITLE;
+ case ScopeSpecifier.Track:
+ return TAG_TRACK;
+ case ScopeSpecifier.Name:
+ return TAG_NAME;
+ case ScopeSpecifier.Genre:
+ return TAG_GENRE;
+ case ScopeSpecifier.Date:
+ return TAG_DATE;
+ case ScopeSpecifier.Composer:
+ return TAG_COMPOSER;
+ case ScopeSpecifier.Performer:
+ return TAG_PERFORMER;
+ case ScopeSpecifier.Comment:
+ return TAG_COMMENT;
+ case ScopeSpecifier.Disc:
+ return TAG_DISC;
+ }
+ }
+ }
+}
diff --git a/LibMpc/MpcConnection.cs b/LibMpc/MpcConnection.cs
new file mode 100644
index 0000000..1a5d2d4
--- /dev/null
+++ b/LibMpc/MpcConnection.cs
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Libmpc
+{
+ ///
+ /// The delegate for the and events.
+ ///
+ /// The connection firing the event.
+ public delegate void MpcConnectionEventDelegate( MpcConnection connection );
+ ///
+ /// Keeps the connection to the MPD server and handels the most basic structure of the
+ /// MPD protocol. The high level commands are handeled in the
+ /// class.
+ ///
+ public class MpcConnection
+ {
+ ///
+ /// Is fired when a connection to a MPD server is established.
+ ///
+ public event MpcConnectionEventDelegate OnConnected;
+ ///
+ /// Is fired when the connection to the MPD server is closed.
+ ///
+ public event MpcConnectionEventDelegate OnDisconnected;
+
+ private static readonly string FIRST_LINE_PREFIX = "OK MPD ";
+
+ private static readonly string OK = "OK";
+ private static readonly string ACK = "ACK";
+
+ private static readonly Regex ACK_REGEX = new Regex("^ACK \\[(?[0-9]*)@(?[0-9]*)] \\{(?[a-z]*)} (?.*)$");
+
+ private IPEndPoint ipEndPoint = null;
+
+ private TcpClient tcpClient = null;
+ private NetworkStream networkStream = null;
+
+ private StreamReader reader;
+ private StreamWriter writer;
+
+ private string version;
+ ///
+ /// If the connection to the MPD is connected.
+ ///
+ public bool Connected { get { return (this.tcpClient != null) && this.tcpClient.Connected; } }
+ ///
+ /// The version of the MPD.
+ ///
+ public string Version { get { return this.version; } }
+
+ private bool autoConnect = false;
+ ///
+ /// If a connection should be established when a command is to be
+ /// executed in disconnected state.
+ ///
+ public bool AutoConnect
+ {
+ get{ return this.autoConnect; }
+ set { this.autoConnect = value; }
+ }
+ ///
+ /// Creates a new MpdConnection.
+ ///
+ public MpcConnection() {}
+ ///
+ /// Creates a new MpdConnection.
+ ///
+ /// The IPEndPoint of the MPD server.
+ public MpcConnection(IPEndPoint server) { this.Connect(server); }
+ ///
+ /// The IPEndPoint of the MPD server.
+ ///
+ /// When a conenction to a MPD server is already established.
+ public IPEndPoint Server
+ {
+ get { return this.ipEndPoint; }
+ set
+ {
+ if (this.Connected)
+ throw new AlreadyConnectedException();
+
+ this.ipEndPoint = value;
+
+ this.ClearConnectionFields();
+ }
+ }
+ ///
+ /// Connects to a MPD server.
+ ///
+ /// The IPEndPoint of the server.
+ public void Connect(IPEndPoint server)
+ {
+ this.Server = server;
+ this.Connect();
+ }
+ ///
+ /// Connects to the MPD server who's IPEndPoint was set in the Server property.
+ ///
+ /// If no IPEndPoint was set to the Server property.
+ public void Connect()
+ {
+ if (this.ipEndPoint == null)
+ throw new InvalidOperationException("Server IPEndPoint not set.");
+
+ if (this.Connected)
+ throw new AlreadyConnectedException();
+
+ this.tcpClient = new TcpClient(
+ this.ipEndPoint.Address.ToString(),
+ this.ipEndPoint.Port);
+ this.networkStream = this.tcpClient.GetStream();
+
+ this.reader = new StreamReader(this.networkStream, Encoding.UTF8);
+ this.writer = new StreamWriter(this.networkStream, Encoding.UTF8);
+ this.writer.NewLine = "\n";
+
+ string firstLine = this.reader.ReadLine();
+ if( !firstLine.StartsWith( FIRST_LINE_PREFIX ) )
+ {
+ this.Disconnect();
+ throw new InvalidDataException("Response of mpd does not start with \"" + FIRST_LINE_PREFIX + "\"." );
+ }
+ this.version = firstLine.Substring(FIRST_LINE_PREFIX.Length);
+
+ this.writer.WriteLine();
+ this.writer.Flush();
+
+ this.readResponse();
+
+ if( this.OnConnected != null )
+ this.OnConnected.Invoke( this );
+ }
+ ///
+ /// Disconnects from the current MPD server.
+ ///
+ public void Disconnect()
+ {
+ if (this.tcpClient == null)
+ return;
+
+ this.networkStream.Close();
+
+ this.ClearConnectionFields();
+
+ if( this.OnDisconnected != null )
+ this.OnDisconnected.Invoke( this );
+ }
+ ///
+ /// Executes a simple command without arguments on the MPD server and returns the response.
+ ///
+ /// The command to execute.
+ /// The MPD server response parsed into a basic object.
+ /// If the command contains a space of a newline charakter.
+ public MpdResponse Exec(string command)
+ {
+ if (command == null)
+ throw new ArgumentNullException("command");
+ if (command.Contains(" "))
+ throw new ArgumentException("command contains space");
+ if (command.Contains("\n"))
+ throw new ArgumentException("command contains newline");
+
+ this.CheckConnected();
+
+ try
+ {
+ this.writer.WriteLine(command);
+ this.writer.Flush();
+
+ return this.readResponse();
+ }
+ catch (Exception)
+ {
+ try { this.Disconnect(); }
+ catch (Exception) { }
+ throw;
+ }
+ }
+ ///
+ /// Executes a MPD command with arguments on the MPD server.
+ ///
+ /// The command to execute.
+ /// The arguments of the command.
+ /// The MPD server response parsed into a basic object.
+ /// If the command contains a space of a newline charakter.
+ public MpdResponse Exec(string command, string[] argument)
+ {
+ if (command == null)
+ throw new ArgumentNullException("command");
+ if (command.Contains(" "))
+ throw new ArgumentException("command contains space");
+ if (command.Contains("\n"))
+ throw new ArgumentException("command contains newline");
+
+ if (argument == null)
+ throw new ArgumentNullException("argument");
+ for (int i = 0; i < argument.Length; i++)
+ {
+ if (argument[i] == null)
+ throw new ArgumentNullException("argument[" + i + "]");
+ if (argument[i].Contains("\n"))
+ throw new ArgumentException("argument[" + i + "] contains newline");
+ }
+
+ this.CheckConnected();
+
+ try
+ {
+ this.writer.Write(command);
+ foreach (string arg in argument)
+ {
+ this.writer.Write(' ');
+ this.WriteToken(arg);
+ }
+ this.writer.WriteLine();
+ this.writer.Flush();
+
+ return this.readResponse();
+ }
+ catch (Exception)
+ {
+ try { this.Disconnect(); } catch (Exception) { }
+ throw;
+ }
+ }
+
+ private void CheckConnected()
+ {
+ if (!this.Connected)
+ {
+ if (this.autoConnect)
+ this.Connect();
+ else
+ throw new NotConnectedException();
+ }
+
+ }
+
+ private void WriteToken(string token)
+ {
+ if (token.Contains(" "))
+ {
+ this.writer.Write("\"");
+ foreach (char chr in token)
+ if (chr == '"')
+ this.writer.Write("\\\"");
+ else
+ this.writer.Write(chr);
+ }
+ else
+ this.writer.Write(token);
+ }
+
+ private MpdResponse readResponse()
+ {
+ List ret = new List();
+ string line = this.reader.ReadLine();
+ while (!(line.Equals(OK) || line.StartsWith(ACK)))
+ {
+ ret.Add(line);
+ line = this.reader.ReadLine();
+ }
+ if (line.Equals(OK))
+ return new MpdResponse(new ReadOnlyCollection(ret));
+ else
+ {
+ Match match = ACK_REGEX.Match(line);
+
+ if (match.Groups.Count != 5)
+ throw new InvalidDataException( "Error response not as expected" );
+
+ return new MpdResponse(
+ int.Parse( match.Result("${code}") ),
+ int.Parse( match.Result("${nr}") ),
+ match.Result("${command}"),
+ match.Result("${message}"),
+ new ReadOnlyCollection(ret)
+ );
+ }
+ }
+
+ private void ClearConnectionFields()
+ {
+ this.tcpClient = null;
+ this.networkStream = null;
+ this.reader = null;
+ this.writer = null;
+ this.version = null;
+ }
+ }
+}
diff --git a/LibMpc/MpdDirectoryListing.cs b/LibMpc/MpdDirectoryListing.cs
new file mode 100644
index 0000000..8949557
--- /dev/null
+++ b/LibMpc/MpdDirectoryListing.cs
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+using System;
+using System.Collections.ObjectModel;
+using System.Collections.Generic;
+
+namespace Libmpc
+{
+ ///
+ /// The MpdDirectoryListing class contains the response of a MPD server to a list command.
+ ///
+ public class MpdDirectoryListing
+ {
+ private readonly ReadOnlyCollection file;
+ private readonly ReadOnlyCollection directory;
+ private readonly ReadOnlyCollection playlist;
+ ///
+ /// The list of files in the directory.
+ ///
+ public ReadOnlyCollection FileList { get { return this.file; } }
+ ///
+ /// The list of subdirectories in the directory.
+ ///
+ public ReadOnlyCollection DirectoryList { get { return this.directory; } }
+ ///
+ /// The list of playlists in the directory.
+ ///
+ public ReadOnlyCollection PlaylistList { get { return this.playlist; } }
+ ///
+ /// Creates a new MpdDirectoryListing.
+ ///
+ /// The list of files in the directory.
+ /// The list of subdirectories in the directory.
+ /// The list of playlists in the directory.
+ public MpdDirectoryListing(List file, List directory, List playlist)
+ {
+ if (file == null)
+ throw new ArgumentNullException("file");
+ if (directory == null)
+ throw new ArgumentNullException("directory");
+ if (playlist == null)
+ throw new ArgumentNullException("playlist");
+
+ this.file = new ReadOnlyCollection(file);
+ this.directory = new ReadOnlyCollection(directory);
+ this.playlist = new ReadOnlyCollection(playlist);
+ }
+ ///
+ /// Creates a new MpdDirectoryListing.
+ ///
+ /// The list of files in the directory.
+ /// The list of subdirectories in the directory.
+ /// The list of playlists in the directory.
+ public MpdDirectoryListing(ReadOnlyCollection file, ReadOnlyCollection directory, ReadOnlyCollection playlist)
+ {
+ if (file == null)
+ throw new ArgumentNullException("file");
+ if (directory == null)
+ throw new ArgumentNullException("directory");
+ if (playlist == null)
+ throw new ArgumentNullException("playlist");
+
+ this.file = file;
+ this.directory = directory;
+ this.playlist = playlist;
+ }
+ }
+}
diff --git a/LibMpc/MpdFile.cs b/LibMpc/MpdFile.cs
new file mode 100644
index 0000000..9785e79
--- /dev/null
+++ b/LibMpc/MpdFile.cs
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Libmpc
+{
+ ///
+ /// The MpdFile class contains all meta data for a file of the MPD.
+ ///
+ public class MpdFile
+ {
+ private const string TAG_FILE = "file";
+ private const string TAG_TIME = "Time";
+ private const string TAG_ARTIST = "Artist";
+ private const string TAG_ALBUM = "Album";
+ private const string TAG_TITLE = "Title";
+ private const string TAG_TRACK = "Track";
+ private const string TAG_NAME = "Name";
+ private const string TAG_GENRE = "Genre";
+ private const string TAG_DATE = "Date";
+ private const string TAG_COMPOSER = "Composer";
+ private const string TAG_PERFORMER = "Performer";
+ private const string TAG_COMMENT = "Comment";
+ private const string TAG_DISC = "Disc";
+ private const string TAG_POS = "Pos";
+ private const string TAG_ID = "Id";
+
+ private const int NO_TIME = -1;
+ private const string NO_ALBUM = null;
+ private const string NO_ARTIST = null;
+ private const string NO_TITLE = null;
+ private const string NO_TRACK = null;
+ private const string NO_NAME = null;
+ private const string NO_GENRE = null;
+ private const string NO_DATE = null;
+ private const string NO_COMPOSER = null;
+ private const string NO_PERFORMER = null;
+ private const string NO_COMMENT = null;
+ private const int NO_DISC = -1;
+ private const int NO_POS = -1;
+ private const int NO_ID = -1;
+
+ private readonly string file;
+ private readonly int time;
+ private readonly string album;
+ private readonly string artist;
+ private readonly string title;
+ private readonly string track;
+ private readonly string name;
+ private readonly string genre;
+ private readonly string date;
+ private readonly string composer;
+ private readonly string performer;
+ private readonly string comment;
+ private readonly int disc;
+ private readonly int pos;
+ private readonly int id;
+ ///
+ /// The name and path of the file.
+ ///
+ public string File { get { return this.file; } }
+ ///
+ /// The length of the file in seconds.
+ ///
+ public int Time { get { return this.time; } }
+ ///
+ /// The album of the file.
+ ///
+ public string Album { get { return this.album; } }
+ ///
+ /// The artist of the file.
+ ///
+ public string Artist { get { return this.artist; } }
+ ///
+ /// The title of the file.
+ ///
+ public string Title { get { return this.title; } }
+ ///
+ /// The value of the track property of the file.
+ ///
+ public string Track { get { return this.track; } }
+ ///
+ /// The name of the song.
+ ///
+ public string Name { get { return this.name; } }
+ ///
+ /// The genre of the song.
+ ///
+ public string Genre { get { return this.genre; } }
+ ///
+ /// The date the song was released.
+ ///
+ public string Date { get { return this.date; } }
+ ///
+ /// The composer of the song.
+ ///
+ public string Composer { get { return this.composer; } }
+ ///
+ /// The performer of the song.
+ ///
+ public string Performer { get { return this.performer; } }
+ ///
+ /// A comment to the file.
+ ///
+ public string Comment { get { return this.comment; } }
+ ///
+ /// The number of the disc on a multidisc album.
+ ///
+ public int Disc { get { return this.disc; } }
+ ///
+ /// The index of the file in a playlist.
+ ///
+ public int Pos { get { return this.pos; } }
+ ///
+ /// The id of the file in a playlist.
+ ///
+ public int Id { get { return this.id; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasTime { get { return this.time != NO_TIME; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasAlbum { get { return this.album != NO_ALBUM; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasArtist { get { return this.artist != NO_ARTIST; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasTitle { get { return this.title != NO_TITLE; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasTrack { get { return this.track != NO_TRACK; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasName { get { return this.name != NO_NAME; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasGenre { get { return this.genre != NO_GENRE; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasDate { get { return this.date != NO_DATE; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasComposer { get { return this.composer != NO_COMPOSER; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasPerformer { get { return this.performer != NO_PERFORMER; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasComment { get { return this.comment != NO_COMMENT; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasDisc { get { return this.disc != NO_DISC; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasPos { get { return this.pos != NO_POS; } }
+ ///
+ /// If the MpdFile has the property set.
+ ///
+ public bool HasId { get { return this.id != NO_ID; } }
+ ///
+ /// Creates a new MpdFile.
+ ///
+ /// The name and path of the file.
+ /// The length of the file in seconds.
+ /// The album of the file.
+ /// The artist of the file.
+ /// The title of the file.
+ /// The value of the track property of the file.
+ /// The name of the song.
+ /// The genre of the song.
+ /// The date the song was released.
+ /// The composer of the song.
+ /// The performer of the song.
+ /// A comment to the file.
+ /// The number of the disc on a multidisc album.
+ /// The index of the file in a playlist.
+ /// The id of the file in a playlist.
+ public MpdFile(string file,
+ int time,
+ string album,
+ string artist,
+ string title,
+ string track,
+ string name,
+ string genre,
+ string date,
+ string composer,
+ string performer,
+ string comment,
+ int disc,
+ int pos,
+ int id)
+ {
+ if (file == null)
+ throw new ArgumentNullException("file");
+
+ this.file = file;
+ this.time = time;
+ this.album = album;
+ this.artist = artist;
+ this.title = title;
+ this.track = track;
+ this.name = name;
+ this.genre = genre;
+ this.date = date;
+ this.composer = composer;
+ this.performer = performer;
+ this.comment = comment;
+ this.disc = disc;
+ this.pos = pos;
+ this.id = id;
+ }
+ ///
+ /// A string containing all the properties of the file.
+ ///
+ ///
+ public override string ToString()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ appendString(builder, TAG_FILE, this.file);
+ if (this.HasTime)
+ appendInt(builder, TAG_TIME, this.time);
+ if (this.HasAlbum)
+ appendString(builder, TAG_ALBUM, this.album);
+ if (this.HasArtist)
+ appendString(builder, TAG_ARTIST, this.artist);
+ if (this.HasTitle)
+ appendString(builder, TAG_TITLE, this.title);
+ if (this.HasTrack)
+ appendString(builder, TAG_TRACK, this.track);
+ if (this.HasName)
+ appendString(builder, TAG_NAME, this.name);
+ if (this.HasGenre)
+ appendString(builder, TAG_GENRE, this.genre);
+ if (this.HasDate)
+ appendString(builder, TAG_DATE, this.date);
+ if (this.HasComposer)
+ appendString(builder, TAG_COMPOSER, this.composer);
+ if (this.HasPerformer)
+ appendString(builder, TAG_PERFORMER, this.performer);
+ if (this.HasComment)
+ appendString(builder, TAG_COMMENT, this.comment);
+ if (this.HasDisc)
+ appendInt(builder, TAG_DISC, this.disc);
+ if (this.HasPos)
+ appendInt(builder, TAG_POS, this.pos);
+ if (this.HasId)
+ appendInt(builder, TAG_ID, this.id);
+
+ return builder.ToString();
+ }
+
+ private static void appendString(StringBuilder builder, string name, string value)
+ {
+ builder.Append(name);
+ builder.Append(": ");
+ builder.Append(value);
+ builder.AppendLine();
+ }
+
+ private static void appendInt(StringBuilder builder, string name, int value)
+ {
+ builder.Append(name);
+ builder.Append(": ");
+ builder.Append(value);
+ builder.AppendLine();
+ }
+ ///
+ /// Returns a MpdFile object from a MpdResponse object.
+ ///
+ /// The response of the MPD server.
+ /// A new MpdFile object build from the MpdResponse object.
+ public static MpdFile build(MpdResponse response)
+ {
+ if (response == null)
+ throw new ArgumentNullException("response");
+
+ string file = null;
+ int time = NO_TIME;
+ string album = NO_ALBUM;
+ string artist = NO_ARTIST;
+ string title = NO_TITLE;
+ string track = NO_TRACK;
+ string name = NO_NAME;
+ string genre = NO_GENRE;
+ string date = NO_DATE;
+ string composer = NO_COMPOSER;
+ string performer = NO_PERFORMER;
+ string comment = NO_COMMENT;
+ int disc = NO_DISC;
+ int pos = NO_POS;
+ int id = NO_ID;
+
+
+ foreach (KeyValuePair line in response)
+ {
+ if( line.Key != null )
+ switch (line.Key)
+ {
+ case TAG_FILE:
+ file = line.Value;
+ break;
+ case TAG_TIME:
+ int tryTime;
+ if( int.TryParse( line.Value, out tryTime ) )
+ time = tryTime;
+ break;
+ case TAG_ALBUM:
+ album = line.Value;
+ break;
+ case TAG_ARTIST:
+ artist = line.Value;
+ break;
+ case TAG_TITLE:
+ title = line.Value;
+ break;
+ case TAG_TRACK:
+ track = line.Value;
+ break;
+ case TAG_NAME:
+ name = line.Value;
+ break;
+ case TAG_GENRE:
+ genre = line.Value;
+ break;
+ case TAG_DATE:
+ date = line.Value;
+ break;
+ case TAG_COMPOSER:
+ composer = line.Value;
+ break;
+ case TAG_PERFORMER:
+ performer = line.Value;
+ break;
+ case TAG_COMMENT:
+ comment = line.Value;
+ break;
+ case TAG_DISC:
+ int tryDisc;
+ if (int.TryParse(line.Value, out tryDisc))
+ disc = tryDisc;
+ break;
+ case TAG_POS:
+ int tryPos;
+ if (int.TryParse(line.Value, out tryPos))
+ pos = tryPos;
+ break;
+ case TAG_ID:
+ int tryId;
+ if (int.TryParse(line.Value, out tryId))
+ id = tryId;
+ break;
+ }
+ }
+
+ if (file == null)
+ return null;
+ else
+ return new MpdFile(
+ file,
+ time,
+ album,
+ artist,
+ title,
+ track,
+ name,
+ genre,
+ date,
+ composer,
+ performer,
+ comment,
+ disc,
+ pos,
+ id);
+ }
+ ///
+ /// Builds a list of MpdFile objects from a MpdResponse object.
+ ///
+ /// The MpdResponse object to build the list of MpdFiles from.
+ /// A list ob MpdFiles built from the MpdResponse object.
+ public static List buildList(MpdResponse response)
+ {
+ if (response == null)
+ throw new ArgumentNullException("response");
+
+ List ret = new List();
+
+ string file = null;
+ int time = NO_TIME;
+ string album = NO_ALBUM;
+ string artist = NO_ARTIST;
+ string title = NO_TITLE;
+ string track = NO_TRACK;
+ string name = NO_NAME;
+ string genre = NO_GENRE;
+ string date = NO_DATE;
+ string composer = NO_COMPOSER;
+ string performer = NO_PERFORMER;
+ string comment = NO_COMMENT;
+ int disc = NO_DISC;
+ int pos = NO_POS;
+ int id = NO_ID;
+
+
+ foreach (KeyValuePair line in response)
+ {
+ if( line.Key != null )
+ switch (line.Key)
+ {
+ case TAG_FILE:
+ if( file != null )
+ ret.Add( new MpdFile(
+ file,
+ time,
+ album,
+ artist,
+ title,
+ track,
+ name,
+ genre,
+ date,
+ composer,
+ performer,
+ comment,
+ disc,
+ pos,
+ id ) );
+
+ file = line.Value;
+
+ time = NO_TIME;
+ album = NO_ALBUM;
+ artist = NO_ARTIST;
+ title = NO_TITLE;
+ track = NO_TRACK;
+ name = NO_NAME;
+ genre = NO_GENRE;
+ date = NO_DATE;
+ composer = NO_COMPOSER;
+ performer = NO_PERFORMER;
+ comment = NO_COMMENT;
+ disc = NO_DISC;
+ pos = NO_POS;
+ id = NO_ID;
+
+ break;
+ case TAG_TIME:
+ int tryTime;
+ if( int.TryParse( line.Value, out tryTime ) )
+ time = tryTime;
+ break;
+ case TAG_ALBUM:
+ album = line.Value;
+ break;
+ case TAG_ARTIST:
+ artist = line.Value;
+ break;
+ case TAG_TITLE:
+ title = line.Value;
+ break;
+ case TAG_TRACK:
+ track = line.Value;
+ /*
+ int tryTrack;
+ if (int.TryParse(line.Value, out tryTrack))
+ track = tryTrack;
+ */
+ break;
+ case TAG_NAME:
+ name = line.Value;
+ break;
+ case TAG_GENRE:
+ genre = line.Value;
+ break;
+ case TAG_DATE:
+ date = line.Value;
+ break;
+ case TAG_COMPOSER:
+ composer = line.Value;
+ break;
+ case TAG_PERFORMER:
+ performer = line.Value;
+ break;
+ case TAG_COMMENT:
+ comment = line.Value;
+ break;
+ case TAG_DISC:
+ int tryDisc;
+ if (int.TryParse(line.Value, out tryDisc))
+ disc = tryDisc;
+ break;
+ case TAG_POS:
+ int tryPos;
+ if (int.TryParse(line.Value, out tryPos))
+ pos = tryPos;
+ break;
+ case TAG_ID:
+ int tryId;
+ if (int.TryParse(line.Value, out tryId))
+ id = tryId;
+ break;
+ }
+ }
+
+ if (file != null)
+ ret.Add(new MpdFile(
+ file,
+ time,
+ album,
+ artist,
+ title,
+ track,
+ name,
+ genre,
+ date,
+ composer,
+ performer,
+ comment,
+ disc,
+ pos,
+ id ));
+
+ return ret;
+ }
+ }
+}
diff --git a/LibMpc/MpdOutput.cs b/LibMpc/MpdOutput.cs
new file mode 100644
index 0000000..8108355
--- /dev/null
+++ b/LibMpc/MpdOutput.cs
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Libmpc
+{
+ ///
+ /// The MpdOutput class contains all attributes of an output device of the MPD.
+ ///
+ public class MpdOutput
+ {
+ private readonly int id;
+ private readonly string name;
+ private readonly bool enabled;
+ ///
+ /// The id of the output.
+ ///
+ public int Id { get { return this.id; } }
+ ///
+ /// The name of the output.
+ ///
+ public string Name { get { return this.name; } }
+ ///
+ /// If the output is enabled.
+ ///
+ public bool IsEnabled { get { return this.enabled; } }
+ ///
+ /// Creates a new MpdOutput object.
+ ///
+ /// The id of the output.
+ /// The name of the output.
+ /// If the output is enabled.
+ public MpdOutput(int id, string name, bool enabled)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ this.id = id;
+ this.name = name;
+ this.enabled = enabled;
+ }
+ ///
+ /// Returns a string representation of the object mainly for debuging purpose.
+ ///
+ /// A string representation of the object.
+ public override string ToString()
+ {
+ return this.id + "::" + this.name + "::" + this.enabled;
+ }
+ }
+}
diff --git a/LibMpc/MpdResponse.cs b/LibMpc/MpdResponse.cs
new file mode 100644
index 0000000..3a3415f
--- /dev/null
+++ b/LibMpc/MpdResponse.cs
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Libmpc
+{
+ ///
+ /// The MpdResponse class parses the response to an MPD command in it's most
+ /// basic structure.
+ ///
+ public class MpdResponse : IEnumerable>
+ {
+ private const string OK = "OK";
+ private const string ACK = "ACK";
+
+ private static readonly Regex LINE_REGEX = new Regex("^(?[A-Za-z]*):[ ]{0,1}(?.*)$");
+
+ private readonly bool isError;
+ private readonly int errorCode;
+ private readonly int commandListNum;
+ private readonly string currentCommand;
+ private readonly string errorMessage;
+ private readonly ReadOnlyCollection message;
+
+ private Dictionary dictionary = null;
+ ///
+ /// If the response denotes an error in the last command.
+ ///
+ public bool IsError { get { return this.isError; } }
+ ///
+ /// The error code if an error occured.
+ ///
+ public int ErrorCode { get { return this.errorCode; } }
+ ///
+ /// If an error occured the index of the invalid command in a command list.
+ ///
+ public int CommandListNum { get { return this.commandListNum; } }
+ ///
+ /// The command executed.
+ ///
+ public string CurrentCommand { get { return this.currentCommand; } }
+ ///
+ /// The description of the error, if occured.
+ ///
+ public string ErrorMessage { get { return this.errorMessage; } }
+ ///
+ /// The lines of the response message.
+ ///
+ public ReadOnlyCollection Message { get { return this.message; } }
+ ///
+ /// The value of an attribute in the MPD response.
+ ///
+ /// The name of the attribute.
+ /// The value of the attribute
+ public string this[string key]
+ {
+ get
+ {
+ if (this.dictionary == null)
+ {
+ this.dictionary = new Dictionary();
+
+ foreach (string line in this.message)
+ {
+ Match match = LINE_REGEX.Match(line);
+ if (match.Success)
+ this.dictionary[match.Result("$key")] = match.Result("$value");
+ }
+ }
+
+ return this.dictionary[key];
+ }
+ }
+ ///
+ /// The number of lines in the response message.
+ ///
+ public int Count { get { return this.message.Count; } }
+ ///
+ /// A line in the MPD response as KeyValuePair. If the message cannot be separated
+ /// into key and value according to the MPD protocol spec, a KeyValuePair is returned
+ /// with the key null and the value the whole text of the line.
+ ///
+ /// The index of the line.
+ /// The requested line as KeyValuePair.
+ public KeyValuePair this[int line]
+ {
+ get
+ {
+ Match match = LINE_REGEX.Match(this.message[line]);
+ if (match.Success)
+ return new KeyValuePair(match.Result("${key}"), match.Result("${value}"));
+ else
+ return new KeyValuePair(null, this.message[line]);
+ }
+ }
+ ///
+ /// Creates a new MpdResponse from a list of lines in case no error occured.
+ ///
+ /// The response to an MPD command.
+ public MpdResponse( ReadOnlyCollection message )
+ {
+ if (message == null)
+ throw new ArgumentNullException("message");
+
+ this.isError = false;
+ this.errorCode = -1;
+ this.commandListNum = 0;
+ this.currentCommand = null;
+ this.errorMessage = null;
+ this.message = message;
+ }
+ ///
+ /// Creates a new MpdResponse in case an error occured.
+ ///
+ /// The code of the error.
+ /// The index of the command which raised the error.
+ /// The command that raised the error.
+ /// The message describing the error.
+ /// The text of the standard MPD response.
+ public MpdResponse( int errorCode, int commandListNum, string currentCommand, string errorMessage, ReadOnlyCollection message)
+ {
+ if (currentCommand == null)
+ throw new ArgumentNullException("currentCommand");
+ if (errorMessage == null)
+ throw new ArgumentNullException("errorMessage");
+ if (message == null)
+ throw new ArgumentNullException("message");
+
+ this.isError = true;
+ this.errorCode = errorCode;
+ this.commandListNum = commandListNum;
+ this.currentCommand = currentCommand;
+ this.errorMessage = errorMessage;
+ this.message = message;
+ }
+ ///
+ /// Returns the values in all lines with the given attribute.
+ ///
+ /// The attribute who's values are reguested.
+ /// The values in all lines with the given attribute.
+ public List getAttributeValueList(string attribute)
+ {
+ List ret = new List();
+
+ foreach (string line in this.message)
+ {
+ Match match = LINE_REGEX.Match(line);
+ if (match.Success)
+ {
+ string key = match.Result("${key}");
+ if( ( key != null ) && key.Equals( attribute ) )
+ {
+ string value = match.Result("${value}");
+ if( value != null )
+ ret.Add( value );
+ }
+ }
+ }
+
+ return ret;
+ }
+ ///
+ /// Returns only the value parts in all key/value pairs in the response.
+ ///
+ /// The list of values in all key/value pairs in the response.
+ public List getValueList()
+ {
+ List ret = new List();
+
+ foreach (string line in this.message)
+ {
+ Match match = LINE_REGEX.Match(line);
+ if (match.Success)
+ {
+ string value = match.Result("${value}");
+ if (value != null)
+ ret.Add(value);
+ }
+ }
+
+ return ret;
+ }
+ ///
+ /// Builds the response text of the MPD server from the object.
+ ///
+ /// The response text of the MPD server from the object.
+ public override string ToString()
+ {
+ StringBuilder builder = new StringBuilder();
+ foreach (string line in this.message)
+ builder.AppendLine(line);
+
+ if (this.isError)
+ {
+ builder.Append(ACK);
+ builder.Append(" [");
+ builder.Append(this.errorMessage);
+ builder.Append('@');
+ builder.Append(this.commandListNum);
+ builder.Append("] {");
+ builder.Append(this.currentCommand);
+ builder.Append("} ");
+ builder.Append(this.errorMessage);
+ //ACK [50@1] {play} song doesn't exist: "10240"
+ }
+ else
+ builder.Append(OK);
+
+ return builder.ToString();
+ }
+
+ #region IEnumerable> Members
+ ///
+ /// Returns an enumerator for all KeyValuePairs in the MPD response.
+ ///
+ /// An enumerator for all KeyValuePairs in the MPD response.
+ IEnumerator> IEnumerable>.GetEnumerator()
+ {
+ return new MpdResponseEnumerator(this);
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+ ///
+ /// Returns an enumerator for all KeyValuePairs in the MPD response.
+ ///
+ /// An enumerator for all KeyValuePairs in the MPD response.
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return new MpdResponseEnumerator(this);
+ }
+
+ #endregion
+ }
+ ///
+ /// A class for enumerating over the KeyValuePairs in the response.
+ ///
+ public class MpdResponseEnumerator :IEnumerator>
+ {
+ private readonly MpdResponse response;
+ private int position = -1;
+ private KeyValuePair current;
+ ///
+ /// Creates a new MpdResponseEnumerator.
+ ///
+ /// The response to enumerate over.
+ protected internal MpdResponseEnumerator(MpdResponse response)
+ {
+ this.response = response;
+ }
+
+ #region IEnumerator> Members
+ ///
+ /// Returns the current element of the enumerator.
+ ///
+ KeyValuePair IEnumerator>.Current
+ {
+ get { return this.current; }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ void IDisposable.Dispose()
+ {
+ this.position = -1;
+ }
+
+ #endregion
+
+ #region IEnumerator Members
+ ///
+ /// Returns the current element of the enumerator.
+ ///
+ object System.Collections.IEnumerator.Current
+ {
+ get { return this.current; }
+ }
+ ///
+ /// Moves the enumerator to the next KeyValuePair in the MPD response.
+ ///
+ /// If the enumerator has any values left.
+ bool System.Collections.IEnumerator.MoveNext()
+ {
+ this.position++;
+ if (this.position < this.response.Count)
+ {
+ this.current = this.response[this.position];
+ return true;
+ }
+ else
+ return false;
+ }
+ ///
+ /// Sets the enumerator to it's initial state.
+ ///
+ void System.Collections.IEnumerator.Reset()
+ {
+ this.position = -1;
+ }
+
+ #endregion
+ }
+}
diff --git a/LibMpc/MpdStatistics.cs b/LibMpc/MpdStatistics.cs
new file mode 100644
index 0000000..e2fe9ca
--- /dev/null
+++ b/LibMpc/MpdStatistics.cs
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Libmpc
+{
+ ///
+ /// The MpdStatistics class contains statistics of the MPD file database.
+ ///
+ public class MpdStatistics
+ {
+ private readonly int artists;
+ private readonly int albums;
+ private readonly int songs;
+ private readonly int uptime;
+ private readonly int playtime;
+ private readonly int db_playtime;
+ private readonly long db_update;
+ ///
+ /// The number of artists in the MPD database.
+ ///
+ public int Artists { get { return this.artists; } }
+ ///
+ /// The number of albums in the MPD database.
+ ///
+ public int Albums { get { return this.albums; } }
+ ///
+ /// The number of songs in the MPD database.
+ ///
+ public int Songs { get { return this.songs; } }
+ ///
+ /// The time the MPD server is running in seconds.
+ ///
+ public int Uptime { get { return this.uptime; } }
+ ///
+ /// The number of seconds the MPD played so far.
+ ///
+ public int Playtime { get { return this.playtime; } }
+ ///
+ /// The total playtime of all songs in the MPD database.
+ ///
+ public int DbPlaytime { get { return this.db_playtime; } }
+ ///
+ /// The timestamp of the last MPD database update.
+ ///
+ public long DbUpdate { get { return this.db_update; } }
+ ///
+ /// Creates a new MpdStatistics object.
+ ///
+ /// The number of artists in the MPD database.
+ /// The number of albums in the MPD database.
+ /// The number of songs in the MPD database.
+ /// The time the MPD server is running in seconds.
+ /// The number of seconds the MPD played so far.
+ /// The total playtime of all songs in the MPD database.
+ /// The timestamp of the last MPD database update.
+ public MpdStatistics(
+ int artists,
+ int albums,
+ int songs,
+ int uptime,
+ int playtime,
+ int db_playtime,
+ long db_update
+ )
+ {
+ this.artists = artists;
+ this.albums = albums;
+ this.songs = songs;
+ this.uptime = uptime;
+ this.playtime = playtime;
+ this.db_playtime = db_playtime;
+ this.db_update = db_update;
+ }
+ ///
+ /// Returns a string representation of the object mainly for debugging purpuse.
+ ///
+ /// A string representation of the object.
+ public override string ToString()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ appendInt(builder, "artists", this.artists);
+ appendInt(builder, "songs", this.songs);
+ appendInt(builder, "uptime", this.uptime);
+ appendInt(builder, "playtime", this.playtime);
+ appendInt(builder, "db_playtime", this.db_playtime);
+ appendLong(builder, "db_update", this.db_update);
+
+ return builder.ToString();
+ }
+
+ private static void appendInt(StringBuilder builder, string name, int value)
+ {
+ if (value < 0)
+ return;
+
+ builder.Append(name);
+ builder.Append(": ");
+ builder.Append(value);
+ builder.AppendLine();
+ }
+
+ private static void appendLong(StringBuilder builder, string name, long value)
+ {
+ if (value < 0)
+ return;
+
+ builder.Append(name);
+ builder.Append(": ");
+ builder.Append(value);
+ builder.AppendLine();
+ }
+
+ }
+}
diff --git a/LibMpc/MpdStatus.cs b/LibMpc/MpdStatus.cs
new file mode 100644
index 0000000..52e268a
--- /dev/null
+++ b/LibMpc/MpdStatus.cs
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2008 Matthias Sessler
+ *
+ * This file is part of LibMpc.net.
+ *
+ * LibMpc.net is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * LibMpc.net is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with LibMpc.net. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Libmpc
+{
+ ///
+ /// The possible states of the MPD.
+ ///
+ public enum MpdState
+ {
+ ///
+ /// The state of the MPD could not be translated into this enumeration.
+ ///
+ Unknown,
+ ///
+ /// The MPD is playing a track.
+ ///
+ Play,
+ ///
+ /// The MPD is not playing a track.
+ ///
+ Stop,
+ ///
+ /// The playback of the MPD is currently paused.
+ ///
+ Pause
+ }
+ ///
+ /// The MpdStatus class contains all values describing the current status of the MPD.
+ ///
+ public class MpdStatus
+ {
+ private int volume;
+ private bool repeat;
+ private bool random;
+ private int playlist;
+ private int playlistLength;
+ private int xFade;
+ private MpdState state;
+ private int song;
+ private int songId;
+ private int timeElapsed;
+ private int timeTotal;
+ private int bitrate;
+ private int audioSampleRate;
+ private int audioBits;
+ private int audioChannels;
+ private int updatingDb;
+ private string error;
+ ///
+ /// The current volume of the output.
+ ///
+ public int Volume { get { return this.volume; } }
+ ///
+ /// If the playlist is repeated after finish.
+ ///
+ public bool Repeat { get { return this.repeat; } }
+ ///
+ /// If the playlist is played in random order.
+ ///
+ public bool Random { get { return this.random; } }
+ ///
+ /// The version number of the playlist.
+ ///
+ public int Playlist { get { return this.playlist; } }
+ ///
+ /// The length of the playlist.
+ ///
+ public int PlaylistLength { get { return this.playlistLength; } }
+ ///
+ /// The number of seconds crossfaded between song changes.
+ ///
+ public int XFade { get { return this.xFade; } }
+ ///
+ /// The state of the MPD.
+ ///
+ public MpdState State { get { return this.state; } }
+ ///
+ /// The index of the currently played song in the playlist.
+ ///
+ public int Song { get { return this.song; } }
+ ///
+ /// The id of the song currently played.
+ ///
+ public int SongId { get { return this.songId; } }
+ ///
+ /// The number of seconds already played of the current song.
+ ///
+ public int TimeElapsed { get { return this.timeElapsed; } }
+ ///
+ /// The length of the current song in seconds.
+ ///
+ public int TimeTotal { get { return this.timeTotal; } }
+ ///
+ /// The bitrate of the current song.
+ ///
+ public int Bitrate { get { return this.bitrate; } }
+ ///
+ /// The audio sample rate of the current song.
+ ///
+ public int AudioSampleRate { get { return this.audioSampleRate; } }
+ ///
+ /// The audio bits of the current song.
+ ///
+ public int AudioBits { get { return this.audioBits; } }
+ ///
+ /// The number of audio channels of the current song.
+ ///
+ public int AudioChannels { get { return this.audioChannels; } }
+ ///
+ /// The number of the update on the MPD database currently running.
+ ///
+ public int UpdatingDb { get { return this.updatingDb; } }
+ ///
+ /// An error message, if there is an error.
+ ///
+ public string Error { get { return this.error; } }
+ ///
+ /// Creates a new MpdStatus object.
+ ///
+ /// The current volume of the output.
+ /// If the playlist is repeated after finish.
+ /// If the playlist is played in random order.
+ /// The version number of the playlist.
+ /// The length of the playlist.
+ /// The number of seconds crossfaded between song changes.
+ /// The state of the MPD.
+ /// The index of the currently played song in the playlist.
+ /// The id of the song currently played.
+ /// The number of seconds already played of the current song.
+ /// The length of the current song in seconds.
+ /// The bitrate of the current song.
+ /// The audio sample rate of the current song.
+ /// The audio bits of the current song.
+ /// The number of audio channels of the current song.
+ /// The number of the update on the MPD database currently running.
+ /// An error message, if there is an error.
+ public MpdStatus(
+ int volume,
+ bool repeat,
+ bool random,
+ int playlist,
+ int playlistLength,
+ int xFade,
+ MpdState state,
+ int song,
+ int songId,
+ int timeElapsed,
+ int timeTotal,
+ int bitrate,
+ int audioSampleRate,
+ int audioBits,
+ int audioChannels,
+ int updatingDb,
+ string error
+ )
+ {
+ this.volume = volume;
+ this.repeat = repeat;
+ this.random = random;
+ this.playlist = playlist;
+ this.playlistLength = playlistLength;
+ this.xFade = xFade;
+ this.state = state;
+ this.song = song;
+ this.songId = songId;
+ this.timeElapsed = timeElapsed;
+ this.timeTotal = timeTotal;
+ this.bitrate = bitrate;
+ this.audioSampleRate = audioSampleRate;
+ this.audioBits = audioBits;
+ this.audioChannels = audioChannels;
+ this.updatingDb = updatingDb;
+ this.error = error;
+ }
+ ///
+ /// Returns a string representation of the object maily for debugging purpuses.
+ ///
+ /// A string representation of the object.
+ public override string ToString()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ appendInt(builder, "volume", this.volume);
+ appendBool(builder, "repeat", this.repeat);
+ appendBool(builder, "random", this.random);
+ appendInt(builder, "playlist", this.playlist);
+ appendInt(builder, "playlistlength", this.playlistLength);
+ appendInt(builder, "xfade", this.xFade);
+ switch (this.state)
+ {
+ case MpdState.Play:
+ builder.AppendLine("state: play");
+ break;
+ case MpdState.Pause:
+ builder.AppendLine("state: pause");
+ break;
+ case MpdState.Stop:
+ builder.AppendLine("state: stop");
+ break;
+ }
+ appendInt(builder, "song", this.song);
+ appendInt(builder, "songid", this.songId);
+ if ((this.timeElapsed >= 0) || (this.timeTotal >= 0))
+ {
+ builder.Append("time: ");
+ builder.Append(this.timeElapsed);
+ builder.Append(":");
+ builder.Append(this.timeTotal);
+ builder.AppendLine();
+ }
+ appendInt(builder, "bitrate", this.bitrate);
+ if ((this.audioSampleRate >= 0) || (this.audioBits >= 0) || (this.audioChannels >= 0))
+ {
+ builder.Append("audio: ");
+ builder.Append(this.audioSampleRate);
+ builder.Append(":");
+ builder.Append(this.audioBits);
+ builder.Append(":");
+ builder.Append(this.audioChannels);
+ builder.AppendLine();
+ }
+ appendInt(builder, "updating_db", this.updatingDb);
+ if (this.error != null)
+ {
+ builder.Append("error: ");
+ builder.AppendLine(this.error);
+ }
+
+ return builder.ToString();
+ }
+
+ private static void appendInt(StringBuilder builder, string name, int value)
+ {
+ if (value < 0)
+ return;
+
+ builder.Append(name);
+ builder.Append(": ");
+ builder.Append(value);
+ builder.AppendLine();
+ }
+
+ private static void appendBool(StringBuilder builder, string name, bool value)
+ {
+ builder.Append(name);
+ builder.Append(": ");
+ builder.Append(value ? '1' : '0');
+ builder.AppendLine();
+ }
+ }
+}