Use cp_track in CurrentPlaylistCtl and PlaybackCtl
A new data structure called ``cp_track`` is now used in the current playlist controller and the playback controller. A ``cp_track`` is a two-tuple of (CPID integer, mopidy.models.Track), identifying an instance of a track uniquely within the current playlist. This fixes issues with using playlists with multiple instances of the same track.
This commit is contained in:
parent
8575c33227
commit
64544a0b71
@ -38,16 +38,35 @@ We got an updated :doc:`release roadmap <development/roadmap>`!
|
||||
|
||||
- Backend API:
|
||||
|
||||
- A new data structure called ``cp_track`` is now used in the current
|
||||
playlist controller and the playback controller. A ``cp_track`` is a
|
||||
two-tuple of (CPID integer, :class:`mopidy.models.Track`), identifying an
|
||||
instance of a track uniquely within the current playlist.
|
||||
- :meth:`mopidy.backends.BaseCurrentPlaylistController.load()` now accepts
|
||||
lists of :class:`mopidy.models.Track` instead of
|
||||
:class:`mopidy.models.Playlist`, as none of the other fields on the
|
||||
``Playlist`` model was in use.
|
||||
- :meth:`mopidy.backends.BaseCurrentPlaylistController.remove()` now takes
|
||||
criterias, just like
|
||||
:meth:`mopidy.backends.BaseCurrentPlaylistController.get()`, and not the
|
||||
track to remove.
|
||||
:meth:`mopidy.backends.BaseCurrentPlaylistController.get()`.
|
||||
- :meth:`mopidy.backends.BaseCurrentPlaylistController.get()` now returns a
|
||||
``cp_track``.
|
||||
- :attr:`mopidy.backends.BaseCurrentPlaylistController.tracks` is now
|
||||
read-only. Use the methods to change its contents.
|
||||
- :attr:`mopidy.backends.BaseCurrentPlaylistController.cp_tracks` is a
|
||||
read-only list of ``cp_track``. Use the methods to change its contents.
|
||||
- :attr:`mopidy.backends.BasePlaybackController.current_track` is now
|
||||
just for convenience and read-only. To set the current track, assign a
|
||||
``cp_track`` to
|
||||
:attr:`mopidy.backends.BasePlaybackController.current_cp_track`.
|
||||
- :attr:`mopidy.backends.BasePlaybackController.current_cpid` is the
|
||||
read-only CPID of the current track.
|
||||
- :attr:`mopidy.backends.BasePlaybackController.next_cp_track` is the
|
||||
next ``cp_track`` in the playlist.
|
||||
- :attr:`mopidy.backends.BasePlaybackController.previous_cp_track` is
|
||||
the previous ``cp_track`` in the playlist.
|
||||
- :meth:`mopidy.backends.BasePlaybackController.play()` now takes a
|
||||
``cp_track``.
|
||||
|
||||
|
||||
0.1.0a2 (2010-06-02)
|
||||
|
||||
@ -95,6 +95,15 @@ class BaseCurrentPlaylistController(object):
|
||||
"""Cleanup after component."""
|
||||
pass
|
||||
|
||||
@property
|
||||
def cp_tracks(self):
|
||||
"""
|
||||
List of two-tuples of (CPID integer, :class:`mopidy.models.Track`).
|
||||
|
||||
Read-only.
|
||||
"""
|
||||
return [copy(ct) for ct in self._cp_tracks]
|
||||
|
||||
@property
|
||||
def tracks(self):
|
||||
"""
|
||||
@ -104,23 +113,6 @@ class BaseCurrentPlaylistController(object):
|
||||
"""
|
||||
return [ct[1] for ct in self._cp_tracks]
|
||||
|
||||
def _get_cp_track(self, **criteria):
|
||||
matches = self._cp_tracks
|
||||
for (key, value) in criteria.iteritems():
|
||||
if key == 'cpid':
|
||||
matches = filter(lambda ct: ct[0] == value, matches)
|
||||
else:
|
||||
matches = filter(lambda ct: getattr(ct[1], 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(u'"%s" match no tracks' % criteria_string)
|
||||
else:
|
||||
raise LookupError(u'"%s" match multiple tracks' % criteria_string)
|
||||
|
||||
def add(self, track, at_position=None):
|
||||
"""
|
||||
Add the track to the end of, or at the given position in the current
|
||||
@ -142,7 +134,7 @@ class BaseCurrentPlaylistController(object):
|
||||
def clear(self):
|
||||
"""Clear the current playlist."""
|
||||
self.backend.playback.stop()
|
||||
self.backend.playback.current_track = None
|
||||
self.backend.playback.current_cp_track = None
|
||||
self._cp_tracks = []
|
||||
self.version += 1
|
||||
|
||||
@ -162,9 +154,23 @@ class BaseCurrentPlaylistController(object):
|
||||
|
||||
:param criteria: on or more criteria to match by
|
||||
:type criteria: dict
|
||||
:rtype: :class:`mopidy.models.Track`
|
||||
:rtype: two-tuple (CPID integer, :class:`mopidy.models.Track`)
|
||||
"""
|
||||
return self._get_cp_track(**criteria)[1]
|
||||
matches = self._cp_tracks
|
||||
for (key, value) in criteria.iteritems():
|
||||
if key == 'cpid':
|
||||
matches = filter(lambda ct: ct[0] == value, matches)
|
||||
else:
|
||||
matches = filter(lambda ct: getattr(ct[1], 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(u'"%s" match no tracks' % criteria_string)
|
||||
else:
|
||||
raise LookupError(u'"%s" match multiple tracks' % criteria_string)
|
||||
|
||||
def load(self, tracks):
|
||||
"""
|
||||
@ -214,13 +220,13 @@ class BaseCurrentPlaylistController(object):
|
||||
"""
|
||||
Remove the track from the current playlist.
|
||||
|
||||
Uses :meth:`get` to lookup the track to remove.
|
||||
Uses :meth:`get()` to lookup the track to remove.
|
||||
|
||||
:param criteria: on or more criteria to match by
|
||||
:type criteria: dict
|
||||
:type track: :class:`mopidy.models.Track`
|
||||
"""
|
||||
cp_track = self._get_cp_track(**criteria)
|
||||
cp_track = self.get(**criteria)
|
||||
position = self._cp_tracks.index(cp_track)
|
||||
del self._cp_tracks[position]
|
||||
self.version += 1
|
||||
@ -338,11 +344,11 @@ class BasePlaybackController(object):
|
||||
#: Tracks are not removed from the playlist.
|
||||
consume = False
|
||||
|
||||
#: The CPID (current playlist ID) of :attr:`current_track`.
|
||||
current_cpid = 0 # TODO Get the correct CPID
|
||||
|
||||
#: The currently playing or selected :class:`mopidy.models.Track`.
|
||||
current_track = None
|
||||
#: The currently playing or selected track
|
||||
#:
|
||||
#: A two-tuple of (CPID integer, :class:`mopidy.models.Track`) or
|
||||
#: :class:`None`.
|
||||
current_cp_track = None
|
||||
|
||||
#: :class:`True`
|
||||
#: Tracks are selected at random from the playlist.
|
||||
@ -375,69 +381,122 @@ class BasePlaybackController(object):
|
||||
"""Cleanup after component."""
|
||||
pass
|
||||
|
||||
@property
|
||||
def current_cpid(self):
|
||||
"""
|
||||
The CPID (current playlist ID) of :attr:`current_track`.
|
||||
|
||||
Read-only. Extracted from :attr:`current_cp_track` for convenience.
|
||||
"""
|
||||
if self.current_cp_track is None:
|
||||
return None
|
||||
return self.current_cp_track[0]
|
||||
|
||||
@property
|
||||
def current_track(self):
|
||||
"""
|
||||
The currently playing or selected :class:`mopidy.models.Track`.
|
||||
|
||||
Read-only. Extracted from :attr:`current_cp_track` for convenience.
|
||||
"""
|
||||
if self.current_cp_track is None:
|
||||
return None
|
||||
return self.current_cp_track[1]
|
||||
|
||||
@property
|
||||
def current_playlist_position(self):
|
||||
"""The position of the current track in the current playlist."""
|
||||
if self.current_track is None:
|
||||
if self.current_cp_track is None:
|
||||
return None
|
||||
try:
|
||||
return self.backend.current_playlist.tracks.index(
|
||||
self.current_track)
|
||||
return self.backend.current_playlist.cp_tracks.index(
|
||||
self.current_cp_track)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def next_track(self):
|
||||
"""
|
||||
The next :class:`mopidy.models.Track` in the playlist.
|
||||
The next track in the playlist.
|
||||
|
||||
A :class:`mopidy.models.Track` extracted from :attr:`next_cp_track` for
|
||||
convenience.
|
||||
"""
|
||||
next_cp_track = self.next_cp_track
|
||||
if next_cp_track is None:
|
||||
return None
|
||||
return next_cp_track[1]
|
||||
|
||||
@property
|
||||
def next_cp_track(self):
|
||||
"""
|
||||
The next track in the playlist.
|
||||
|
||||
A two-tuple of (CPID integer, :class:`mopidy.models.Track`).
|
||||
|
||||
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
|
||||
enabled this should be a random track, all tracks should be played once
|
||||
before the list repeats.
|
||||
"""
|
||||
tracks = self.backend.current_playlist.tracks
|
||||
cp_tracks = self.backend.current_playlist.cp_tracks
|
||||
|
||||
if not tracks:
|
||||
if not cp_tracks:
|
||||
return None
|
||||
|
||||
if self.random and not self._shuffled:
|
||||
if self.repeat or self._first_shuffle:
|
||||
logger.debug('Shuffling tracks')
|
||||
self._shuffled = tracks
|
||||
self._shuffled = cp_tracks
|
||||
random.shuffle(self._shuffled)
|
||||
self._first_shuffle = False
|
||||
|
||||
if self._shuffled:
|
||||
return self._shuffled[0]
|
||||
|
||||
if self.current_track is None:
|
||||
return tracks[0]
|
||||
if self.current_cp_track is None:
|
||||
return cp_tracks[0]
|
||||
|
||||
if self.repeat:
|
||||
return tracks[(self.current_playlist_position + 1) % len(tracks)]
|
||||
return cp_tracks[
|
||||
(self.current_playlist_position + 1) % len(cp_tracks)]
|
||||
|
||||
try:
|
||||
return tracks[self.current_playlist_position + 1]
|
||||
return cp_tracks[self.current_playlist_position + 1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def previous_track(self):
|
||||
"""
|
||||
The previous :class:`mopidy.models.Track` in the playlist.
|
||||
The previous track in the playlist.
|
||||
|
||||
A :class:`mopidy.models.Track` extracted from :attr:`previous_cp_track`
|
||||
for convenience.
|
||||
"""
|
||||
previous_cp_track = self.previous_cp_track
|
||||
if previous_cp_track is None:
|
||||
return None
|
||||
return previous_cp_track[1]
|
||||
|
||||
@property
|
||||
def previous_cp_track(self):
|
||||
"""
|
||||
The previous track in the playlist.
|
||||
|
||||
A two-tuple of (CPID integer, :class:`mopidy.models.Track`).
|
||||
|
||||
For normal playback this is the previous track in the playlist. If
|
||||
random and/or consume is enabled it should return the current track
|
||||
instead.
|
||||
"""
|
||||
if self.repeat or self.consume or self.random:
|
||||
return self.current_track
|
||||
return self.current_cp_track
|
||||
|
||||
if self.current_track is None or self.current_playlist_position == 0:
|
||||
if self.current_cp_track is None or self.current_playlist_position == 0:
|
||||
return None
|
||||
|
||||
return self.backend.current_playlist.tracks[
|
||||
return self.backend.current_playlist.cp_tracks[
|
||||
self.current_playlist_position - 1]
|
||||
|
||||
@property
|
||||
@ -507,11 +566,11 @@ class BasePlaybackController(object):
|
||||
Typically called by :class:`mopidy.process.CoreProcess` after a message
|
||||
from a library thread is received.
|
||||
"""
|
||||
if self.next_track is not None:
|
||||
if self.next_cp_track is not None:
|
||||
self.next()
|
||||
else:
|
||||
self.stop()
|
||||
self.current_track = None
|
||||
self.current_cp_track = None
|
||||
|
||||
def new_playlist_loaded_callback(self):
|
||||
"""
|
||||
@ -520,7 +579,7 @@ class BasePlaybackController(object):
|
||||
Typically called by :class:`mopidy.process.CoreProcess` after a message
|
||||
from a library thread is received.
|
||||
"""
|
||||
self.current_track = None
|
||||
self.current_cp_track = None
|
||||
self._first_shuffle = True
|
||||
self._shuffled = []
|
||||
|
||||
@ -534,23 +593,23 @@ class BasePlaybackController(object):
|
||||
|
||||
def next(self):
|
||||
"""Play the next track."""
|
||||
original_track = self.current_track
|
||||
original_cp_track = self.current_cp_track
|
||||
|
||||
if self.state == self.STOPPED:
|
||||
return
|
||||
elif self.next_track is not None and self._next(self.next_track):
|
||||
self.current_track = self.next_track
|
||||
elif self.next_cp_track is not None and self._next(self.next_track):
|
||||
self.current_cp_track = self.next_cp_track
|
||||
self.state = self.PLAYING
|
||||
elif self.next_track is None:
|
||||
elif self.next_cp_track is None:
|
||||
self.stop()
|
||||
self.current_track = None
|
||||
self.current_cp_track = None
|
||||
|
||||
# FIXME handle in play aswell?
|
||||
if self.consume:
|
||||
self.backend.current_playlist.remove(id=original_track.id)
|
||||
self.backend.current_playlist.remove(cpid=original_cp_track[0])
|
||||
|
||||
if self.random and self.current_track in self._shuffled:
|
||||
self._shuffled.remove(self.current_track)
|
||||
if self.random and self.current_cp_track in self._shuffled:
|
||||
self._shuffled.remove(self.current_cp_track)
|
||||
|
||||
def _next(self, track):
|
||||
return self._play(track)
|
||||
@ -563,41 +622,42 @@ class BasePlaybackController(object):
|
||||
def _pause(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def play(self, track=None):
|
||||
def play(self, cp_track=None):
|
||||
"""
|
||||
Play the given track or the currently active track.
|
||||
|
||||
:param track: track to play
|
||||
:type track: :class:`mopidy.models.Track` or :class:`None`
|
||||
:param cp_track: track to play
|
||||
:type cp_track: two-tuple (CPID integer, :class:`mopidy.models.Track`)
|
||||
or :class:`None`
|
||||
"""
|
||||
|
||||
if track:
|
||||
assert track in self.backend.current_playlist.tracks
|
||||
elif not self.current_track:
|
||||
track = self.next_track
|
||||
if cp_track is not None:
|
||||
assert cp_track in self.backend.current_playlist.cp_tracks
|
||||
elif not self.current_cp_track:
|
||||
cp_track = self.next_cp_track
|
||||
|
||||
if self.state == self.PAUSED and track is None:
|
||||
if self.state == self.PAUSED and cp_track is None:
|
||||
self.resume()
|
||||
elif track is not None and self._play(track):
|
||||
self.current_track = track
|
||||
elif cp_track is not None and self._play(cp_track[1]):
|
||||
self.current_cp_track = cp_track
|
||||
self.state = self.PLAYING
|
||||
|
||||
# TODO Do something sensible when _play() returns False, like calling
|
||||
# next(). Adding this todo instead of just implementing it as I want a
|
||||
# test case first.
|
||||
|
||||
if self.random and self.current_track in self._shuffled:
|
||||
self._shuffled.remove(self.current_track)
|
||||
if self.random and self.current_cp_track in self._shuffled:
|
||||
self._shuffled.remove(self.current_cp_track)
|
||||
|
||||
def _play(self, track):
|
||||
raise NotImplementedError
|
||||
|
||||
def previous(self):
|
||||
"""Play the previous track."""
|
||||
if (self.previous_track is not None
|
||||
if (self.previous_cp_track is not None
|
||||
and self.state != self.STOPPED
|
||||
and self._previous(self.previous_track)):
|
||||
self.current_track = self.previous_track
|
||||
self.current_cp_track = self.previous_cp_track
|
||||
self.state = self.PLAYING
|
||||
|
||||
def _previous(self, track):
|
||||
|
||||
@ -369,8 +369,8 @@ class MpdFrontend(object):
|
||||
"""
|
||||
cpid = int(cpid)
|
||||
to = int(to)
|
||||
track = self.backend.current_playlist.get(cpid=cpid)
|
||||
position = self.backend.current_playlist.tracks.index(track)
|
||||
cp_track = self.backend.current_playlist.get(cpid=cpid)
|
||||
position = self.backend.current_playlist.cp_tracks.index(cp_track)
|
||||
self.backend.current_playlist.move(position, position + 1, to)
|
||||
|
||||
@handle_pattern(r'^playlist$')
|
||||
@ -404,8 +404,8 @@ class MpdFrontend(object):
|
||||
"""
|
||||
if tag == 'filename':
|
||||
try:
|
||||
track = self.backend.current_playlist.get(uri=needle)
|
||||
return track.mpd_format()
|
||||
cp_track = self.backend.current_playlist.get(uri=needle)
|
||||
return cp_track[1].mpd_format()
|
||||
except LookupError:
|
||||
return None
|
||||
raise MpdNotImplemented # TODO
|
||||
@ -423,9 +423,10 @@ class MpdFrontend(object):
|
||||
if cpid is not None:
|
||||
try:
|
||||
cpid = int(cpid)
|
||||
track = self.backend.current_playlist.get(cpid=cpid)
|
||||
position = self.backend.current_playlist.tracks.index(track)
|
||||
return track.mpd_format(position=position, cpid=cpid)
|
||||
cp_track = self.backend.current_playlist.get(cpid=cpid)
|
||||
position = self.backend.current_playlist.cp_tracks.index(
|
||||
cp_track)
|
||||
return cp_track[1].mpd_format(position=position, cpid=cpid)
|
||||
except LookupError:
|
||||
raise MpdNoExistError(u'No such song', command=u'playlistid')
|
||||
else:
|
||||
@ -571,10 +572,10 @@ class MpdFrontend(object):
|
||||
"""
|
||||
cpid1 = int(cpid1)
|
||||
cpid2 = int(cpid2)
|
||||
track1 = self.backend.current_playlist.get(cpid=cpid1)
|
||||
track2 = self.backend.current_playlist.get(cpid=cpid2)
|
||||
position1 = self.backend.current_playlist.tracks.index(track1)
|
||||
position2 = self.backend.current_playlist.tracks.index(track2)
|
||||
cp_track1 = self.backend.current_playlist.get(cpid=cpid1)
|
||||
cp_track2 = self.backend.current_playlist.get(cpid=cpid2)
|
||||
position1 = self.backend.current_playlist.cp_tracks.index(cp_track1)
|
||||
position2 = self.backend.current_playlist.cp_tracks.index(cp_track2)
|
||||
self._current_playlist_swap(position1, position2)
|
||||
|
||||
@handle_pattern(r'^$')
|
||||
@ -914,10 +915,10 @@ class MpdFrontend(object):
|
||||
cpid = int(cpid)
|
||||
try:
|
||||
if cpid == -1:
|
||||
track = self.backend.current_playlist.tracks[0]
|
||||
cp_track = self.backend.current_playlist.cp_tracks[0]
|
||||
else:
|
||||
track = self.backend.current_playlist.get(cpid=cpid)
|
||||
return self.backend.playback.play(track)
|
||||
cp_track = self.backend.current_playlist.get(cpid=cpid)
|
||||
return self.backend.playback.play(cp_track)
|
||||
except LookupError:
|
||||
raise MpdNoExistError(u'No such song', command=u'playid')
|
||||
|
||||
@ -933,15 +934,16 @@ class MpdFrontend(object):
|
||||
|
||||
*MPoD:*
|
||||
|
||||
- issues ``play "-1"`` after playlist replacement.
|
||||
- issues ``play "-1"`` after playlist replacement to start playback at
|
||||
the first track.
|
||||
"""
|
||||
songpos = int(songpos)
|
||||
try:
|
||||
if songpos == -1:
|
||||
track = self.backend.current_playlist.tracks[0]
|
||||
cp_track = self.backend.current_playlist.cp_tracks[0]
|
||||
else:
|
||||
track = self.backend.current_playlist.tracks[songpos]
|
||||
return self.backend.playback.play(track)
|
||||
cp_track = self.backend.current_playlist.cp_tracks[songpos]
|
||||
return self.backend.playback.play(cp_track)
|
||||
except IndexError:
|
||||
raise MpdArgError(u'Bad song index', command=u'play')
|
||||
|
||||
@ -1365,9 +1367,8 @@ class MpdFrontend(object):
|
||||
return int(self.backend.playback.single)
|
||||
|
||||
def __status_status_songid(self):
|
||||
# TODO Replace track.id with CPID
|
||||
if self.backend.playback.current_track.id is not None:
|
||||
return self.backend.playback.current_track.id
|
||||
if self.backend.playback.current_cpid is not None:
|
||||
return self.backend.playback.current_cpid
|
||||
else:
|
||||
return self.__status_status_songpos()
|
||||
|
||||
|
||||
@ -63,14 +63,13 @@ class BaseCurrentPlaylistControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_get_by_cpid(self):
|
||||
track = self.controller.tracks[1]
|
||||
cpid = self.controller._cp_tracks[1][0] # XXX Messing in internals
|
||||
self.assertEqual(track, self.controller.get(cpid=cpid))
|
||||
cp_track = self.controller.cp_tracks[1]
|
||||
self.assertEqual(cp_track, self.controller.get(cpid=cp_track[0]))
|
||||
|
||||
@populate_playlist
|
||||
def test_get_by_id(self):
|
||||
track = self.controller.tracks[1]
|
||||
self.assertEqual(track, self.controller.get(id=track.id))
|
||||
cp_track = self.controller.cp_tracks[1]
|
||||
self.assertEqual(cp_track, self.controller.get(id=cp_track[1].id))
|
||||
|
||||
@populate_playlist
|
||||
def test_get_by_id_raises_error_for_invalid_id(self):
|
||||
@ -78,8 +77,8 @@ class BaseCurrentPlaylistControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_get_by_uri(self):
|
||||
track = self.controller.tracks[1]
|
||||
self.assertEqual(track, self.controller.get(uri=track.uri))
|
||||
cp_track = self.controller.cp_tracks[1]
|
||||
self.assertEqual(cp_track, self.controller.get(uri=cp_track[1].uri))
|
||||
|
||||
@populate_playlist
|
||||
def test_get_by_uri_raises_error_for_invalid_id(self):
|
||||
@ -111,7 +110,7 @@ class BaseCurrentPlaylistControllerTest(object):
|
||||
def test_get_by_id_returns_unique_match(self):
|
||||
track = Track(id=1)
|
||||
self.controller.load([Track(id=13), track, Track(id=17)])
|
||||
self.assertEqual(track, self.controller.get(id=1))
|
||||
self.assertEqual(track, self.controller.get(id=1)[1])
|
||||
|
||||
def test_get_by_id_raises_error_if_multiple_matches(self):
|
||||
track = Track(id=1)
|
||||
@ -133,7 +132,7 @@ class BaseCurrentPlaylistControllerTest(object):
|
||||
def test_get_by_uri_returns_unique_match(self):
|
||||
track = Track(uri='a')
|
||||
self.controller.load([Track(uri='z'), track, Track(uri='y')])
|
||||
self.assertEqual(track, self.controller.get(uri='a'))
|
||||
self.assertEqual(track, self.controller.get(uri='a')[1])
|
||||
|
||||
def test_get_by_uri_raises_error_if_multiple_matches(self):
|
||||
track = Track(uri='a')
|
||||
@ -158,16 +157,16 @@ class BaseCurrentPlaylistControllerTest(object):
|
||||
track2 = Track(id=1, uri='b')
|
||||
track3 = Track(id=2, uri='b')
|
||||
self.controller.load([track1, track2, track3])
|
||||
self.assertEqual(track1, self.controller.get(id=1, uri='a'))
|
||||
self.assertEqual(track2, self.controller.get(id=1, uri='b'))
|
||||
self.assertEqual(track3, self.controller.get(id=2, uri='b'))
|
||||
self.assertEqual(track1, self.controller.get(id=1, uri='a')[1])
|
||||
self.assertEqual(track2, self.controller.get(id=1, uri='b')[1])
|
||||
self.assertEqual(track3, self.controller.get(id=2, uri='b')[1])
|
||||
|
||||
def test_get_by_criteria_that_is_not_present_in_all_elements(self):
|
||||
track1 = Track(id=1)
|
||||
track2 = Track(uri='b')
|
||||
track3 = Track(id=2)
|
||||
self.controller.load([track1, track2, track3])
|
||||
self.assertEqual(track1, self.controller.get(id=1))
|
||||
self.assertEqual(track1, self.controller.get(id=1)[1])
|
||||
|
||||
@populate_playlist
|
||||
def test_load_replaces_playlist(self):
|
||||
@ -316,6 +315,7 @@ class BasePlaybackControllerTest(object):
|
||||
def setUp(self):
|
||||
self.backend = self.backend_class(mixer=DummyMixer())
|
||||
self.playback = self.backend.playback
|
||||
self.current_playlist = self.backend.current_playlist
|
||||
|
||||
assert len(self.tracks) >= 3, \
|
||||
'Need at least three tracks to run tests.'
|
||||
@ -349,12 +349,13 @@ class BasePlaybackControllerTest(object):
|
||||
@populate_playlist
|
||||
def test_play_track_state(self):
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
self.playback.play(self.tracks[-1])
|
||||
self.playback.play(self.current_playlist.cp_tracks[-1])
|
||||
self.assertEqual(self.playback.state, self.playback.PLAYING)
|
||||
|
||||
@populate_playlist
|
||||
def test_play_track_return_value(self):
|
||||
self.assertEqual(self.playback.play(self.tracks[-1]), None)
|
||||
self.assertEqual(self.playback.play(
|
||||
self.current_playlist.cp_tracks[-1]), None)
|
||||
|
||||
@populate_playlist
|
||||
def test_play_when_playing(self):
|
||||
@ -379,17 +380,17 @@ class BasePlaybackControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_play_track_sets_current_track(self):
|
||||
self.playback.play(self.tracks[-1])
|
||||
self.playback.play(self.current_playlist.cp_tracks[-1])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[-1])
|
||||
|
||||
@populate_playlist
|
||||
def test_current_track_after_completed_playlist(self):
|
||||
self.playback.play(self.tracks[-1])
|
||||
self.playback.play(self.current_playlist.cp_tracks[-1])
|
||||
self.playback.end_of_track_callback()
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
|
||||
self.playback.play(self.tracks[-1])
|
||||
self.playback.play(self.current_playlist.cp_tracks[-1])
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
@ -510,7 +511,7 @@ class BasePlaybackControllerTest(object):
|
||||
@populate_playlist
|
||||
def test_next_track_at_end_of_playlist(self):
|
||||
self.playback.play()
|
||||
for track in self.tracks[1:]:
|
||||
for track in self.current_playlist.cp_tracks[1:]:
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.next_track, None)
|
||||
|
||||
@ -602,7 +603,7 @@ class BasePlaybackControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_current_playlist_position_at_end_of_playlist(self):
|
||||
self.playback.play(self.tracks[-1])
|
||||
self.playback.play(self.current_playlist.cp_tracks[-1])
|
||||
self.playback.end_of_track_callback()
|
||||
self.assertEqual(self.playback.current_playlist_position, None)
|
||||
|
||||
@ -763,8 +764,8 @@ class BasePlaybackControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_seek_beyond_end_of_song_for_last_track(self):
|
||||
self.playback.play(self.tracks[-1])
|
||||
self.playback.seek(self.tracks[-1].length*100)
|
||||
self.playback.play(self.current_playlist.cp_tracks[-1])
|
||||
self.playback.seek(self.current_playlist.tracks[-1].length * 100)
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@ -884,7 +885,7 @@ class BasePlaybackControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_end_of_playlist_stops(self):
|
||||
self.playback.play(self.tracks[-1])
|
||||
self.playback.play(self.current_playlist.cp_tracks[-1])
|
||||
self.playback.end_of_track_callback()
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
|
||||
|
||||
@ -119,7 +119,7 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
def test_currentsong(self):
|
||||
track = Track()
|
||||
self.b.current_playlist.load([track])
|
||||
self.b.playback.current_track = track
|
||||
self.b.playback.play()
|
||||
result = self.h.handle_request(u'currentsong')
|
||||
self.assert_(u'file: ' in result)
|
||||
self.assert_(u'Time: 0' in result)
|
||||
@ -129,7 +129,7 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'Track: 0' in result)
|
||||
self.assert_(u'Date: ' in result)
|
||||
self.assert_(u'Pos: 0' in result)
|
||||
self.assert_(u'Id: 0' in result)
|
||||
self.assert_(u'Id: 1' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_currentsong_without_song(self):
|
||||
@ -257,32 +257,22 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(result['state'], 'pause')
|
||||
|
||||
def test_status_method_when_playlist_loaded_contains_song(self):
|
||||
track = Track()
|
||||
self.b.current_playlist.load([track])
|
||||
self.b.playback.current_track = track
|
||||
self.b.current_playlist.load([Track()])
|
||||
self.b.playback.play()
|
||||
result = dict(self.h._status_status())
|
||||
self.assert_('song' in result)
|
||||
self.assert_(int(result['song']) >= 0)
|
||||
|
||||
def test_status_method_when_playlist_loaded_contains_pos_as_songid(self):
|
||||
track = Track()
|
||||
self.b.current_playlist.load([track])
|
||||
self.b.playback.current_track = track
|
||||
result = dict(self.h._status_status())
|
||||
self.assert_('songid' in result)
|
||||
self.assert_(int(result['songid']) >= 0)
|
||||
|
||||
def test_status_method_when_playlist_loaded_contains_id_as_songid(self):
|
||||
track = Track(id=1)
|
||||
self.b.current_playlist.load([track])
|
||||
self.b.playback.current_track = track
|
||||
def test_status_method_when_playlist_loaded_contains_cpid_as_songid(self):
|
||||
self.b.current_playlist.load([Track()])
|
||||
self.b.playback.play()
|
||||
result = dict(self.h._status_status())
|
||||
self.assert_('songid' in result)
|
||||
self.assertEqual(int(result['songid']), 1)
|
||||
|
||||
def test_status_method_when_playing_contains_time_with_no_length(self):
|
||||
self.b.playback.current_track = Track(length=None)
|
||||
self.b.playback.state = self.b.playback.PLAYING
|
||||
self.b.current_playlist.load([Track(length=None)])
|
||||
self.b.playback.play()
|
||||
result = dict(self.h._status_status())
|
||||
self.assert_('time' in result)
|
||||
(position, total) = result['time'].split(':')
|
||||
@ -291,8 +281,8 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assert_(position <= total)
|
||||
|
||||
def test_status_method_when_playing_contains_time_with_length(self):
|
||||
self.b.playback.current_track = Track(length=10000)
|
||||
self.b.playback.state = self.b.playback.PLAYING
|
||||
self.b.current_playlist.load([Track(length=10000)])
|
||||
self.b.playback.play()
|
||||
result = dict(self.h._status_status())
|
||||
self.assert_('time' in result)
|
||||
(position, total) = result['time'].split(':')
|
||||
@ -308,8 +298,8 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(int(result['elapsed']), 59123)
|
||||
|
||||
def test_status_method_when_playing_contains_bitrate(self):
|
||||
self.b.playback.state = self.b.playback.PLAYING
|
||||
self.b.playback.current_track = Track(bitrate=320)
|
||||
self.b.current_playlist.load([Track(bitrate=320)])
|
||||
self.b.playback.play()
|
||||
result = dict(self.h._status_status())
|
||||
self.assert_('bitrate' in result)
|
||||
self.assertEqual(int(result['bitrate']), 320)
|
||||
@ -448,7 +438,6 @@ class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
def test_pause_off(self):
|
||||
track = Track()
|
||||
self.b.current_playlist.load([track])
|
||||
self.b.playback.current_track = track
|
||||
self.h.handle_request(u'play "0"')
|
||||
self.h.handle_request(u'pause "1"')
|
||||
result = self.h.handle_request(u'pause "0"')
|
||||
@ -458,7 +447,6 @@ class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
def test_pause_on(self):
|
||||
track = Track()
|
||||
self.b.current_playlist.load([track])
|
||||
self.b.playback.current_track = track
|
||||
self.h.handle_request(u'play "0"')
|
||||
result = self.h.handle_request(u'pause "1"')
|
||||
self.assert_(u'OK' in result)
|
||||
@ -467,7 +455,6 @@ class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
def test_play_without_pos(self):
|
||||
track = Track()
|
||||
self.b.current_playlist.load([track])
|
||||
self.b.playback.current_track = track
|
||||
self.b.playback.state = self.b.playback.PAUSED
|
||||
result = self.h.handle_request(u'play')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user