From 62429d6781472afbcc1250988dcd8e95f31cfb67 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 6 Feb 2010 02:51:07 +0100 Subject: [PATCH 01/10] Fix typo --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 766d5354..9bca444e 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Mopidy Mopidy is an `MPD `_ server with a `Spotify `_ backend. Using a standard MPD client you -can search for music in Spotify's wast archive, manage Spotify play lists and +can search for music in Spotify's vast archive, manage Spotify play lists and play music from Spotify. Mopidy is currently under development. Unless you want to contribute to the From c0ee95777b0b54e3536a792131fbd952cc262531 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 6 Feb 2010 03:26:10 +0100 Subject: [PATCH 02/10] Fix crash in despotify search --- mopidy/backends/despotify.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mopidy/backends/despotify.py b/mopidy/backends/despotify.py index 646228c8..a60e2aac 100644 --- a/mopidy/backends/despotify.py +++ b/mopidy/backends/despotify.py @@ -120,4 +120,5 @@ class DespotifyBackend(BaseBackend): def search(self, type, what): query = u'%s:%s' % (type, what) result = self.spotify.search(query.encode(ENCODING)) - return self._to_mopidy_playlist(result.playlist).mpd_format() + if result is not None: + return self._to_mopidy_playlist(result.playlist).mpd_format() From 1c75716a3904f5fc0ed009c64669bcc6bf4ff7be Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 6 Feb 2010 03:29:49 +0100 Subject: [PATCH 03/10] Remove end_of_track hack --- mopidy/backends/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index b7d761ff..d087a241 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -123,10 +123,6 @@ class BaseBackend(object): return self.state def status_time(self): - # XXX This is only called when a client is connected, and is thus not a - # complete solution - if self._play_time_elapsed >= self.status_time_total() > 0: - self.end_of_track() return u'%s:%s' % (self._play_time_elapsed, self.status_time_total()) def status_time_total(self): From 19bd58f3428e973045c1fb4d75aa347ee89889f2 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 6 Feb 2010 03:37:58 +0100 Subject: [PATCH 04/10] When loading a new playlist, reset the current song pos, and if state==PLAY play the first song in the new list --- mopidy/backends/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index d087a241..a98be901 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -205,9 +205,12 @@ class BaseBackend(object): return None def playlist_load(self, name): + self._current_song_pos = None matches = filter(lambda p: p.name == name, self._playlists) if matches: self._current_playlist = matches[0] + if self.state == self.PLAY: + self.play(songpos=0) else: self._current_playlist = None From 16d79866103483bd0f342bec5567e139d6055bb1 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 6 Feb 2010 13:02:27 +0100 Subject: [PATCH 05/10] Create docs/_static dir --- docs/_static/.placeholder | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/_static/.placeholder diff --git a/docs/_static/.placeholder b/docs/_static/.placeholder new file mode 100644 index 00000000..e69de29b From ea45b4d5e782553688704d65d1b40e46394884b5 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 6 Feb 2010 13:59:50 +0100 Subject: [PATCH 06/10] Add API doc of mopidy.models --- docs/api/models.rst | 8 +++++ docs/conf.py | 6 ++-- docs/development.rst | 9 +++++ docs/index.rst | 1 + mopidy/models.py | 81 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 docs/api/models.rst diff --git a/docs/api/models.rst b/docs/api/models.rst new file mode 100644 index 00000000..75f9ab02 --- /dev/null +++ b/docs/api/models.rst @@ -0,0 +1,8 @@ +********************************************* +:mod:`mopidy.models` -- Immutable data models +********************************************* + +.. automodule:: mopidy.models + :synopsis: Immutable data models. + :members: + :undoc-members: diff --git a/docs/conf.py b/docs/conf.py index f0d29003..8cb63290 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,13 +16,13 @@ import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.append(os.path.abspath('.')) +sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/../')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [] +extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -147,7 +147,7 @@ html_static_path = ['_static'] #html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +html_show_sourcelink = False # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the diff --git a/docs/development.rst b/docs/development.rst index 68239393..959dcd58 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -6,6 +6,15 @@ Development of Mopidy is coordinated through the IRC channel ``#mopidy`` at ``irc.freenode.net`` and through `GitHub `_. +API documentation +================= + +.. toctree:: + :glob: + + api/* + + Scope ===== diff --git a/docs/index.rst b/docs/index.rst index 08a9dacd..7c618dc3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,5 +14,6 @@ Indices and tables ================== * :ref:`genindex` +* :ref:`modindex` * :ref:`search` diff --git a/mopidy/models.py b/mopidy/models.py index bd47c1ef..d81dfe4f 100644 --- a/mopidy/models.py +++ b/mopidy/models.py @@ -1,20 +1,40 @@ from copy import copy class Artist(object): + """ + :param uri: artist URI + :type uri: string + :param name: artist name + :type name: string + """ + def __init__(self, uri=None, name=None): self._uri = None self._name = name @property def uri(self): + """The artist URI. Read-only.""" return self._uri @property def name(self): + """The artist name. Read-only.""" return self._name class Album(object): + """ + :param uri: album URI + :type uri: string + :param name: album name + :type name: string + :param artists: album artists + :type artists: list of :class:`Artist` + :param num_tracks: number of tracks in album + :type num_tracks: integer + """ + def __init__(self, uri=None, name=None, artists=None, num_tracks=0): self._uri = uri self._name = name @@ -23,22 +43,45 @@ class Album(object): @property def uri(self): + """The album URI. Read-only.""" return self._uri @property def name(self): + """The album name. Read-only.""" return self._name @property def artists(self): + """List of :class:`Artist` elements. Read-only.""" return copy(self._artists) @property def num_tracks(self): + """The number of tracks in the album. Read-only.""" return self._num_tracks class Track(object): + """ + :param uri: track URI + :type uri: string + :param title: track title + :type title: string + :param artists: track artists + :type artists: list of :class:`Artist` + :param album: track album + :type album: :class:`Album` + :param track_no: track number in album + :type track_no: integer + :param date: track release date + :type date: :class:`datetime.date` + :param length: track length in milliseconds + :type length: integer + :param id: track ID (unique and non-changing as long as the process lives) + :type id: integer + """ + def __init__(self, uri=None, title=None, artists=None, album=None, track_no=0, date=None, length=None, id=None): self._uri = uri @@ -52,37 +95,52 @@ class Track(object): @property def uri(self): + """The track URI. Read-only.""" return self._uri @property def title(self): + """The track title. Read-only.""" return self._title @property def artists(self): + """List of :class:`Artist`. Read-only.""" return copy(self._artists) @property def album(self): + """The track :class:`Album`. Read-only.""" return self._album @property def track_no(self): + """The track number in album. Read-only.""" return self._track_no @property def date(self): + """The track release date. Read-only.""" return self._date @property def length(self): + """The track length in milliseconds. Read-only.""" return self._length @property def id(self): + """The track ID. Read-only.""" return self._id def mpd_format(self, position=0): + """ + Format track for output to MPD client. + + :param position: track's position in playlist + :type position: integer + :rtype: list of two-tuples + """ return [ ('file', self.uri), ('Time', self.length // 1000), @@ -96,10 +154,24 @@ class Track(object): ] def mpd_format_artists(self): + """ + Format track artists for output to MPD client. + + :rtype: string + """ return u', '.join([a.name for a in self.artists]) class Playlist(object): + """ + :param uri: playlist URI + :type uri: string + :param name: playlist name + :type name: string + :param tracks: playlist's tracks + :type tracks: list of :class:`Track` elements + """ + def __init__(self, uri=None, name=None, tracks=None): self._uri = uri self._name = name @@ -107,21 +179,30 @@ class Playlist(object): @property def uri(self): + """The playlist URI. Read-only.""" return self._uri @property def name(self): + """The playlist name. Read-only.""" return self._name @property def tracks(self): + """List of :class:`Track` elements. Read-only.""" return copy(self._tracks) @property def length(self): + """The number of tracks in the playlist. Read-only.""" return len(self._tracks) def mpd_format(self, start=0, end=None): + """ + Format playlist for output to MPD client. + + :rtype: list of lists of two-tuples + """ if end is None: end = self.length tracks = [] From 931ff06613fa02aae7abb7e29f52d17e730f3c66 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 6 Feb 2010 14:27:44 +0100 Subject: [PATCH 07/10] Add Sphinx nature theme CSS taken from the pip documentation --- docs/_themes/nature/static/nature.css_t | 121 +++++++++++++----------- 1 file changed, 64 insertions(+), 57 deletions(-) diff --git a/docs/_themes/nature/static/nature.css_t b/docs/_themes/nature/static/nature.css_t index 03b0379d..8762e019 100644 --- a/docs/_themes/nature/static/nature.css_t +++ b/docs/_themes/nature/static/nature.css_t @@ -10,8 +10,8 @@ body { font-family: Arial, sans-serif; font-size: 100%; - background-color: #111; - color: #555; + background-color: #111111; + color: #555555; margin: 0; padding: 0; } @@ -22,7 +22,7 @@ div.documentwrapper { } div.bodywrapper { - margin: 0 0 0 230px; + margin: 0 0 0 300px; } hr{ @@ -30,14 +30,14 @@ hr{ } div.document { - background-color: #eee; + background-color: #fafafa; } div.body { background-color: #ffffff; color: #3E4349; - padding: 0 30px 30px 30px; - font-size: 0.8em; + padding: 1em 30px 30px 30px; + font-size: 0.9em; } div.footer { @@ -49,25 +49,29 @@ div.footer { } div.footer a { - color: #444; - text-decoration: underline; + color: #444444; } div.related { background-color: #6BA81E; - line-height: 32px; - color: #fff; - text-shadow: 0px 1px 0 #444; - font-size: 0.80em; + line-height: 36px; + color: #ffffff; + text-shadow: 0px 1px 0 #444444; + font-size: 1.1em; } div.related a { color: #E2F3CC; } - + +div.related .right { + font-size: 0.9em; +} + div.sphinxsidebar { - font-size: 0.75em; + font-size: 0.9em; line-height: 1.5em; + width: 300px } div.sphinxsidebarwrapper{ @@ -77,46 +81,46 @@ div.sphinxsidebarwrapper{ div.sphinxsidebar h3, div.sphinxsidebar h4 { font-family: Arial, sans-serif; - color: #222; + color: #222222; font-size: 1.2em; - font-weight: normal; + font-weight: bold; margin: 0; padding: 5px 10px; - background-color: #ddd; text-shadow: 1px 1px 0 white } -div.sphinxsidebar h4{ - font-size: 1.1em; -} - div.sphinxsidebar h3 a { - color: #444; + color: #444444; } - - + div.sphinxsidebar p { - color: #888; + color: #888888; padding: 5px 20px; + margin: 0.5em 0px; } div.sphinxsidebar p.topless { } div.sphinxsidebar ul { - margin: 10px 20px; + margin: 10px 10px 10px 20px; padding: 0; - color: #000; + color: #000000; } div.sphinxsidebar a { - color: #444; + color: #444444; } - + +div.sphinxsidebar a:hover { + color: #E32E00; +} + div.sphinxsidebar input { - border: 1px solid #ccc; + border: 1px solid #cccccc; font-family: sans-serif; - font-size: 1em; + font-size: 1.1em; + padding: 0.15em 0.3em; } div.sphinxsidebar input[type=text]{ @@ -132,7 +136,6 @@ a { a:hover { color: #E32E00; - text-decoration: underline; } div.body h1, @@ -142,20 +145,20 @@ div.body h4, div.body h5, div.body h6 { font-family: Arial, sans-serif; - background-color: #BED4EB; font-weight: normal; color: #212224; margin: 30px 0px 10px 0px; - padding: 5px 0 5px 10px; - text-shadow: 0px 1px 0 white + padding: 5px 0 5px 0px; + text-shadow: 0px 1px 0 white; + border-bottom: 1px solid #C8D5E3; } -div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 150%; background-color: #C8D5E3; } -div.body h3 { font-size: 120%; background-color: #D8DEE3; } -div.body h4 { font-size: 110%; background-color: #D8DEE3; } -div.body h5 { font-size: 100%; background-color: #D8DEE3; } -div.body h6 { font-size: 100%; background-color: #D8DEE3; } +div.body h1 { margin-top: 0; font-size: 200%; } +div.body h2 { font-size: 150%; } +div.body h3 { font-size: 120%; } +div.body h4 { font-size: 110%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } a.headerlink { color: #c60f0f; @@ -170,7 +173,7 @@ a.headerlink:hover { } div.body p, div.body dd, div.body li { - line-height: 1.5em; + line-height: 1.8em; } div.admonition p.admonition-title + p { @@ -182,22 +185,23 @@ div.highlight{ } div.note { - background-color: #eee; - border: 1px solid #ccc; + background-color: #eeeeee; + border: 1px solid #cccccc; } div.seealso { - background-color: #ffc; - border: 1px solid #ff6; + background-color: #ffffcc; + border: 1px solid #ffff66; } div.topic { - background-color: #eee; + background-color: #fafafa; + border-width: 0; } div.warning { background-color: #ffe4e4; - border: 1px solid #f66; + border: 1px solid #ff6666; } p.admonition-title { @@ -210,20 +214,23 @@ p.admonition-title:after { pre { padding: 10px; - background-color: White; - color: #222; - line-height: 1.2em; - border: 1px solid #C6C9CB; - font-size: 1.2em; + background-color: #fafafa; + color: #222222; + line-height: 1.5em; + font-size: 1.1em; margin: 1.5em 0 1.5em 0; - -webkit-box-shadow: 1px 1px 1px #d8d8d8; - -moz-box-shadow: 1px 1px 1px #d8d8d8; + -webkit-box-shadow: 0px 0px 4px #d8d8d8; + -moz-box-shadow: 0px 0px 4px #d8d8d8; + box-shadow: 0px 0px 4px #d8d8d8; } tt { - background-color: #ecf0f3; - color: #222; + color: #222222; padding: 1px 2px; font-size: 1.2em; font-family: monospace; } + +#table-of-contents ul { + padding-left: 2em; +} From 06720e27644f7999927547567887ef5675c252dd Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 6 Feb 2010 15:14:31 +0100 Subject: [PATCH 08/10] Remove method which was defined twice --- mopidy/backends/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index a98be901..af3e1b23 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -201,9 +201,6 @@ class BaseBackend(object): # Current/single playlist methods - def playlist_changes_since(self, version): - return None - def playlist_load(self, name): self._current_song_pos = None matches = filter(lambda p: p.name == name, self._playlists) From 554c0b4e52cfb42913415e0628069e77513fc3de Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 7 Feb 2010 02:28:41 +0100 Subject: [PATCH 09/10] Add Track.bitrate field --- mopidy/models.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mopidy/models.py b/mopidy/models.py index d81dfe4f..39212c8e 100644 --- a/mopidy/models.py +++ b/mopidy/models.py @@ -78,12 +78,14 @@ class Track(object): :type date: :class:`datetime.date` :param length: track length in milliseconds :type length: integer + :param bitrate: bitrate in kbit/s + :type bitrate: integer :param id: track ID (unique and non-changing as long as the process lives) :type id: integer """ def __init__(self, uri=None, title=None, artists=None, album=None, - track_no=0, date=None, length=None, id=None): + track_no=0, date=None, length=None, bitrate=None, id=None): self._uri = uri self._title = title self._artists = artists or [] @@ -91,6 +93,7 @@ class Track(object): self._track_no = track_no self._date = date self._length = length + self._bitrate = bitrate self._id = id @property @@ -128,6 +131,11 @@ class Track(object): """The track length in milliseconds. Read-only.""" return self._length + @property + def bitrate(self): + """The track's bitrate in kbit/s. Read-only.""" + return self._bitrate + @property def id(self): """The track ID. Read-only.""" From 922662bff914b78e0aa7d9759d2f185bf7dd5ecd Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sun, 7 Feb 2010 03:34:43 +0100 Subject: [PATCH 10/10] First draft of backend API --- docs/api/backends.rst | 298 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 docs/api/backends.rst diff --git a/docs/api/backends.rst b/docs/api/backends.rst new file mode 100644 index 00000000..365c6d85 --- /dev/null +++ b/docs/api/backends.rst @@ -0,0 +1,298 @@ +************************************* +:mod:`mopidy.backends` -- Backend API +************************************* + +.. warning:: + This is our *planned* backend API, and not the current API. + +.. module:: mopidy.backends + :synopsis: Interface between Mopidy and its various backends. + +.. class:: BaseBackend() + + .. attribute:: current_playlist + + The current playlist controller. An instance of + :class:`BaseCurrentPlaylistController`. + + .. attribute:: library + + The library controller. An instance of :class:`BaseLibraryController`. + + .. attribute:: playback + + The playback controller. An instance of :class:`BasePlaybackController`. + + .. attribute:: stored_playlists + + The stored playlists controller. An instance of + :class:`BaseStoredPlaylistsController`. + + .. attribute:: uri_handlers + + List of URI prefixes this backend can handle. + + +.. class:: BaseCurrentPlaylistController(backend) + + :param backend: backend the controller is a part of + :type backend: :class:`BaseBackend` + + .. method:: add(track, at_position=None) + + Add the track to the end of, or at the given position in the current + playlist. + + :param track: track to add + :type track: :class:`mopidy.models.Track` + :param at_position: position in current playlist to add track + :type at_position: int or :class:`None` + + .. method:: clear() + + Clear the current playlist. + + .. method:: load(playlist) + + Replace the current playlist with the given playlist. + + :param playlist: playlist to load + :type playlist: :class:`mopidy.models.Playlist` + + .. method:: move(start, end, to_position) + + Move the tracks at positions in [``start``, ``end``] to + ``to_position``. + + :param start: position of first track to move + :type start: int + :param end: position of last track to move + :type end: int + :param to_position: new position for the tracks + :type to_position: int + + .. attribute:: playlist + + The currently loaded :class:`mopidy.models.Playlist`. + + .. method:: remove(position) + + Remove the track at ``position`` from the current playlist. + + :param position: position of track to remove + :type position: int + + .. method:: shuffle(start=None, end=None) + + Shuffles the playlist, optionally a part of the playlist given by + ``start`` and ``end``. + + :param start: position of first track to shuffle + :type start: int or :class:`None` + :param end: position of last track to shuffle + :type end: int or :class:`None` + + .. attribute:: version + + The current playlist version. Integer which is increased every time the + current playlist is changed. + + +.. class:: BasePlaybackController(backend) + + :param backend: backend the controller is a part of + :type backend: :class:`BaseBackend` + + .. attribute:: consume + + :class:`True` + Tracks are removed from the playlist when they have been played. + :class:`False` + Tracks are not removed from the playlist. + + .. attribute:: current_track + + The currently playing or selected :class:`mopidy.models.Track`. + + .. method:: next() + + Play the next track. + + .. method:: pause() + + Pause playblack. + + .. attribute:: PAUSED + + Constant representing the paused state. + + .. method:: play(id=None, position=None) + + Play either the track with the given ID, the given position, or the + currently active track. + + :param id: ID of track to play + :type id: int + :param position: position in current playlist of track to play + :type position: int + + .. attribute:: PLAYING + + Constant representing the playing state. + + .. attribute:: playlist_position + + The position in the current playlist. + + .. method:: previous() + + Play the previous track. + + .. attribute:: random + + :class:`True` + Tracks are selected at random from the playlist. + :class:`False` + Tracks are played in the order of the playlist. + + .. attribute:: repeat + + :class:`True` + The current track is played repeatedly. + :class:`False` + The current track is played once. + + .. method:: resume() + + If paused, resume playing the current track. + + .. method:: seek(time_position) + + Seeks to time position given in milliseconds. + + :param time_position: time position in milliseconds + :type time_position: int + + .. attribute:: state + + The playback state. Must be :attr:`PLAYING`, :attr:`PAUSED`, or + :attr:`STOPPED`. + + .. method:: stop() + + Stop playing. + + .. attribute:: STOPPED + + Constant representing the stopped state. + + .. attribute:: time_position + + Time position in milliseconds. + + .. attribute:: volume + + The audio volume as an int in the range [0, 100]. :class:`None` if + unknown. + + +.. class:: BaseLibraryController(backend) + + :param backend: backend the controller is a part of + :type backend: :class:`BaseBackend` + + .. method:: find_exact(type, query) + + Find tracks in the library where ``type`` matches ``query`` exactly. + + :param type: 'title', 'artist', or 'album' + :type type: string + :param query: the search query + :type query: string + :rtype: list of :class:`mopidy.models.Track` + + .. method:: lookup(uri) + + Lookup track with given URI. + + :param uri: track URI + :type uri: string + :rtype: :class:`mopidy.models.Track` + + .. method:: refresh(uri=None) + + Refresh library. Limit to URI and below if an URI is given. + + :param uri: directory or track URI + :type uri: string + + .. method:: search(type, query) + + Search the library for tracks where ``type`` contains ``query``. + + :param type: 'title', 'artist', 'album', or 'uri' + :type type: string + :param query: the search query + :type query: string + :rtype: list of :class:`mopidy.models.Track` + + +.. class:: BaseStoredPlaylistsController(backend) + + :param backend: backend the controller is a part of + :type backend: :class:`BaseBackend` + + .. method:: add(uri) + + Add existing playlist with the given URI. + + :param uri: URI of existing playlist + :type uri: string + + .. method:: create(name) + + Create a new playlist. + + :param name: name of the new playlist + :type name: string + :rtype: :class:`mopidy.models.Playlist` + + .. attribute:: playlists + + List of :class:`mopidy.models.Playlist`. + + .. method:: delete(playlist) + + Delete playlist. + + :param playlist: the playlist to delete + :type playlist: :class:`mopidy.models.Playlist` + + .. method:: lookup(uri) + + Lookup playlist with given URI. + + :param uri: playlist URI + :type uri: string + :rtype: :class:`mopidy.models.Playlist` + + .. method:: refresh() + + Refresh stored playlists. + + .. method:: rename(playlist, new_name) + + Rename playlist. + + :param playlist: the playlist + :type playlist: :class:`mopidy.models.Playlist` + :param new_name: the new name + :type new_name: string + + .. method:: search(query) + + Search for playlists whose name contains ``query``. + + :param query: query to search for + :type query: string + :rtype: list of :class:`mopidy.models.Playlist`