From f7ee5b70bb9cb0a63e1df72d42a34274a7ed0a0a Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 22 Feb 2010 23:45:41 +0100 Subject: [PATCH] docs: Add MPD command descriptions from musicpd.org --- mopidy/mpd/handler.py | 718 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 708 insertions(+), 10 deletions(-) diff --git a/mopidy/mpd/handler.py b/mopidy/mpd/handler.py index 17d11aef..e75fb00f 100644 --- a/mopidy/mpd/handler.py +++ b/mopidy/mpd/handler.py @@ -27,9 +27,8 @@ def handle_pattern(pattern): raise ValueError(u'Tried to redefine handler for %s with %s' % ( pattern, func)) _request_handlers[pattern] = func - if func.__doc__ is None: - func.__doc__ = '' - func.__doc__ += '\n\n- **Pattern:** ``%s``' % pattern + func.__doc__ = ' - **Pattern:** ``%s``\n\n%s' % ( + pattern, func.__doc__ or '') return func return decorator @@ -88,44 +87,102 @@ class MpdHandler(object): @handle_pattern(r'^ack$') def _ack(self): """ - Always returns an 'ACK' and not 'OK'. - - Not a part of the MPD protocol. + Always returns an 'ACK'. Not a part of the MPD protocol. """ raise MpdNotImplemented @handle_pattern(r'^add "(?P[^"]*)"$') def _add(self, uri): + """ + *musicpd.org, current playlist section:* + + ``add {URI}`` + + Adds the file ``URI`` to the playlist (directories add recursively). + ``URI`` can also be a single file. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^addid "(?P[^"]*)"( (?P\d+))*$') - def _add(self, uri, songpos=None): + def _addid(self, uri, songpos=None): + """ + *musicpd.org, current playlist section:* + + ``addid {URI} [POSITION]`` + + Adds a song to the playlist (non-recursive) and returns the song id. + + ``URI`` is always a single file or URL. For example:: + + addid "foo.mp3" + Id: 999 + OK + """ raise MpdNotImplemented # TODO @handle_pattern(r'^clear$') def _clear(self): + """ + *musicpd.org, current playlist section:* + + ``clear`` + + Clears the current playlist. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^clearerror$') def _clearerror(self): + """ + *musicpd.org, status section:* + + ``clearerror`` + + Clears the current error message in status (this is also + accomplished by any command that starts playback). + """ raise MpdNotImplemented # TODO @handle_pattern(r'^close$') def _close(self): + """ + *musicpd.org, connection section:* + + ``close`` + + Closes the connection to MPD. + """ self.session.do_close() @handle_pattern(r'^command_list_begin$') def _command_list_begin(self): + """ + *musicpd.org, command list section:* + + To facilitate faster adding of files etc. you can pass a list of + commands all at once using a command list. The command list begins + with ``command_list_begin`` or ``command_list_ok_begin`` and ends + with ``command_list_end``. + + It does not execute any commands until the list has ended. The + return value is whatever the return for a list of commands is. On + success for all commands, ``OK`` is returned. If a command fails, + no more commands are executed and the appropriate ``ACK`` error is + returned. If ``command_list_ok_begin`` is used, ``list_OK`` is + returned for each successful command executed in the command list. + """ self.command_list = [] self.command_list_ok = False @handle_pattern(r'^command_list_ok_begin$') def _command_list_ok_begin(self): + """See :meth:`_command_list_begin`.""" self.command_list = [] self.command_list_ok = True @handle_pattern(r'^command_list_end$') def _command_list_end(self): + """See :meth:`_command_list_begin`.""" (command_list, self.command_list) = (self.command_list, False) (command_list_ok, self.command_list_ok) = (self.command_list_ok, False) result = [] @@ -141,10 +198,26 @@ class MpdHandler(object): @handle_pattern(r'^commands$') def _commands(self): + """ + *musicpd.org, reflection section:* + + ``commands`` + + Shows which commands the current user has access to. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^consume "(?P[01])"$') def _consume(self, state): + """ + *musicpd.org, playback section:* + + ``consume {STATE}`` + + Sets consume state to ``STATE``, ``STATE`` should be 0 or + 1. When consume is activated, each song played is removed from + playlist. + """ state = int(state) if state: raise MpdNotImplemented # TODO @@ -153,30 +226,82 @@ class MpdHandler(object): @handle_pattern(r'^count "(?P[^"]+)" "(?P[^"]+)"$') def _count(self, tag, needle): + """ + *musicpd.org, music database section:* + + ``count {TAG} {NEEDLE}`` + + Counts the number of songs and their total playtime in the db + matching ``TAG`` exactly. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^crossfade "(?P\d+)"$') def _crossfade(self, seconds): + """ + *musicpd.org, playback section:* + + ``crossfade {SECONDS}`` + + Sets crossfading between songs. + """ seconds = int(seconds) raise MpdNotImplemented # TODO @handle_pattern(r'^currentsong$') def _currentsong(self): + """ + *musicpd.org, status section:* + + ``currentsong`` + + Displays the song info of the current song (same song that is + identified in status). + """ if self.backend.playback.current_track is not None: return self.backend.playback.current_track.mpd_format( position=self.backend.playback.playlist_position) @handle_pattern(r'^decoders$') def _decoders(self): + """ + *musicpd.org, reflection section:* + + ``decoders`` + + Print a list of decoder plugins, followed by their supported + suffixes and MIME types. Example response:: + + plugin: mad + suffix: mp3 + suffix: mp2 + mime_type: audio/mpeg + plugin: mpcdec + suffix: mpc + """ raise MpdNotImplemented # TODO @handle_pattern(r'^delete "(?P\d+)"$') @handle_pattern(r'^delete "(?P\d+):(?P\d+)*"$') def _delete(self, songpos=None, start=None, end=None): + """ + *musicpd.org, current playlist section:* + + ``delete [{POS} | {START:END}]`` + + Deletes a song from the playlist. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^deleteid "(?P\d+)"$') def _deleteid(self, songid): + """ + *musicpd.org, current playlist section:* + + ``deleteid {SONGID}`` + + Deletes the song ``SONGID`` from the playlist + """ songid = int(songid) try: track = self.backend.current_playlist.get_by_id(songid) @@ -186,22 +311,54 @@ class MpdHandler(object): @handle_pattern(r'^disableoutput "(?P\d+)"$') def _disableoutput(self, outputid): + """ + *musicpd.org, audio output section:* + + ``disableoutput`` + + Turns an output off. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^$') def _empty(self): + """The original MPD server returns ``OK`` on an empty request.``""" pass @handle_pattern(r'^enableoutput "(?P\d+)"$') def _enableoutput(self, outputid): + """ + *musicpd.org, audio output section:* + + ``enableoutput`` + + Turns an output on. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^find "(?P(album|artist|title))" "(?P[^"]+)"$') def _find(self, type, what): + """ + *musicpd.org, music database section:* + + ``find {TYPE} {WHAT}`` + + Finds songs in the db that are exactly ``WHAT``. ``TYPE`` should be + ``album``, ``artist``, or ``title``. ``WHAT`` is what to find. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^findadd "(?P(album|artist|title))" "(?P[^"]+)"$') def _findadd(self, type, what): + """ + *musicpd.org, music database section:* + + ``findadd {TYPE} {WHAT}`` + + Finds songs in the db that are exactly ``WHAT`` and adds them to + current playlist. ``TYPE`` can be any tag supported by MPD. + ``WHAT`` is what to find. + """ result = self._find(type, what) # TODO Add result to current playlist #return result @@ -209,40 +366,139 @@ class MpdHandler(object): @handle_pattern(r'^idle$') @handle_pattern(r'^idle (?P.+)$') def _idle(self, subsystems=None): + """ + *musicpd.org, status section:* + + ``idle [SUBSYSTEMS...]`` + + Waits until there is a noteworthy change in one or more of MPD's + subsystems. As soon as there is one, it lists all changed systems + in a line in the format ``changed: SUBSYSTEM``, where ``SUBSYSTEM`` + is one of the following: + + - ``database``: the song database has been modified after update. + - ``update``: a database update has started or finished. If the + database was modified during the update, the database event is + also emitted. + - ``stored_playlist``: a stored playlist has been modified, + renamed, created or deleted + - ``playlist``: the current playlist has been modified + - ``player``: the player has been started, stopped or seeked + - ``mixer``: the volume has been changed + - ``output``: an audio output has been enabled or disabled + - ``options``: options like repeat, random, crossfade, replay gain + + While a client is waiting for idle results, the server disables + timeouts, allowing a client to wait for events as long as MPD runs. + The idle command can be canceled by sending the command ``noidle`` + (no other commands are allowed). MPD will then leave idle mode and + print results immediately; might be empty at this time. + + If the optional ``SUBSYSTEMS`` argument is used, MPD will only send + notifications when something changed in one of the specified + subsystems. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^kill$') def _kill(self): + """ + *musicpd.org, connection section:* + + ``kill`` + + Kills MPD. + """ self.session.do_kill() @handle_pattern(r'^list "(?Partist)"$') @handle_pattern(r'^list "(?Palbum)"( "(?P[^"]+)")*$') def _list(self, type, artist=None): + """ + *musicpd.org, music database section:* + + ``list {TYPE} [ARTIST]`` + + Lists all tags of the specified type. ``TYPE`` should be ``album`` + or artist. + + ``ARTIST`` is an optional parameter when type is ``album``, this + specifies to list albums by an artist. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^listall "(?P[^"]+)"') def _listall(self, uri): + """ + *musicpd.org, music database section:* + + ``listall [URI]`` + + Lists all songs and directories in ``URI``. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^listallinfo "(?P[^"]+)"') def _listallinfo(self, uri): + """ + *musicpd.org, music database section:* + + ``listallinfo [URI]`` + + Same as ``listall``, except it also returns metadata info in the + same format as ``lsinfo``. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^listplaylist "(?P[^"]+)"$') def _listplaylist(self, name): + """ + *musicpd.org, stored playlists section:* + + ``listplaylist {NAME}`` + + Lists the files in the playlist ``NAME.m3u``. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^listplaylistinfo "(?P[^"]+)"$') def _listplaylistinfo(self, name): + """ + *musicpd.org, stored playlists section:* + + ``listplaylistinfo {NAME}`` + + Lists songs in the playlist ``NAME.m3u``. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^listplaylists$') def _listplaylists(self): + """ + *musicpd.org, stored playlists section:* + + ``listplaylists`` + + Prints a list of the playlist directory. + + After each playlist name the server sends its last modification + time as attribute ``Last-Modified`` in ISO 8601 format. To avoid + problems due to clock differences between clients and the server, + clients should not compare this value with their local clock. + """ + # TODO Add Last-Modified attribute to output return [u'playlist: %s' % p.name for p in self.backend.stored_playlists.playlists] @handle_pattern(r'^load "(?P[^"]+)"$') def _load(self, name): + """ + *musicpd.org, stored playlists section:* + + ``load {NAME}`` + + Loads the playlist ``NAME.m3u`` from the playlist directory. + """ matches = self.backend.stored_playlists.search(name) if matches: self.backend.current_playlist.load(matches[0]) @@ -251,6 +507,17 @@ class MpdHandler(object): @handle_pattern(r'^lsinfo$') @handle_pattern(r'^lsinfo "(?P[^"]*)"$') def _lsinfo(self, uri=None): + """ + *musicpd.org, music database section:* + + ``lsinfo [URI]`` + + Lists the contents of the directory ``URI``. + + When listing the root directory, this currently returns the list of + stored playlists. This behavior is deprecated; use + ``listplaylists`` instead. + """ if uri == u'/' or uri is None: return self._listplaylists() raise MpdNotImplemented # TODO @@ -258,22 +525,65 @@ class MpdHandler(object): @handle_pattern(r'^move "(?P\d+)" "(?P\d+)"$') @handle_pattern(r'^move "(?P\d+):(?P\d+)*" "(?P\d+)"$') def _move(self, songpos=None, start=None, end=None, to=None): + """ + *musicpd.org, current playlist section:* + + ``move [{FROM} | {START:END}] {TO}`` + + Moves the song at ``FROM`` or range of songs at ``START:END`` to + ``TO`` in the playlist. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^moveid "(?P\d+)" "(?P\d+)"$') def _moveid(self, songid, to): + """ + *musicpd.org, current playlist section:* + + ``moveid {FROM} {TO}`` + + Moves the song with ``FROM`` (songid) to ``TO` (playlist index) in + the playlist. If ``TO`` is negative, it is relative to the current + song in the playlist (if there is one). + """ raise MpdNotImplemented # TODO @handle_pattern(r'^next$') def _next(self): + """ + *musicpd.org, playback section:* + + ``next`` + + Plays next song in the playlist. + """ return self.backend.playback.next() + @handle_pattern(r'^noidle$') + def _noidle(self): + """See :meth:`_idle`.""" + raise MpdNotImplemented # TODO + @handle_pattern(r'^notcommands$') def _notcommands(self): + """ + *musicpd.org, reflection section:* + + ``notcommands`` + + Shows which commands the current user does not have access to. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^outputs$') def _outputs(self): + """ + *musicpd.org, audio output section:* + + ``outputs`` + + Shows information about all outputs. + """ return [ ('outputid', 0), ('outputname', self.backend.__class__.__name__), @@ -282,10 +592,25 @@ class MpdHandler(object): @handle_pattern(r'^password "(?P[^"]+)"$') def _password(self, password): + """ + *musicpd.org, connection section:* + + ``password {PASSWORD}`` + + This is used for authentication with the server. ``PASSWORD`` is + simply the plaintext password. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^pause "(?P[01])"$') def _pause(self, state): + """ + *musicpd.org, playback section:* + + ``pause {PAUSE}`` + + Toggles pause/resumes playing, ``PAUSE`` is 0 or 1. + """ if int(state): self.backend.playback.pause() else: @@ -293,14 +618,32 @@ class MpdHandler(object): @handle_pattern(r'^ping$') def _ping(self): + """ + *musicpd.org, connection section:* + + ``ping`` + + Does nothing but return ``OK``. + """ pass @handle_pattern(r'^play$') def _play(self): + """ + The original MPD server resumes from the paused state on ``play`` + without arguments. + """ return self.backend.playback.play() @handle_pattern(r'^play "(?P\d+)"$') def _playpos(self, songpos): + """ + *musicpd.org, playback section:* + + ``play [SONGPOS]`` + + Begins playing the playlist at song number ``SONGPOS``. + """ songpos = int(songpos) try: track = self.backend.current_playlist.playlist.tracks[songpos] @@ -310,6 +653,13 @@ class MpdHandler(object): @handle_pattern(r'^playid "(?P\d+)"$') def _playid(self, songid): + """ + *musicpd.org, playback section:* + + ``playid [SONGID]`` + + Begins playing the playlist at song ``SONGID``. + """ songid = int(songid) try: track = self.backend.current_playlist.get_by_id(songid) @@ -319,32 +669,91 @@ class MpdHandler(object): @handle_pattern(r'^playlist$') def _playlist(self): + """ + *musicpd.org, current playlist section:* + + ``playlist`` + + Displays the current playlist. + + .. note:: + + Do not use this, instead use ``playlistinfo``. + """ return self._playlistinfo() @handle_pattern(r'^playlistadd "(?P[^"]+)" "(?P[^"]+)"$') def _playlistadd(self, name, uri): + """ + *musicpd.org, stored playlists section:* + + ``playlistadd {NAME} {URI}`` + + Adds ``URI`` to the playlist ``NAME.m3u``. + + ``NAME.m3u`` will be created if it does not exist. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^playlistclear "(?P[^"]+)"$') def _playlistclear(self, name): + """ + *musicpd.org, stored playlists section:* + + ``playlistclear {NAME}`` + + Clears the playlist ``NAME.m3u``. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^playlistdelete "(?P[^"]+)" "(?P\d+)"$') def _playlistdelete(self, name, songpos): + """ + *musicpd.org, stored playlists section:* + + ``playlistdelete {NAME} {SONGPOS}`` + + Deletes ``SONGPOS`` from the playlist ``NAME.m3u``. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^playlistfind "(?P[^"]+)" "(?P[^"]+)"$') def _playlistfind(self, tag, needle): + """ + *musicpd.org, current playlist section:* + + ``playlistfind {TAG} {NEEDLE}`` + + Finds songs in the current playlist with strict matching. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^playlistid( "(?P\S+)")*$') def _playlistid(self, songid=None): + """ + *musicpd.org, current playlist section:* + + ``playlistid {SONGID}`` + + Displays a list of songs in the playlist. ``SONGID`` is optional + and specifies a single song to display info for. + """ + # TODO Limit selection to songid return self.backend.current_playlist.playlist.mpd_format() @handle_pattern(r'^playlistinfo$') @handle_pattern(r'^playlistinfo "(?P\d+)"$') @handle_pattern(r'^playlistinfo "(?P\d+):(?P\d+)*"$') def _playlistinfo(self, songpos=None, start=None, end=None): + """ + *musicpd.org, current playlist section:* + + ``playlistinfo [[SONGPOS] | [START:END]]`` + + Displays a list of all songs in the playlist, or if the optional + argument is given, displays information only for the song + ``SONGPOS`` or the range of songs ``START:END``. + """ if songpos is not None: songpos = int(songpos) return self.backend.current_playlist.playlist.mpd_format( @@ -358,32 +767,91 @@ class MpdHandler(object): return self.backend.current_playlist.playlist.mpd_format(start, end) @handle_pattern(r'^playlistmove "(?P[^"]+)" "(?P\d+)" "(?P\d+)"$') - def _playlistdelete(self, name, songid, songpos): + def _playlistmove(self, name, songid, songpos): + """ + *musicpd.org, stored playlists section:* + + ``playlistmove {NAME} {SONGID} {SONGPOS}`` + + Moves ``SONGID`` in the playlist ``NAME.m3u`` to the position + ``SONGPOS``. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^playlistsearch "(?P[^"]+)" "(?P[^"]+)"$') def _playlistsearch(self, tag, needle): + """ + *musicpd.org, current playlist section:* + + ``playlistsearch {TAG} {NEEDLE}`` + + Searches case-sensitively for partial matches in the current + playlist. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^plchanges "(?P\d+)"$') def _plchanges(self, version): + """ + *musicpd.org, current playlist section:* + + ``plchanges {VERSION}`` + + Displays changed songs currently in the playlist since ``VERSION``. + + To detect songs that were deleted at the end of the playlist, use + ``playlistlength`` returned by status command. + """ if int(version) < self.backend.current_playlist.version: return self.backend.current_playlist.playlist.mpd_format() @handle_pattern(r'^plchangesposid "(?P\d+)"$') def _plchangesposid(self, version): + """ + *musicpd.org, current playlist section:* + + ``plchangesposid {VERSION}`` + + Displays changed songs currently in the playlist since ``VERSION``. + This function only returns the position and the id of the changed + song, not the complete metadata. This is more bandwidth efficient. + + To detect songs that were deleted at the end of the playlist, use + ``playlistlength`` returned by status command. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^previous$') def _previous(self): + """ + *musicpd.org, playback section:* + + ``previous`` + + Plays previous song in the playlist. + """ return self.backend.playback.previous() @handle_pattern(r'^rename "(?P[^"]+)" "(?P[^"]+)"$') def _rename(self, old_name, new_name): + """ + *musicpd.org, stored playlists section:* + + ``rename {NAME} {NEW_NAME}`` + + Renames the playlist ``NAME.m3u`` to ``NEW_NAME.m3u``. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^random "(?P[01])"$') def _random(self, state): + """ + *musicpd.org, playback section:* + + ``random {STATE}`` + + Sets random state to ``STATE``, ``STATE`` should be 0 or 1. + """ state = int(state) if state: raise MpdNotImplemented # TODO @@ -392,6 +860,13 @@ class MpdHandler(object): @handle_pattern(r'^repeat "(?P[01])"$') def _repeat(self, state): + """ + *musicpd.org, playback section:* + + ``repeat {STATE}`` + + Sets repeat state to ``STATE``, ``STATE`` should be 0 or 1. + """ state = int(state) if state: raise MpdNotImplemented # TODO @@ -400,40 +875,112 @@ class MpdHandler(object): @handle_pattern(r'^replay_gain_mode "(?P(off|track|album))"$') def _replay_gain_mode(self, mode): + """ + *musicpd.org, playback section:* + + ``replay_gain_mode {MODE}`` + + Sets the replay gain mode. One of ``off``, ``track``, ``album``. + + Changing the mode during playback may take several seconds, because + the new settings does not affect the buffered data. + + This command triggers the options idle event. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^replay_gain_status$') def _replay_gain_status(self): + """ + *musicpd.org, playback section:* + + ``replay_gain_status`` + + Prints replay gain options. Currently, only the variable + ``replay_gain_mode`` is returned. + """ return u'off' # TODO @handle_pattern(r'^rescan( "(?P[^"]+)")*$') - def _update(self, uri=None): + def _rescan(self, uri=None): + """ + *musicpd.org, music database section:* + + ``rescan [URI]`` + + Same as ``update``, but also rescans unmodified files. + """ return self._update(uri, rescan_unmodified_files=True) @handle_pattern(r'^rm "(?P[^"]+)"$') def _rm(self, name): + """ + *musicpd.org, stored playlists section:* + + ``rm {NAME}`` + + Removes the playlist ``NAME.m3u`` from the playlist directory. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^save "(?P[^"]+)"$') def _save(self, name): + """ + *musicpd.org, stored playlists section:* + + ``save {NAME}`` + + Saves the current playlist to ``NAME.m3u`` in the playlist + directory. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^search "(?P(album|artist|filename|title))" "(?P[^"]+)"$') def _search(self, type, what): + """ + *musicpd.org, music database section:* + + ``search {TYPE} {WHAT}`` + + Searches for any song that contains ``WHAT``. ``TYPE`` can be + ``title``, ``artist``, ``album`` or ``filename``. Search is not + case sensitive. + """ return self.backend.library.search(type, what).mpd_format( search_result=True) @handle_pattern(r'^seek "(?P\d+)" "(?P\d+)"$') def _seek(self, songpos, seconds): + """ + *musicpd.org, playback section:* + + ``seek {SONGPOS} {TIME}`` + + Seeks to the position ``TIME`` (in seconds) of entry ``SONGPOS`` in + the playlist. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^seekid "(?P\d+)" "(?P\d+)"$') def _seekid(self, songid, seconds): + """ + *musicpd.org, playback section:* + + ``seekid {SONGID} {TIME}`` + + Seeks to the position ``TIME`` (in seconds) of song ``SONGID``. + """ raise MpdNotImplemented # TODO - @handle_pattern(r'^setvol (?P[-+]*\d+)$') @handle_pattern(r'^setvol "(?P[-+]*\d+)"$') def _setvol(self, volume): + """ + *musicpd.org, playback section:* + + ``setvol {VOL}`` + + Sets volume to ``VOL``, the range of volume is 0-100. + """ volume = int(volume) if volume < 0: volume = 0 @@ -444,10 +991,27 @@ class MpdHandler(object): @handle_pattern(r'^shuffle$') @handle_pattern(r'^shuffle "(?P\d+):(?P\d+)*"$') def _shuffle(self, start=None, end=None): + """ + *musicpd.org, current playlist section:* + + ``shuffle [START:END]`` + + Shuffles the current playlist. ``START:END`` is optional and + specifies a range of songs. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^single "(?P[01])"$') def _single(self, state): + """ + *musicpd.org, playback section:* + + ``single {STATE}`` + + Sets single state to ``STATE``, ``STATE`` should be 0 or 1. When + single is activated, playback is stopped after current song, or + song is repeated if the ``repeat`` mode is enabled. + """ state = int(state) if state: raise MpdNotImplemented # TODO @@ -456,6 +1020,20 @@ class MpdHandler(object): @handle_pattern(r'^stats$') def _stats(self): + """ + *musicpd.org, status section:* + + ``stats`` + + Displays statistics. + + - ``artists``: number of artists + - ``songs``: number of albums + - ``uptime``: daemon uptime in seconds + - ``db_playtime``: sum of all song times in the db + - ``db_update``: last db update in UNIX time + - ``playtime``: time length of music played + """ return { 'artists': 0, # TODO 'albums': 0, # TODO @@ -468,10 +1046,47 @@ class MpdHandler(object): @handle_pattern(r'^stop$') def _stop(self): + """ + *musicpd.org, playback section:* + + ``stop`` + + Stops playing. + """ self.backend.playback.stop() @handle_pattern(r'^status$') def _status(self): + """ + *musicpd.org, status section:* + + ``status`` + + Reports the current status of the player and the volume level. + + - ``volume``: 0-100 + - ``repeat``: 0 or 1 + - ``single``: 0 or 1 + - ``consume``: 0 or 1 + - ``playlist``: 31-bit unsigned integer, the playlist version + number + - ``playlistlength``: integer, the length of the playlist + - ``state``: play, stop, or pause + - ``song``: playlist song number of the current song stopped on or + playing + - ``songid``: playlist songid of the current song stopped on or + playing + - ``nextsong``: playlist song number of the next song to be played + - ``nextsongid``: playlist songid of the next song to be played + - ``time``: total time elapsed (of current playing/paused song) + - ``elapsed``: Total time elapsed within the current song, but with + higher resolution. + - ``bitrate``: instantaneous bitrate in kbps + - ``xfade``: crossfade in seconds + - ``audio``: sampleRate``:bits``:channels + - ``updatings_db``: job id + - ``error``: if there is an error, returns message here + """ result = [ ('volume', self._status_volume()), ('repeat', self._status_repeat()), @@ -489,6 +1104,7 @@ class MpdHandler(object): if self.backend.playback.state in ( self.backend.playback.PLAYING, self.backend.playback.PAUSED): result.append(('time', self._status_time())) + # TODO Add 'elapsed' here when moving to MPD 0.16.0 result.append(('bitrate', self._status_bitrate())) return result @@ -566,40 +1182,122 @@ class MpdHandler(object): @handle_pattern(r'^sticker delete "(?P[^"]+)" "(?P[^"]+)"( "(?P[^"]+)")*$') def _sticker_delete(self, type, uri, name=None): + """ + *musicpd.org, sticker section:* + + ``sticker delete {TYPE} {URI} [NAME]`` + + Deletes a sticker value from the specified object. If you do not + specify a sticker name, all sticker values are deleted. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^sticker find "(?P[^"]+)" "(?P[^"]+)" "(?P[^"]+)"$') def _sticker_find(self, type, uri, name): + """ + *musicpd.org, sticker section:* + + ``sticker find {TYPE} {URI} {NAME}`` + + Searches the sticker database for stickers with the specified name, + below the specified directory (``URI``). For each matching song, it + prints the ``URI`` and that one sticker's value. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^sticker get "(?P[^"]+)" "(?P[^"]+)" "(?P[^"]+)"$') def _sticker_get(self, type, uri, name): + """ + *musicpd.org, sticker section:* + + ``sticker get {TYPE} {URI} {NAME}`` + + Reads a sticker value for the specified object. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^sticker list "(?P[^"]+)" "(?P[^"]+)"$') def _sticker_list(self, type, uri): + """ + *musicpd.org, sticker section:* + + ``sticker list {TYPE} {URI}`` + + Lists the stickers for the specified object. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^sticker set "(?P[^"]+)" "(?P[^"]+)" "(?P[^"]+)" "(?P[^"]+)"$') def _sticker_set(self, type, uri, name, value): + """ + *musicpd.org, sticker section:* + + ``sticker set {TYPE} {URI} {NAME} {VALUE}`` + + Adds a sticker value to the specified object. If a sticker item + with that name already exists, it is replaced. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^swap "(?P\d+)" "(?P\d+)"$') def _swap(self, songpos1, songpos2): + """ + *musicpd.org, current playlist section:* + + ``swap {SONG1} {SONG2}`` + + Swaps the positions of ``SONG1`` and ``SONG2``. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^swapid "(?P\d+)" "(?P\d+)"$') def _swapid(self, songid1, songid2): + """ + *musicpd.org, current playlist section:* + + ``swapid {SONG1} {SONG2}`` + + Swaps the positions of ``SONG1`` and ``SONG2`` (both song ids). + """ raise MpdNotImplemented # TODO @handle_pattern(r'^tagtypes$') def _tagtypes(self): + """ + *musicpd.org, reflection section:* + + ``tagtypes`` + + Shows a list of available song metadata. + """ raise MpdNotImplemented # TODO @handle_pattern(r'^update( "(?P[^"]+)")*$') def _update(self, uri=None, rescan_unmodified_files=False): + """ + *musicpd.org, music database section:* + + ``update [URI]`` + + Updates the music database: find new files, remove deleted files, + update modified files. + + ``URI`` is a particular directory or song/file to update. If you do + not specify it, everything is updated. + + Prints ``updating_db: JOBID`` where ``JOBID`` is a positive number + identifying the update job. You can read the current job id in the + ``status`` response. + """ return {'updating_db': 0} # TODO @handle_pattern(r'^urlhandlers$') def _urlhandlers(self): + """ + *musicpd.org, reflection section:* + + ``urlhandlers`` + + Gets a list of available URL handlers. + """ return self.backend.uri_handlers