Merge branch 'develop' into feature/http-frontend

This commit is contained in:
Stein Magnus Jodal 2012-11-20 00:27:19 +01:00
commit 56c4892f46
29 changed files with 379 additions and 347 deletions

View File

@ -128,6 +128,23 @@ backends:
means that you now can select playlists to queue and play from the Ubuntu
Sound Menu.
- :meth:`mopidy.core.LibraryController.find_exact` and
:meth:`mopidy.core.LibraryController.search` now returns plain lists of
tracks instead of playlist objects.
- :meth:`mopidy.core.TracklistController.get` has been replaced by
:meth:`mopidy.core.TracklistController.filter`.
- :meth:`mopidy.core.PlaylistsController.get` has been replaced by
:meth:`mopidy.core.PlaylistsController.filter`.
- :meth:`mopidy.core.TracklistController.remove` can now remove multiple
tracks, and returns the tracks it removed.
- :meth:`mopidy.core.LibraryController.lookup` now returns a list of tracks.
This makes it possible to support lookup of artist or album URIs which then
can expand to a list of tracks.
**Bug fixes**
- :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now
@ -143,6 +160,10 @@ backends:
files (Apple lossless) because it didn't support multiple tag messages from
GStreamer per track it scanned.
- :issue:`246`: The MPD command ``list album artist ""`` and similar
``search``, ``find``, and ``list`` commands with empty filter values caused a
:exc:`LookupError`, but should have been ignored by the MPD server.
v0.8.1 (2012-10-30)
===================

View File

@ -89,7 +89,8 @@ class Audio(pykka.ThreadingActor):
b'rate=(int)44100')
source = element.get_property('source')
source.set_property('caps', default_caps)
source.set_property('format', b'time') # Gstreamer does not like unicode
# GStreamer does not like unicode
source.set_property('format', b'time')
self._appsrc = source
@ -219,8 +220,8 @@ class Audio(pykka.ThreadingActor):
logger.debug(
'Triggering event: state_changed(old_state=%s, new_state=%s)',
old_state, new_state)
AudioListener.send('state_changed',
old_state=old_state, new_state=new_state)
AudioListener.send(
'state_changed', old_state=old_state, new_state=new_state)
def _on_end_of_stream(self):
logger.debug('Triggering reached_end_of_stream event')

View File

@ -39,18 +39,16 @@ class DummyLibraryProvider(base.BaseLibraryProvider):
self.dummy_library = []
def find_exact(self, **query):
return Playlist()
return []
def lookup(self, uri):
matches = filter(lambda t: uri == t.uri, self.dummy_library)
if matches:
return matches[0]
return filter(lambda t: uri == t.uri, self.dummy_library)
def refresh(self, uri=None):
pass
def search(self, **query):
return Playlist()
return []
class DummyPlaybackProvider(base.BasePlaybackProvider):

View File

@ -4,7 +4,7 @@ import logging
from mopidy import settings
from mopidy.backends import base
from mopidy.models import Playlist, Album
from mopidy.models import Album
from .translator import parse_mpd_tag_cache
@ -30,10 +30,10 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
def lookup(self, uri):
try:
return self._uri_mapping[uri]
return [self._uri_mapping[uri]]
except KeyError:
logger.debug('Failed to lookup %r', uri)
return None
return []
def find_exact(self, **query):
self._validate_query(query)
@ -67,7 +67,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
result_tracks = filter(any_filter, result_tracks)
else:
raise LookupError('Invalid lookup field: %s' % field)
return Playlist(tracks=result_tracks)
return result_tracks
def search(self, **query):
self._validate_query(query)
@ -101,7 +101,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
result_tracks = filter(any_filter, result_tracks)
else:
raise LookupError('Invalid lookup field: %s' % field)
return Playlist(tracks=result_tracks)
return result_tracks
def _validate_query(self, query):
for (_, values) in query.iteritems():

View File

@ -55,7 +55,7 @@ class LocalPlaylistsProvider(base.BasePlaylistsProvider):
try:
# TODO We must use core.library.lookup() to support tracks
# from other backends
tracks.append(self.backend.library.lookup(track_uri))
tracks += self.backend.library.lookup(track_uri)
except LookupError as ex:
logger.error('Playlist item could not be added: %s', ex)

View File

@ -6,7 +6,7 @@ import Queue
from spotify import Link, SpotifyError
from mopidy.backends import base
from mopidy.models import Track, Playlist
from mopidy.models import Track
from . import translator
@ -57,10 +57,10 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
def lookup(self, uri):
try:
return SpotifyTrack(uri)
return [SpotifyTrack(uri)]
except SpotifyError as e:
logger.debug('Failed to lookup "%s": %s', uri, e)
return None
return []
def refresh(self, uri=None):
pass # TODO
@ -72,7 +72,7 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
tracks = []
for playlist in self.backend.playlists.playlists:
tracks += playlist.tracks
return Playlist(tracks=tracks)
return tracks
spotify_query = []
for (field, values) in query.iteritems():
if field == 'uri':
@ -81,7 +81,7 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
track = self.lookup(value)
if track:
tracks.append(track)
return Playlist(tracks=tracks)
return tracks
elif field == 'track':
field = 'title'
elif field == 'date':
@ -103,4 +103,4 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
try:
return queue.get(timeout=3) # XXX What is an reasonable timeout?
except Queue.Empty:
return Playlist(tracks=[])
return []

View File

@ -12,7 +12,6 @@ from spotify.manager import SpotifySessionManager as PyspotifySessionManager
from mopidy import settings
from mopidy.backends.listener import BackendListener
from mopidy.models import Playlist
from mopidy.utils import process, versioning
from . import translator
@ -164,9 +163,9 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
# TODO Include results from results.albums(), etc. too
# TODO Consider launching a second search if results.total_tracks()
# is larger than len(results.tracks())
playlist = Playlist(tracks=[
translator.to_mopidy_track(t) for t in results.tracks()])
queue.put(playlist)
tracks = [
translator.to_mopidy_track(t) for t in results.tracks()]
queue.put(tracks)
self.connected.wait()
self.session.search(
query, callback, track_count=100, album_count=0, artist_count=0)

View File

@ -5,8 +5,6 @@ import urlparse
import pykka
from mopidy.models import Playlist
class LibraryController(object):
pykka_traversable = True
@ -34,27 +32,29 @@ class LibraryController(object):
:param query: one or more queries to search for
:type query: dict
:rtype: :class:`mopidy.models.Playlist`
:rtype: list of :class:`mopidy.models.Track`
"""
futures = [b.library.find_exact(**query)
for b in self.backends.with_library]
futures = [
b.library.find_exact(**query) for b in self.backends.with_library]
results = pykka.get_all(futures)
return Playlist(tracks=[
track for playlist in results for track in playlist.tracks])
return list(itertools.chain(*results))
def lookup(self, uri):
"""
Lookup track with given URI. Returns :class:`None` if not found.
Lookup the given URI.
If the URI expands to multiple tracks, the returned list will contain
them all.
:param uri: track URI
:type uri: string
:rtype: :class:`mopidy.models.Track` or :class:`None`
:rtype: list of :class:`mopidy.models.Track`
"""
backend = self._get_backend(uri)
if backend:
return backend.library.lookup(uri).get()
else:
return None
return []
def refresh(self, uri=None):
"""
@ -68,8 +68,8 @@ class LibraryController(object):
if backend:
backend.library.refresh(uri).get()
else:
futures = [b.library.refresh(uri)
for b in self.backends.with_library]
futures = [
b.library.refresh(uri) for b in self.backends.with_library]
pykka.get_all(futures)
def search(self, **query):
@ -87,11 +87,9 @@ class LibraryController(object):
:param query: one or more queries to search for
:type query: dict
:rtype: :class:`mopidy.models.Playlist`
:rtype: list of :class:`mopidy.models.Track`
"""
futures = [b.library.search(**query)
for b in self.backends.with_library]
futures = [
b.library.search(**query) for b in self.backends.with_library]
results = pykka.get_all(futures)
track_lists = [playlist.tracks for playlist in results]
tracks = list(itertools.chain(*track_lists))
return Playlist(tracks=tracks)
return list(itertools.chain(*results))

View File

@ -38,9 +38,7 @@ class PlaybackController(object):
#: Tracks are not removed from the playlist.
consume = option_wrapper('_consume', False)
#: The currently playing or selected track.
#:
#: A two-tuple of (TLID integer, :class:`mopidy.models.Track`) or
#: The currently playing or selected :class:`mopidy.models.TlTrack`, or
#: :class:`None`.
current_tl_track = None
@ -139,7 +137,7 @@ class PlaybackController(object):
"""
The track that will be played at the end of the current track.
Read-only. A two-tuple of (TLID integer, :class:`mopidy.models.Track`).
Read-only. A :class:`mopidy.models.TlTrack`.
Not necessarily the same track as :attr:`tl_track_at_next`.
"""
@ -190,7 +188,7 @@ class PlaybackController(object):
"""
The track that will be played if calling :meth:`next()`.
Read-only. A two-tuple of (TLID integer, :class:`mopidy.models.Track`).
Read-only. A :class:`mopidy.models.TlTrack`.
For normal playback this is the next track in the playlist. If repeat
is enabled the next track can loop around the playlist. When random is
@ -238,7 +236,7 @@ class PlaybackController(object):
"""
The track that will be played if calling :meth:`previous()`.
A two-tuple of (TLID integer, :class:`mopidy.models.Track`).
A :class:`mopidy.models.TlTrack`.
For normal playback this is the previous track in the playlist. If
random and/or consume is enabled it should return the current track
@ -310,12 +308,10 @@ class PlaybackController(object):
Change to the given track, keeping the current playback state.
:param tl_track: track to change to
:type tl_track: two-tuple (TLID integer, :class:`mopidy.models.Track`)
or :class:`None`
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
:param on_error_step: direction to step at play error, 1 for next
track (default), -1 for previous track
:type on_error_step: int, -1 or 1
"""
old_state = self.state
self.stop()
@ -383,8 +379,7 @@ class PlaybackController(object):
currently active track.
:param tl_track: track to play
:type tl_track: two-tuple (TLID integer, :class:`mopidy.models.Track`)
or :class:`None`
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
:param on_error_step: direction to step at play error, 1 for next
track (default), -1 for previous track
:type on_error_step: int, -1 or 1

View File

@ -69,35 +69,25 @@ class PlaylistsController(object):
if backend:
backend.playlists.delete(uri).get()
def get(self, **criteria):
def filter(self, **criteria):
"""
Get playlist by given criterias from the set of playlists.
Raises :exc:`LookupError` if a unique match is not found.
Filter playlists by the given criterias.
Examples::
get(name='a') # Returns track with name 'a'
get(uri='xyz') # Returns track with URI 'xyz'
get(name='a', uri='xyz') # Returns track with name 'a' and URI
# 'xyz'
filter(name='a') # Returns track with name 'a'
filter(uri='xyz') # Returns track with URI 'xyz'
filter(name='a', uri='xyz') # Returns track with name 'a' and URI
# 'xyz'
:param criteria: one or more criteria to match by
:type criteria: dict
:rtype: :class:`mopidy.models.Playlist`
:rtype: list of :class:`mopidy.models.Playlist`
"""
matches = self.playlists
for (key, value) in criteria.iteritems():
matches = filter(lambda p: getattr(p, key) == value, matches)
if len(matches) == 1:
return matches[0]
criteria_string = ', '.join(
['%s=%s' % (k, v) for (k, v) in criteria.iteritems()])
if len(matches) == 0:
raise LookupError('"%s" match no playlists' % criteria_string)
else:
raise LookupError(
'"%s" match multiple playlists' % criteria_string)
return matches
def lookup(self, uri):
"""

View File

@ -23,7 +23,7 @@ class TracklistController(object):
@property
def tl_tracks(self):
"""
List of two-tuples of (TLID integer, :class:`mopidy.models.Track`).
List of :class:`mopidy.models.TlTrack`.
Read-only.
"""
@ -72,8 +72,7 @@ class TracklistController(object):
:type at_position: int or :class:`None`
:param increase_version: if the tracklist version should be increased
:type increase_version: :class:`True` or :class:`False`
:rtype: two-tuple of (TLID integer, :class:`mopidy.models.Track`) that
was added to the tracklist
:rtype: :class:`mopidy.models.TlTrack` that was added to the tracklist
"""
assert at_position <= len(self._tl_tracks), \
'at_position can not be greater than tracklist length'
@ -117,22 +116,20 @@ class TracklistController(object):
self._tl_tracks = []
self.version += 1
def get(self, **criteria):
def filter(self, **criteria):
"""
Get track by given criterias from tracklist.
Raises :exc:`LookupError` if a unique match is not found.
Filter the tracklist by the given criterias.
Examples::
get(tlid=7) # Returns track with TLID 7 (tracklist ID)
get(id=1) # Returns track with ID 1
get(uri='xyz') # Returns track with URI 'xyz'
get(id=1, uri='xyz') # Returns track with ID 1 and URI 'xyz'
filter(tlid=7) # Returns track with TLID 7 (tracklist ID)
filter(id=1) # Returns track with ID 1
filter(uri='xyz') # Returns track with URI 'xyz'
filter(id=1, uri='xyz') # Returns track with ID 1 and URI 'xyz'
:param criteria: on or more criteria to match by
:type criteria: dict
:rtype: two-tuple (TLID integer, :class:`mopidy.models.Track`)
:rtype: list of :class:`mopidy.models.TlTrack`
"""
matches = self._tl_tracks
for (key, value) in criteria.iteritems():
@ -141,24 +138,16 @@ class TracklistController(object):
else:
matches = filter(
lambda ct: getattr(ct.track, key) == value, matches)
if len(matches) == 1:
return matches[0]
criteria_string = ', '.join(
['%s=%s' % (k, v) for (k, v) in criteria.iteritems()])
if len(matches) == 0:
raise LookupError('"%s" match no tracks' % criteria_string)
else:
raise LookupError('"%s" match multiple tracks' % criteria_string)
return matches
def index(self, tl_track):
"""
Get index of the given (TLID integer, :class:`mopidy.models.Track`)
two-tuple in the tracklist.
Get index of the given :class:`mopidy.models.TlTrack` in the tracklist.
Raises :exc:`ValueError` if not found.
:param tl_track: track to find the index of
:type tl_track: two-tuple (TLID integer, :class:`mopidy.models.Track`)
:type tl_track: :class:`mopidy.models.TlTrack`
:rtype: int
"""
return self._tl_tracks.index(tl_track)
@ -199,20 +188,23 @@ class TracklistController(object):
def remove(self, **criteria):
"""
Remove the track from the tracklist.
Remove the matching tracks from the tracklist.
Uses :meth:`get()` to lookup the track to remove.
Uses :meth:`filter()` to lookup the tracks to remove.
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed`
event.
:param criteria: on or more criteria to match by
:type criteria: dict
:rtype: list of :class:`mopidy.models.TlTrack` that was removed
"""
tl_track = self.get(**criteria)
position = self._tl_tracks.index(tl_track)
del self._tl_tracks[position]
tl_tracks = self.filter(**criteria)
for tl_track in tl_tracks:
position = self._tl_tracks.index(tl_track)
del self._tl_tracks[position]
self.version += 1
return tl_tracks
def shuffle(self, start=None, end=None):
"""
@ -255,7 +247,7 @@ class TracklistController(object):
:type start: int
:param end: position after last track to include in slice
:type end: int
:rtype: two-tuple of (TLID integer, :class:`mopidy.models.Track`)
:rtype: :class:`mopidy.models.TlTrack`
"""
return self._tl_tracks[start:end]

View File

@ -22,9 +22,9 @@ def add(context, uri):
"""
if not uri:
return
track = context.core.library.lookup(uri).get()
if track:
context.core.tracklist.add(track)
tracks = context.core.library.lookup(uri).get()
if tracks:
context.core.tracklist.append(tracks)
return
raise MpdNoExistError('directory or file not found', command='add')
@ -52,13 +52,19 @@ def addid(context, uri, songpos=None):
raise MpdNoExistError('No such song', command='addid')
if songpos is not None:
songpos = int(songpos)
track = context.core.library.lookup(uri).get()
if track is None:
tracks = context.core.library.lookup(uri).get()
if not tracks:
raise MpdNoExistError('No such song', command='addid')
if songpos and songpos > context.core.tracklist.length.get():
raise MpdArgError('Bad song index', command='addid')
tl_track = context.core.tracklist.add(track, at_position=songpos).get()
return ('Id', tl_track.tlid)
first_tl_track = None
for track in tracks:
tl_track = context.core.tracklist.add(track, at_position=songpos).get()
if songpos is not None:
songpos += 1
if first_tl_track is None:
first_tl_track = tl_track
return ('Id', first_tl_track.tlid)
@handle_request(r'^delete "(?P<start>\d+):(?P<end>\d+)*"$')
@ -103,12 +109,11 @@ def deleteid(context, tlid):
Deletes the song ``SONGID`` from the playlist
"""
try:
tlid = int(tlid)
if context.core.playback.current_tlid.get() == tlid:
context.core.playback.next()
return context.core.tracklist.remove(tlid=tlid).get()
except LookupError:
tlid = int(tlid)
if context.core.playback.current_tlid.get() == tlid:
context.core.playback.next()
tl_tracks = context.core.tracklist.remove(tlid=tlid).get()
if not tl_tracks:
raise MpdNoExistError('No such song', command='deleteid')
@ -163,8 +168,10 @@ def moveid(context, tlid, to):
"""
tlid = int(tlid)
to = int(to)
tl_track = context.core.tracklist.get(tlid=tlid).get()
position = context.core.tracklist.index(tl_track).get()
tl_tracks = context.core.tracklist.filter(tlid=tlid).get()
if not tl_tracks:
raise MpdNoExistError('No such song', command='moveid')
position = context.core.tracklist.index(tl_tracks[0]).get()
context.core.tracklist.move(position, position + 1, to)
@ -199,12 +206,11 @@ def playlistfind(context, tag, needle):
- does not add quotes around the tag.
"""
if tag == 'filename':
try:
tl_track = context.core.tracklist.get(uri=needle).get()
position = context.core.tracklist.index(tl_track).get()
return translator.track_to_mpd_format(tl_track, position=position)
except LookupError:
tl_tracks = context.core.tracklist.filter(uri=needle).get()
if not tl_tracks:
return None
position = context.core.tracklist.index(tl_tracks[0]).get()
return translator.track_to_mpd_format(tl_tracks[0], position=position)
raise MpdNotImplemented # TODO
@ -219,13 +225,12 @@ def playlistid(context, tlid=None):
and specifies a single song to display info for.
"""
if tlid is not None:
try:
tlid = int(tlid)
tl_track = context.core.tracklist.get(tlid=tlid).get()
position = context.core.tracklist.index(tl_track).get()
return translator.track_to_mpd_format(tl_track, position=position)
except LookupError:
tlid = int(tlid)
tl_tracks = context.core.tracklist.filter(tlid=tlid).get()
if not tl_tracks:
raise MpdNoExistError('No such song', command='playlistid')
position = context.core.tracklist.index(tl_tracks[0]).get()
return translator.track_to_mpd_format(tl_tracks[0], position=position)
else:
return translator.tracks_to_mpd_format(
context.core.tracklist.tl_tracks.get())
@ -385,8 +390,10 @@ def swapid(context, tlid1, tlid2):
"""
tlid1 = int(tlid1)
tlid2 = int(tlid2)
tl_track1 = context.core.tracklist.get(tlid=tlid1).get()
tl_track2 = context.core.tracklist.get(tlid=tlid2).get()
position1 = context.core.tracklist.index(tl_track1).get()
position2 = context.core.tracklist.index(tl_track2).get()
tl_tracks1 = context.core.tracklist.filter(tlid=tlid1).get()
tl_tracks2 = context.core.tracklist.filter(tlid=tlid2).get()
if not tl_tracks1 or not tl_tracks2:
raise MpdNoExistError('No such song', command='swapid')
position1 = context.core.tracklist.index(tl_tracks1[0]).get()
position2 = context.core.tracklist.index(tl_tracks2[0]).get()
swap(context, position1, position2)

View File

@ -5,7 +5,7 @@ import shlex
from mopidy.frontends.mpd.exceptions import MpdArgError, MpdNotImplemented
from mopidy.frontends.mpd.protocol import handle_request, stored_playlists
from mopidy.frontends.mpd.translator import playlist_to_mpd_format
from mopidy.frontends.mpd.translator import tracks_to_mpd_format
def _build_query(mpd_query):
@ -28,6 +28,8 @@ def _build_query(mpd_query):
field = 'uri'
field = str(field) # Needed for kwargs keys on OS X and Windows
what = m.groupdict()['what']
if not what:
raise ValueError
if field in query:
query[field].append(what)
else:
@ -76,8 +78,11 @@ def find(context, mpd_query):
- also uses the search type "date".
- uses "file" instead of "filename".
"""
query = _build_query(mpd_query)
return playlist_to_mpd_format(
try:
query = _build_query(mpd_query)
except ValueError:
return
return tracks_to_mpd_format(
context.core.library.find_exact(**query).get())
@ -185,7 +190,10 @@ def list_(context, field, mpd_query=None):
- capitalizes the field argument.
"""
field = field.lower()
query = _list_build_query(field, mpd_query)
try:
query = _list_build_query(field, mpd_query)
except ValueError:
return
if field == 'artist':
return _list_artist(context, query)
elif field == 'album':
@ -211,6 +219,8 @@ def _list_build_query(field, mpd_query):
tokens = [t.decode('utf-8') for t in tokens]
if len(tokens) == 1:
if field == 'album':
if not tokens[0]:
raise ValueError
return {'artist': [tokens[0]]}
else:
raise MpdArgError(
@ -224,6 +234,8 @@ def _list_build_query(field, mpd_query):
tokens = tokens[2:]
if key not in ('artist', 'album', 'date', 'genre'):
raise MpdArgError('not able to parse args', command='list')
if not value:
raise ValueError
if key in query:
query[key].append(value)
else:
@ -235,8 +247,8 @@ def _list_build_query(field, mpd_query):
def _list_artist(context, query):
artists = set()
playlist = context.core.library.find_exact(**query).get()
for track in playlist.tracks:
tracks = context.core.library.find_exact(**query).get()
for track in tracks:
for artist in track.artists:
artists.add(('Artist', artist.name))
return artists
@ -244,8 +256,8 @@ def _list_artist(context, query):
def _list_album(context, query):
albums = set()
playlist = context.core.library.find_exact(**query).get()
for track in playlist.tracks:
tracks = context.core.library.find_exact(**query).get()
for track in tracks:
if track.album is not None:
albums.add(('Album', track.album.name))
return albums
@ -253,8 +265,8 @@ def _list_album(context, query):
def _list_date(context, query):
dates = set()
playlist = context.core.library.find_exact(**query).get()
for track in playlist.tracks:
tracks = context.core.library.find_exact(**query).get()
for track in tracks:
if track.date is not None:
dates.add(('Date', track.date))
return dates
@ -351,8 +363,11 @@ def search(context, mpd_query):
- also uses the search type "date".
- uses "file" instead of "filename".
"""
query = _build_query(mpd_query)
return playlist_to_mpd_format(
try:
query = _build_query(mpd_query)
except ValueError:
return
return tracks_to_mpd_format(
context.core.library.search(**query).get())

View File

@ -151,11 +151,10 @@ def playid(context, tlid):
tlid = int(tlid)
if tlid == -1:
return _play_minus_one(context)
try:
tl_track = context.core.tracklist.get(tlid=tlid).get()
return context.core.playback.play(tl_track).get()
except LookupError:
tl_tracks = context.core.tracklist.filter(tlid=tlid).get()
if not tl_tracks:
raise MpdNoExistError('No such song', command='playid')
return context.core.playback.play(tl_tracks[0]).get()
@handle_request(r'^play (?P<songpos>-?\d+)$')

View File

@ -23,11 +23,10 @@ def listplaylist(context, name):
file: relative/path/to/file2.ogg
file: relative/path/to/file3.mp3
"""
try:
playlist = context.core.playlists.get(name=name).get()
return ['file: %s' % t.uri for t in playlist.tracks]
except LookupError:
playlists = context.core.playlists.filter(name=name).get()
if not playlists:
raise MpdNoExistError('No such playlist', command='listplaylist')
return ['file: %s' % t.uri for t in playlists[0].tracks]
@handle_request(r'^listplaylistinfo (?P<name>\S+)$')
@ -45,11 +44,10 @@ def listplaylistinfo(context, name):
Standard track listing, with fields: file, Time, Title, Date,
Album, Artist, Track
"""
try:
playlist = context.core.playlists.get(name=name).get()
return playlist_to_mpd_format(playlist)
except LookupError:
playlists = context.core.playlists.filter(name=name).get()
if not playlists:
raise MpdNoExistError('No such playlist', command='listplaylistinfo')
return playlist_to_mpd_format(playlists[0])
@handle_request(r'^listplaylists$')
@ -100,11 +98,10 @@ def load(context, name):
- ``load`` appends the given playlist to the current playlist.
"""
try:
playlist = context.core.playlists.get(name=name).get()
context.core.tracklist.append(playlist.tracks)
except LookupError:
playlists = context.core.playlists.filter(name=name).get()
if not playlists:
raise MpdNoExistError('No such playlist', command='load')
context.core.tracklist.append(playlists[0].tracks)
@handle_request(r'^playlistadd "(?P<name>[^"]+)" "(?P<uri>[^"]+)"$')

View File

@ -279,13 +279,10 @@ class MprisObject(dbus.service.Object):
return
# NOTE Check if URI has MIME type known to the backend, if MIME support
# is added to the backend.
uri_schemes = self.core.uri_schemes.get()
if not any([uri.startswith(uri_scheme) for uri_scheme in uri_schemes]):
return
track = self.core.library.lookup(uri).get()
if track is not None:
tl_track = self.core.tracklist.add(track).get()
self.core.playback.play(tl_track)
tracks = self.core.library.lookup(uri).get()
if tracks:
tl_tracks = self.core.tracklist.append(tracks).get()
self.core.playback.play(tl_tracks[0])
else:
logger.debug('Track with URI "%s" not found in library.', uri)

View File

@ -14,7 +14,7 @@ class ImmutableObject(object):
def __init__(self, *args, **kwargs):
for key, value in kwargs.items():
if not hasattr(self, key):
if not hasattr(self, key) or callable(getattr(self, key)):
raise TypeError(
'__init__() got an unexpected keyword argument "%s"' %
key)
@ -80,7 +80,7 @@ class ImmutableObject(object):
def serialize(self):
data = {}
data['__type__'] = self.__class__.__name__
data['__model__'] = self.__class__.__name__
for key in self.__dict__.keys():
public_key = key.lstrip('_')
value = self.__dict__[key]
@ -101,7 +101,7 @@ class ModelJSONEncoder(json.JSONEncoder):
>>> import json
>>> json.dumps({'a_track': Track(name='name')}, cls=ModelJSONEncoder)
'{"a_track": {"__type__": "Track", "name": "name"}}'
'{"a_track": {"__model__": "Track", "name": "name"}}'
"""
def default(self, obj):
@ -118,23 +118,16 @@ def model_json_decoder(dct):
>>> import json
>>> json.loads(
... '{"a_track": {"__type__": "Track", "name": "name"}}',
... '{"a_track": {"__model__": "Track", "name": "name"}}',
... object_hook=model_json_decoder)
{u'a_track': Track(artists=[], name=u'name')}
"""
if '__type__' in dct:
obj_type = dct.pop('__type__')
if obj_type == 'Album':
return Album(**dct)
if obj_type == 'Artist':
return Artist(**dct)
if obj_type == 'Playlist':
return Playlist(**dct)
if obj_type == 'TlTrack':
return TlTrack(**dct)
if obj_type == 'Track':
return Track(**dct)
if '__model__' in dct:
model_name = dct.pop('__model__')
cls = globals().get(model_name, None)
if issubclass(cls, ImmutableObject):
return cls(**dct)
return dct

View File

@ -317,7 +317,7 @@ class LineProtocol(pykka.ThreadingActor):
super(LineProtocol, self).__init__()
self.connection = connection
self.prevent_timeout = False
self.recv_buffer = ''
self.recv_buffer = b''
if self.delimiter:
self.delimiter = re.compile(self.delimiter)

View File

@ -16,7 +16,8 @@ from mopidy import exceptions
logger = logging.getLogger('mopidy.utils.process')
SIGNALS = dict((k, v) for v, k in signal.__dict__.iteritems()
SIGNALS = dict(
(k, v) for v, k in signal.__dict__.iteritems()
if v.startswith('SIG') and not v.startswith('SIG_'))
@ -98,7 +99,8 @@ class DebugThread(threading.Thread):
for ident, frame in sys._current_frames().items():
if self.ident != ident:
stack = ''.join(traceback.format_stack(frame))
logger.debug('Current state of %s (%s):\n%s',
logger.debug(
'Current state of %s (%s):\n%s',
threads[ident], ident, stack)
del frame

View File

@ -3,7 +3,7 @@ from __future__ import unicode_literals
import pykka
from mopidy import core
from mopidy.models import Playlist, Track, Album, Artist
from mopidy.models import Track, Album, Artist
from tests import unittest, path_to_data_dir
@ -43,52 +43,52 @@ class LibraryControllerTest(object):
pass
def test_lookup(self):
track = self.library.lookup(self.tracks[0].uri)
self.assertEqual(track, self.tracks[0])
tracks = self.library.lookup(self.tracks[0].uri)
self.assertEqual(tracks, self.tracks[0:1])
def test_lookup_unknown_track(self):
track = self.library.lookup('fake uri')
self.assertEquals(track, None)
tracks = self.library.lookup('fake uri')
self.assertEqual(tracks, [])
def test_find_exact_no_hits(self):
result = self.library.find_exact(track=['unknown track'])
self.assertEqual(result, Playlist())
self.assertEqual(result, [])
result = self.library.find_exact(artist=['unknown artist'])
self.assertEqual(result, Playlist())
self.assertEqual(result, [])
result = self.library.find_exact(album=['unknown artist'])
self.assertEqual(result, Playlist())
self.assertEqual(result, [])
def test_find_exact_artist(self):
result = self.library.find_exact(artist=['artist1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.find_exact(artist=['artist2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
self.assertEqual(result, self.tracks[1:2])
def test_find_exact_track(self):
result = self.library.find_exact(track=['track1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.find_exact(track=['track2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
self.assertEqual(result, self.tracks[1:2])
def test_find_exact_album(self):
result = self.library.find_exact(album=['album1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.find_exact(album=['album2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
self.assertEqual(result, self.tracks[1:2])
def test_find_exact_uri(self):
track_1_uri = 'file://' + path_to_data_dir('uri1')
result = self.library.find_exact(uri=track_1_uri)
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
track_2_uri = 'file://' + path_to_data_dir('uri2')
result = self.library.find_exact(uri=track_2_uri)
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
self.assertEqual(result, self.tracks[1:2])
def test_find_exact_wrong_type(self):
test = lambda: self.library.find_exact(wrong=['test'])
@ -106,57 +106,57 @@ class LibraryControllerTest(object):
def test_search_no_hits(self):
result = self.library.search(track=['unknown track'])
self.assertEqual(result, Playlist())
self.assertEqual(result, [])
result = self.library.search(artist=['unknown artist'])
self.assertEqual(result, Playlist())
self.assertEqual(result, [])
result = self.library.search(album=['unknown artist'])
self.assertEqual(result, Playlist())
self.assertEqual(result, [])
result = self.library.search(uri=['unknown'])
self.assertEqual(result, Playlist())
self.assertEqual(result, [])
result = self.library.search(any=['unknown'])
self.assertEqual(result, Playlist())
self.assertEqual(result, [])
def test_search_artist(self):
result = self.library.search(artist=['Tist1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.search(artist=['Tist2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
self.assertEqual(result, self.tracks[1:2])
def test_search_track(self):
result = self.library.search(track=['Rack1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.search(track=['Rack2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
self.assertEqual(result, self.tracks[1:2])
def test_search_album(self):
result = self.library.search(album=['Bum1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.search(album=['Bum2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
self.assertEqual(result, self.tracks[1:2])
def test_search_uri(self):
result = self.library.search(uri=['RI1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.search(uri=['RI2'])
self.assertEqual(result, Playlist(tracks=self.tracks[1:2]))
self.assertEqual(result, self.tracks[1:2])
def test_search_any(self):
result = self.library.search(any=['Tist1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.search(any=['Rack1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.search(any=['Bum1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
result = self.library.search(any=['RI1'])
self.assertEqual(result, Playlist(tracks=self.tracks[:1]))
self.assertEqual(result, self.tracks[:1])
def test_search_wrong_type(self):
test = lambda: self.library.search(wrong=['test'])

View File

@ -58,43 +58,35 @@ class PlaylistsControllerTest(object):
self.assertNotIn(playlist, self.core.playlists.playlists)
def test_get_without_criteria(self):
test = self.core.playlists.get
self.assertRaises(LookupError, test)
def test_filter_without_criteria(self):
self.assertEqual(
self.core.playlists.playlists, self.core.playlists.filter())
def test_get_with_wrong_cirteria(self):
test = lambda: self.core.playlists.get(name='foo')
self.assertRaises(LookupError, test)
def test_filter_with_wrong_criteria(self):
self.assertEqual([], self.core.playlists.filter(name='foo'))
def test_get_with_right_criteria(self):
playlist1 = self.core.playlists.create('test')
playlist2 = self.core.playlists.get(name='test')
self.assertEqual(playlist1, playlist2)
def test_filter_with_right_criteria(self):
playlist = self.core.playlists.create('test')
playlists = self.core.playlists.filter(name='test')
self.assertEqual([playlist], playlists)
def test_get_by_name_returns_unique_match(self):
def test_filter_by_name_returns_single_match(self):
playlist = Playlist(name='b')
self.backend.playlists.playlists = [
Playlist(name='a'), playlist]
self.assertEqual(playlist, self.core.playlists.get(name='b'))
self.backend.playlists.playlists = [Playlist(name='a'), playlist]
self.assertEqual([playlist], self.core.playlists.filter(name='b'))
def test_get_by_name_returns_first_of_multiple_matches(self):
def test_filter_by_name_returns_multiple_matches(self):
playlist = Playlist(name='b')
self.backend.playlists.playlists = [
playlist, Playlist(name='a'), Playlist(name='b')]
try:
self.core.playlists.get(name='b')
self.fail('Should raise LookupError if multiple matches')
except LookupError as e:
self.assertEqual('"name=b" match multiple playlists', e[0])
playlists = self.core.playlists.filter(name='b')
self.assertIn(playlist, playlists)
self.assertEqual(2, len(playlists))
def test_get_by_name_raises_keyerror_if_no_match(self):
def test_filter_by_name_returns_no_matches(self):
self.backend.playlists.playlists = [
Playlist(name='a'), Playlist(name='b')]
try:
self.core.playlists.get(name='c')
self.fail('Should raise LookupError if no match')
except LookupError as e:
self.assertEqual('"name=c" match no playlists', e[0])
self.assertEqual([], self.core.playlists.filter(name='c'))
def test_lookup_finds_playlist_by_uri(self):
original_playlist = self.core.playlists.create('test')

View File

@ -55,19 +55,56 @@ class TracklistControllerTest(object):
self.assertRaises(AssertionError, test)
@populate_playlist
def test_get_by_tlid(self):
def test_filter_by_tlid(self):
tl_track = self.controller.tl_tracks[1]
self.assertEqual(tl_track, self.controller.get(tlid=tl_track.tlid))
self.assertEqual(
[tl_track], self.controller.filter(tlid=tl_track.tlid))
@populate_playlist
def test_get_by_uri(self):
def test_filter_by_uri(self):
tl_track = self.controller.tl_tracks[1]
self.assertEqual(tl_track, self.controller.get(uri=tl_track.track.uri))
self.assertEqual(
[tl_track], self.controller.filter(uri=tl_track.track.uri))
@populate_playlist
def test_get_by_uri_raises_error_for_invalid_uri(self):
test = lambda: self.controller.get(uri='foobar')
self.assertRaises(LookupError, test)
def test_filter_by_uri_returns_nothing_for_invalid_uri(self):
self.assertEqual([], self.controller.filter(uri='foobar'))
def test_filter_by_uri_returns_single_match(self):
track = Track(uri='a')
self.controller.append([Track(uri='z'), track, Track(uri='y')])
self.assertEqual(track, self.controller.filter(uri='a')[0].track)
def test_filter_by_uri_returns_multiple_matches(self):
track = Track(uri='a')
self.controller.append([Track(uri='z'), track, track])
tl_tracks = self.controller.filter(uri='a')
self.assertEqual(track, tl_tracks[0].track)
self.assertEqual(track, tl_tracks[1].track)
def test_filter_by_uri_returns_nothing_if_no_match(self):
self.controller.playlist = Playlist(
tracks=[Track(uri='z'), Track(uri='y')])
self.assertEqual([], self.controller.filter(uri='a'))
def test_filter_by_multiple_criteria_returns_elements_matching_all(self):
track1 = Track(uri='a', name='x')
track2 = Track(uri='b', name='x')
track3 = Track(uri='b', name='y')
self.controller.append([track1, track2, track3])
self.assertEqual(
track1, self.controller.filter(uri='a', name='x')[0].track)
self.assertEqual(
track2, self.controller.filter(uri='b', name='x')[0].track)
self.assertEqual(
track3, self.controller.filter(uri='b', name='y')[0].track)
def test_filter_by_criteria_that_is_not_present_in_all_elements(self):
track1 = Track()
track2 = Track(uri='b')
track3 = Track()
self.controller.append([track1, track2, track3])
self.assertEqual(track2, self.controller.filter(uri='b')[0].track)
@populate_playlist
def test_clear(self):
@ -85,45 +122,6 @@ class TracklistControllerTest(object):
self.controller.clear()
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
def test_get_by_uri_returns_unique_match(self):
track = Track(uri='a')
self.controller.append([Track(uri='z'), track, Track(uri='y')])
self.assertEqual(track, self.controller.get(uri='a').track)
def test_get_by_uri_raises_error_if_multiple_matches(self):
track = Track(uri='a')
self.controller.append([Track(uri='z'), track, track])
try:
self.controller.get(uri='a')
self.fail('Should raise LookupError if multiple matches')
except LookupError as e:
self.assertEqual('"uri=a" match multiple tracks', e[0])
def test_get_by_uri_raises_error_if_no_match(self):
self.controller.playlist = Playlist(
tracks=[Track(uri='z'), Track(uri='y')])
try:
self.controller.get(uri='a')
self.fail('Should raise LookupError if no match')
except LookupError as e:
self.assertEqual('"uri=a" match no tracks', e[0])
def test_get_by_multiple_criteria_returns_elements_matching_all(self):
track1 = Track(uri='a', name='x')
track2 = Track(uri='b', name='x')
track3 = Track(uri='b', name='y')
self.controller.append([track1, track2, track3])
self.assertEqual(track1, self.controller.get(uri='a', name='x').track)
self.assertEqual(track2, self.controller.get(uri='b', name='x').track)
self.assertEqual(track3, self.controller.get(uri='b', name='y').track)
def test_get_by_criteria_that_is_not_present_in_all_elements(self):
track1 = Track()
track2 = Track(uri='b')
track3 = Track()
self.controller.append([track1, track2, track3])
self.assertEqual(track2, self.controller.get(uri='b').track)
def test_append_appends_to_the_tracklist(self):
self.controller.append([Track(uri='a'), Track(uri='b')])
self.assertEqual(len(self.controller.tracks), 2)
@ -222,13 +220,11 @@ class TracklistControllerTest(object):
self.assertEqual(track2, self.controller.tracks[1])
@populate_playlist
def test_removing_track_that_does_not_exist(self):
test = lambda: self.controller.remove(uri='/nonexistant')
self.assertRaises(LookupError, test)
def test_removing_track_that_does_not_exist_does_nothing(self):
self.controller.remove(uri='/nonexistant')
def test_removing_from_empty_playlist(self):
test = lambda: self.controller.remove(uri='/nonexistant')
self.assertRaises(LookupError, test)
def test_removing_from_empty_playlist_does_nothing(self):
self.controller.remove(uri='/nonexistant')
@populate_playlist
def test_shuffle(self):

View File

@ -4,7 +4,7 @@ import mock
from mopidy.backends import base
from mopidy.core import Core
from mopidy.models import Playlist, Track
from mopidy.models import Track
from tests import unittest
@ -41,10 +41,10 @@ class CoreLibraryTest(unittest.TestCase):
self.assertFalse(self.library1.lookup.called)
self.library2.lookup.assert_called_once_with('dummy2:a')
def test_lookup_fails_for_dummy3_track(self):
def test_lookup_returns_nothing_for_dummy3_track(self):
result = self.core.library.lookup('dummy3:a')
self.assertIsNone(result)
self.assertEqual(result, [])
self.assertFalse(self.library1.lookup.called)
self.assertFalse(self.library2.lookup.called)
@ -75,29 +75,29 @@ class CoreLibraryTest(unittest.TestCase):
def test_find_exact_combines_results_from_all_backends(self):
track1 = Track(uri='dummy1:a')
track2 = Track(uri='dummy2:a')
self.library1.find_exact().get.return_value = Playlist(tracks=[track1])
self.library1.find_exact().get.return_value = [track1]
self.library1.find_exact.reset_mock()
self.library2.find_exact().get.return_value = Playlist(tracks=[track2])
self.library2.find_exact().get.return_value = [track2]
self.library2.find_exact.reset_mock()
result = self.core.library.find_exact(any=['a'])
self.assertIn(track1, result.tracks)
self.assertIn(track2, result.tracks)
self.assertIn(track1, result)
self.assertIn(track2, result)
self.library1.find_exact.assert_called_once_with(any=['a'])
self.library2.find_exact.assert_called_once_with(any=['a'])
def test_search_combines_results_from_all_backends(self):
track1 = Track(uri='dummy1:a')
track2 = Track(uri='dummy2:a')
self.library1.search().get.return_value = Playlist(tracks=[track1])
self.library1.search().get.return_value = [track1]
self.library1.search.reset_mock()
self.library2.search().get.return_value = Playlist(tracks=[track2])
self.library2.search().get.return_value = [track2]
self.library2.search.reset_mock()
result = self.core.library.search(any=['a'])
self.assertIn(track1, result.tracks)
self.assertIn(track2, result.tracks)
self.assertIn(track1, result)
self.assertIn(track2, result)
self.library1.search.assert_called_once_with(any=['a'])
self.library2.search.assert_called_once_with(any=['a'])

View File

@ -58,8 +58,8 @@ class CorePlaybackTest(unittest.TestCase):
self.playback1.play.assert_called_once_with(self.tracks[3])
self.assertFalse(self.playback2.play.called)
self.assertEqual(self.core.playback.current_tl_track,
self.tl_tracks[3])
self.assertEqual(
self.core.playback.current_tl_track, self.tl_tracks[3])
def test_pause_selects_dummy1_backend(self):
self.core.playback.play(self.tl_tracks[0])

View File

@ -214,6 +214,11 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertEqual(tracks[5].name, 'f')
self.assertInResponse('OK')
def test_moveid_with_tlid_not_found_in_tracklist_should_ack(self):
self.sendRequest('moveid "9" "0"')
self.assertEqualResponse(
'ACK [50@0] {moveid} No such song')
def test_playlist_returns_same_as_playlistinfo(self):
playlist_response = self.sendRequest('playlist')
playlistinfo_response = self.sendRequest('playlistinfo')
@ -505,3 +510,15 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertEqual(tracks[4].name, 'b')
self.assertEqual(tracks[5].name, 'f')
self.assertInResponse('OK')
def test_swapid_with_first_id_unknown_should_ack(self):
self.core.tracklist.append([Track()])
self.sendRequest('swapid "0" "4"')
self.assertEqualResponse(
'ACK [50@0] {swapid} No such song')
def test_swapid_with_second_id_unknown_should_ack(self):
self.core.tracklist.append([Track()])
self.sendRequest('swapid "4" "0"')
self.assertEqualResponse(
'ACK [50@0] {swapid} No such song')

View File

@ -236,8 +236,8 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.sendRequest('play "-1"')
self.assertEqual(PLAYING, self.core.playback.state.get())
self.assertEqual('dummy:a',
self.core.playback.current_track.get().uri)
self.assertEqual(
'dummy:a', self.core.playback.current_track.get().uri)
self.assertInResponse('OK')
def test_play_minus_one_plays_current_track_if_current_track_is_set(self):
@ -253,8 +253,8 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.sendRequest('play "-1"')
self.assertEqual(PLAYING, self.core.playback.state.get())
self.assertEqual('dummy:b',
self.core.playback.current_track.get().uri)
self.assertEqual(
'dummy:b', self.core.playback.current_track.get().uri)
self.assertInResponse('OK')
def test_play_minus_one_on_empty_playlist_does_not_ack(self):
@ -318,8 +318,8 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.sendRequest('playid "-1"')
self.assertEqual(PLAYING, self.core.playback.state.get())
self.assertEqual('dummy:a',
self.core.playback.current_track.get().uri)
self.assertEqual(
'dummy:a', self.core.playback.current_track.get().uri)
self.assertInResponse('OK')
def test_playid_minus_1_plays_current_track_if_current_track_is_set(self):
@ -335,8 +335,8 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.sendRequest('playid "-1"')
self.assertEqual(PLAYING, self.core.playback.state.get())
self.assertEqual('dummy:b',
self.core.playback.current_track.get().uri)
self.assertEqual(
'dummy:b', self.core.playback.current_track.get().uri)
self.assertInResponse('OK')
def test_playid_minus_one_on_empty_playlist_does_not_ack(self):

View File

@ -29,22 +29,22 @@ class IssueGH17RegressionTest(protocol.BaseTestCase):
random.seed(1) # Playlist order: abcfde
self.sendRequest('play')
self.assertEquals('dummy:a',
self.core.playback.current_track.get().uri)
self.assertEquals(
'dummy:a', self.core.playback.current_track.get().uri)
self.sendRequest('random "1"')
self.sendRequest('next')
self.assertEquals('dummy:b',
self.core.playback.current_track.get().uri)
self.assertEquals(
'dummy:b', self.core.playback.current_track.get().uri)
self.sendRequest('next')
# Should now be at track 'c', but playback fails and it skips ahead
self.assertEquals('dummy:f',
self.core.playback.current_track.get().uri)
self.assertEquals(
'dummy:f', self.core.playback.current_track.get().uri)
self.sendRequest('next')
self.assertEquals('dummy:d',
self.core.playback.current_track.get().uri)
self.assertEquals(
'dummy:d', self.core.playback.current_track.get().uri)
self.sendRequest('next')
self.assertEquals('dummy:e',
self.core.playback.current_track.get().uri)
self.assertEquals(
'dummy:e', self.core.playback.current_track.get().uri)
class IssueGH18RegressionTest(protocol.BaseTestCase):

View File

@ -812,23 +812,20 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_open_uri_ignores_uris_with_unknown_uri_scheme(self):
self.assertListEqual(self.core.uri_schemes.get(), ['dummy'])
self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [
Track(uri='notdummy:/test/uri')]
self.backend.library.dummy_library = [Track(uri='notdummy:/test/uri')]
self.mpris.OpenUri('notdummy:/test/uri')
self.assertEqual(len(self.core.tracklist.tracks.get()), 0)
def test_open_uri_adds_uri_to_tracklist(self):
self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [
Track(uri='dummy:/test/uri')]
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.mpris.OpenUri('dummy:/test/uri')
self.assertEqual(
self.core.tracklist.tracks.get()[0].uri, 'dummy:/test/uri')
def test_open_uri_starts_playback_of_new_track_if_stopped(self):
self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [
Track(uri='dummy:/test/uri')]
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.assertEqual(self.core.playback.state.get(), STOPPED)
@ -841,8 +838,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_open_uri_starts_playback_of_new_track_if_paused(self):
self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [
Track(uri='dummy:/test/uri')]
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
@ -858,8 +854,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_open_uri_starts_playback_of_new_track_if_playing(self):
self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [
Track(uri='dummy:/test/uri')]
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()

View File

@ -79,6 +79,13 @@ class ArtistTest(unittest.TestCase):
test = lambda: Artist(foo='baz')
self.assertRaises(TypeError, test)
def test_invalid_kwarg_with_name_matching_method(self):
test = lambda: Artist(copy='baz')
self.assertRaises(TypeError, test)
test = lambda: Artist(serialize='baz')
self.assertRaises(TypeError, test)
def test_repr(self):
self.assertEquals(
"Artist(name=u'name', uri=u'uri')",
@ -86,15 +93,36 @@ class ArtistTest(unittest.TestCase):
def test_serialize(self):
self.assertDictEqual(
{'__type__': 'Artist', 'uri': 'uri', 'name': 'name'},
{'__model__': 'Artist', 'uri': 'uri', 'name': 'name'},
Artist(uri='uri', name='name').serialize())
def test_to_json_and_Back(self):
def test_to_json_and_back(self):
artist1 = Artist(uri='uri', name='name')
serialized = json.dumps(artist1, cls=ModelJSONEncoder)
artist2 = json.loads(serialized, object_hook=model_json_decoder)
self.assertEqual(artist1, artist2)
def test_to_json_and_back_with_unknown_field(self):
artist = Artist(uri='uri', name='name').serialize()
artist['foo'] = 'foo'
serialized = json.dumps(artist)
test = lambda: json.loads(serialized, object_hook=model_json_decoder)
self.assertRaises(TypeError, test)
def test_to_json_and_back_with_field_matching_method(self):
artist = Artist(uri='uri', name='name').serialize()
artist['copy'] = 'foo'
serialized = json.dumps(artist)
test = lambda: json.loads(serialized, object_hook=model_json_decoder)
self.assertRaises(TypeError, test)
def test_to_json_and_back_with_field_matching_internal_field(self):
artist = Artist(uri='uri', name='name').serialize()
artist['__mro__'] = 'foo'
serialized = json.dumps(artist)
test = lambda: json.loads(serialized, object_hook=model_json_decoder)
self.assertRaises(TypeError, test)
def test_eq_name(self):
artist1 = Artist(name='name')
artist2 = Artist(name='name')
@ -204,14 +232,14 @@ class AlbumTest(unittest.TestCase):
def test_serialize_without_artists(self):
self.assertDictEqual(
{'__type__': 'Album', 'uri': 'uri', 'name': 'name'},
{'__model__': 'Album', 'uri': 'uri', 'name': 'name'},
Album(uri='uri', name='name').serialize())
def test_serialize_with_artists(self):
artist = Artist(name='foo')
self.assertDictEqual(
{'__type__': 'Album', 'uri': 'uri', 'name': 'name', 'artists':
[artist.serialize()]},
{'__model__': 'Album', 'uri': 'uri', 'name': 'name',
'artists': [artist.serialize()]},
Album(uri='uri', name='name', artists=[artist]).serialize())
def test_to_json_and_back(self):
@ -402,20 +430,20 @@ class TrackTest(unittest.TestCase):
def test_serialize_without_artists(self):
self.assertDictEqual(
{'__type__': 'Track', 'uri': 'uri', 'name': 'name'},
{'__model__': 'Track', 'uri': 'uri', 'name': 'name'},
Track(uri='uri', name='name').serialize())
def test_serialize_with_artists(self):
artist = Artist(name='foo')
self.assertDictEqual(
{'__type__': 'Track', 'uri': 'uri', 'name': 'name',
{'__model__': 'Track', 'uri': 'uri', 'name': 'name',
'artists': [artist.serialize()]},
Track(uri='uri', name='name', artists=[artist]).serialize())
def test_serialize_with_album(self):
album = Album(name='foo')
self.assertDictEqual(
{'__type__': 'Track', 'uri': 'uri', 'name': 'name',
{'__model__': 'Track', 'uri': 'uri', 'name': 'name',
'album': album.serialize()},
Track(uri='uri', name='name', album=album).serialize())
@ -618,7 +646,7 @@ class TlTrackTest(unittest.TestCase):
def test_serialize(self):
track = Track(uri='uri', name='name')
self.assertDictEqual(
{'__type__': 'TlTrack', 'tlid': 123, 'track': track.serialize()},
{'__model__': 'TlTrack', 'tlid': 123, 'track': track.serialize()},
TlTrack(tlid=123, track=track).serialize())
def test_to_json_and_back(self):
@ -752,13 +780,13 @@ class PlaylistTest(unittest.TestCase):
def test_serialize_without_tracks(self):
self.assertDictEqual(
{'__type__': 'Playlist', 'uri': 'uri', 'name': 'name'},
{'__model__': 'Playlist', 'uri': 'uri', 'name': 'name'},
Playlist(uri='uri', name='name').serialize())
def test_serialize_with_tracks(self):
track = Track(name='foo')
self.assertDictEqual(
{'__type__': 'Playlist', 'uri': 'uri', 'name': 'name',
{'__model__': 'Playlist', 'uri': 'uri', 'name': 'name',
'tracks': [track.serialize()]},
Playlist(uri='uri', name='name', tracks=[track]).serialize())