/* * 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; } } } }