From e923d0698448a59b5e9e22a5bf469772761ed035 Mon Sep 17 00:00:00 2001 From: glucaci Date: Wed, 30 Nov 2016 12:59:26 +0100 Subject: [PATCH] Mpc use now EventHandler --- LibMpc/Mpc.cs | 2624 +++++++++++++++++++-------------------- LibMpc/MpcConnection.cs | 30 +- 2 files changed, 1318 insertions(+), 1336 deletions(-) diff --git a/LibMpc/Mpc.cs b/LibMpc/Mpc.cs index c241f69..5791c6c 100644 --- a/LibMpc/Mpc.cs +++ b/LibMpc/Mpc.cs @@ -1,1317 +1,1307 @@ -using System; -using System.Collections.Generic; -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; - } - } - } -} +using System; +using System.Collections.Generic; +using System.Net; +using System.Text.RegularExpressions; + +namespace Libmpc +{ + public interface IMpc + { + bool IsConnected { get; } + bool Connect(); + + event EventHandler Connected; + event EventHandler Disconnected; + + MpdOutput[] Outputs(); + MpdStatistics Stats(); + } + + /// + /// 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 : IMpc + { + 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 MpcConnection _connection; + private IPEndPoint _server; + + public Mpc(IPEndPoint server) + { + _server = server; + } + + /// + /// Is fired when a connection to a MPD server is established. + /// + public event EventHandler Connected + { + add { ConnectedRelayEvent += value; } + remove { ConnectedRelayEvent -= value; } + } + + private event EventHandler ConnectedRelayEvent; + + /// + /// Is fired when the connection to the MPD server is closed. + /// + public event EventHandler Disconnected + { + add { DisconnectedRelayEvent += value; } + remove { DisconnectedRelayEvent -= value; } + } + + private event EventHandler DisconnectedRelayEvent; + + /// + /// Connection status to MPD Server. + /// + public bool IsConnected => _connection?.IsConnected ?? false; + + public bool Connect() + { + if (_connection == null) + { + _connection = new MpcConnection(_server); + _connection.Connected += OnConnected; + _connection.Disconnected += OnDisconnected; + } + + if (!_connection.IsConnected) + { + _connection.Connect(); + } + + return _connection.IsConnected; + } + + private void OnConnected(object sender, EventArgs e) + { + ConnectedRelayEvent?.Invoke(this, e); + } + + private void OnDisconnected(object sender, EventArgs e) + { + DisconnectedRelayEvent?.Invoke(this, e); + } + + #region Admin Commands + /// + /// Disables an MPD output. + /// + /// The id of the output. + /// If the action was successful. + public bool DisableOutput(int id) + { + return !_connection.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 !_connection.Exec("enableoutput", new string[] { id.ToString() }).IsError; + } + /// + /// Lists all outputs of the MPD. + /// + /// The list of all MPD outputs. + public MpdOutput[] Outputs() + { + MpdResponse response = _connection.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 = _connection.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 = _connection.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 = _connection.Exec("find", new string[] { 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 = _connection.Exec("list", new string[] { 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 = _connection.Exec("list", new string[] { toTag(resultTag), 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 = _connection.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 = _connection.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 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 = _connection.Exec("lsinfo"); + else + response = _connection.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 = _connection.Exec("search", new string[] { 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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.Exec("save", new string[] { name }); + + if (response.IsError) + throw new MpdResponseException(response.ErrorCode, response.ErrorMessage); + } + /// + /// Shuffles the current playlist. + /// + public void Shuffle() + { + MpdResponse response = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.Exec("playlistfind", new string[] { 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 = _connection.Exec("playlistsearch", new string[] { 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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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 = _connection.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() + { + _connection.Exec("clearerror"); + } + /// + /// Returns which commands the current user has access to. + /// + /// The commands the current user has access to. + public List Commands() + { + MpdResponse response = _connection.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 = _connection.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 _connection.Exec("password", new string[] { password }).IsError; + } + /// + /// Sends a ping command to the server and waits for the response. + /// + public void Ping() + { + _connection.Exec("ping"); + } + /// + /// Requests the current statistics from the MPD, + /// + /// The current statistics fromt the MPD. + public MpdStatistics Stats() + { + MpdResponse response = _connection.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 = _connection.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 index 88849de..65c2007 100644 --- a/LibMpc/MpcConnection.cs +++ b/LibMpc/MpcConnection.cs @@ -19,11 +19,11 @@ namespace Libmpc /// /// Is fired when a connection to a MPD server is established. /// - public event EventHandler OnConnected; + public event EventHandler Connected; /// /// Is fired when the connection to the MPD server is closed. /// - public event EventHandler OnDisconnected; + public event EventHandler Disconnected; private static readonly string FIRST_LINE_PREFIX = "OK MPD "; @@ -44,7 +44,7 @@ namespace Libmpc /// /// If the connection to the MPD is connected. /// - public bool Connected { get { return (_tcpClient != null) && _tcpClient.Connected; } } + public bool IsConnected { get { return (_tcpClient != null) && _tcpClient.Connected; } } /// /// The version of the MPD. /// @@ -67,7 +67,7 @@ namespace Libmpc /// The IPEndPoint of the MPD server. public MpcConnection(IPEndPoint server) { - Connect(server); + Server = server; } /// /// The IPEndPoint of the MPD server. @@ -78,7 +78,7 @@ namespace Libmpc get { return _ipEndPoint; } set { - if (Connected) + if (IsConnected) throw new AlreadyConnectedException(); _ipEndPoint = value; @@ -86,15 +86,7 @@ namespace Libmpc ClearConnectionFields(); } } - /// - /// Connects to a MPD server. - /// - /// The IPEndPoint of the server. - public void Connect(IPEndPoint server) - { - Server = server; - Connect(); - } + /// /// Connects to the MPD server who's IPEndPoint was set in the Server property. /// @@ -104,7 +96,7 @@ namespace Libmpc if (_ipEndPoint == null) throw new InvalidOperationException("Server IPEndPoint not set."); - if (Connected) + if (IsConnected) throw new AlreadyConnectedException(); @@ -131,7 +123,7 @@ namespace Libmpc ReadResponse(); - OnConnected?.Invoke(this, EventArgs.Empty); + Connected?.Invoke(this, EventArgs.Empty); } /// /// Disconnects from the current MPD server. @@ -145,7 +137,7 @@ namespace Libmpc ClearConnectionFields(); - OnDisconnected?.Invoke(this, EventArgs.Empty); + Disconnected?.Invoke(this, EventArgs.Empty); } /// /// Executes a simple command without arguments on the MPD server and returns the response. @@ -175,7 +167,7 @@ namespace Libmpc { try { Disconnect(); } catch (Exception) { } - throw; + return null; // TODO: Create Null Object for MpdResponse } } /// @@ -228,7 +220,7 @@ namespace Libmpc private void CheckConnected() { - if (!Connected) + if (!IsConnected) { if (_autoConnect) Connect();