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:
Stein Magnus Jodal 2010-07-31 19:51:03 +02:00
parent 8575c33227
commit 64544a0b71
5 changed files with 207 additions and 139 deletions

View File

@ -38,16 +38,35 @@ We got an updated :doc:`release roadmap <development/roadmap>`!
- Backend API: - 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 - :meth:`mopidy.backends.BaseCurrentPlaylistController.load()` now accepts
lists of :class:`mopidy.models.Track` instead of lists of :class:`mopidy.models.Track` instead of
:class:`mopidy.models.Playlist`, as none of the other fields on the :class:`mopidy.models.Playlist`, as none of the other fields on the
``Playlist`` model was in use. ``Playlist`` model was in use.
- :meth:`mopidy.backends.BaseCurrentPlaylistController.remove()` now takes - :meth:`mopidy.backends.BaseCurrentPlaylistController.remove()` now takes
criterias, just like criterias, just like
:meth:`mopidy.backends.BaseCurrentPlaylistController.get()`, and not the :meth:`mopidy.backends.BaseCurrentPlaylistController.get()`.
track to remove. - :meth:`mopidy.backends.BaseCurrentPlaylistController.get()` now returns a
``cp_track``.
- :attr:`mopidy.backends.BaseCurrentPlaylistController.tracks` is now - :attr:`mopidy.backends.BaseCurrentPlaylistController.tracks` is now
read-only. Use the methods to change its contents. 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) 0.1.0a2 (2010-06-02)

View File

@ -95,6 +95,15 @@ class BaseCurrentPlaylistController(object):
"""Cleanup after component.""" """Cleanup after component."""
pass 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 @property
def tracks(self): def tracks(self):
""" """
@ -104,23 +113,6 @@ class BaseCurrentPlaylistController(object):
""" """
return [ct[1] for ct in self._cp_tracks] 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): def add(self, track, at_position=None):
""" """
Add the track to the end of, or at the given position in the current 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): def clear(self):
"""Clear the current playlist.""" """Clear the current playlist."""
self.backend.playback.stop() self.backend.playback.stop()
self.backend.playback.current_track = None self.backend.playback.current_cp_track = None
self._cp_tracks = [] self._cp_tracks = []
self.version += 1 self.version += 1
@ -162,9 +154,23 @@ class BaseCurrentPlaylistController(object):
:param criteria: on or more criteria to match by :param criteria: on or more criteria to match by
:type criteria: dict :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): def load(self, tracks):
""" """
@ -214,13 +220,13 @@ class BaseCurrentPlaylistController(object):
""" """
Remove the track from the current playlist. 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 :param criteria: on or more criteria to match by
:type criteria: dict :type criteria: dict
:type track: :class:`mopidy.models.Track` :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) position = self._cp_tracks.index(cp_track)
del self._cp_tracks[position] del self._cp_tracks[position]
self.version += 1 self.version += 1
@ -338,11 +344,11 @@ class BasePlaybackController(object):
#: Tracks are not removed from the playlist. #: Tracks are not removed from the playlist.
consume = False consume = False
#: The CPID (current playlist ID) of :attr:`current_track`. #: The currently playing or selected track
current_cpid = 0 # TODO Get the correct CPID #:
#: A two-tuple of (CPID integer, :class:`mopidy.models.Track`) or
#: The currently playing or selected :class:`mopidy.models.Track`. #: :class:`None`.
current_track = None current_cp_track = None
#: :class:`True` #: :class:`True`
#: Tracks are selected at random from the playlist. #: Tracks are selected at random from the playlist.
@ -375,69 +381,122 @@ class BasePlaybackController(object):
"""Cleanup after component.""" """Cleanup after component."""
pass 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 @property
def current_playlist_position(self): def current_playlist_position(self):
"""The position of the current track in the current playlist.""" """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 return None
try: try:
return self.backend.current_playlist.tracks.index( return self.backend.current_playlist.cp_tracks.index(
self.current_track) self.current_cp_track)
except ValueError: except ValueError:
return None return None
@property @property
def next_track(self): 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 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 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 enabled this should be a random track, all tracks should be played once
before the list repeats. 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 return None
if self.random and not self._shuffled: if self.random and not self._shuffled:
if self.repeat or self._first_shuffle: if self.repeat or self._first_shuffle:
logger.debug('Shuffling tracks') logger.debug('Shuffling tracks')
self._shuffled = tracks self._shuffled = cp_tracks
random.shuffle(self._shuffled) random.shuffle(self._shuffled)
self._first_shuffle = False self._first_shuffle = False
if self._shuffled: if self._shuffled:
return self._shuffled[0] return self._shuffled[0]
if self.current_track is None: if self.current_cp_track is None:
return tracks[0] return cp_tracks[0]
if self.repeat: if self.repeat:
return tracks[(self.current_playlist_position + 1) % len(tracks)] return cp_tracks[
(self.current_playlist_position + 1) % len(cp_tracks)]
try: try:
return tracks[self.current_playlist_position + 1] return cp_tracks[self.current_playlist_position + 1]
except IndexError: except IndexError:
return None return None
@property @property
def previous_track(self): 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 For normal playback this is the previous track in the playlist. If
random and/or consume is enabled it should return the current track random and/or consume is enabled it should return the current track
instead. instead.
""" """
if self.repeat or self.consume or self.random: 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 None
return self.backend.current_playlist.tracks[ return self.backend.current_playlist.cp_tracks[
self.current_playlist_position - 1] self.current_playlist_position - 1]
@property @property
@ -507,11 +566,11 @@ class BasePlaybackController(object):
Typically called by :class:`mopidy.process.CoreProcess` after a message Typically called by :class:`mopidy.process.CoreProcess` after a message
from a library thread is received. from a library thread is received.
""" """
if self.next_track is not None: if self.next_cp_track is not None:
self.next() self.next()
else: else:
self.stop() self.stop()
self.current_track = None self.current_cp_track = None
def new_playlist_loaded_callback(self): def new_playlist_loaded_callback(self):
""" """
@ -520,7 +579,7 @@ class BasePlaybackController(object):
Typically called by :class:`mopidy.process.CoreProcess` after a message Typically called by :class:`mopidy.process.CoreProcess` after a message
from a library thread is received. from a library thread is received.
""" """
self.current_track = None self.current_cp_track = None
self._first_shuffle = True self._first_shuffle = True
self._shuffled = [] self._shuffled = []
@ -534,23 +593,23 @@ class BasePlaybackController(object):
def next(self): def next(self):
"""Play the next track.""" """Play the next track."""
original_track = self.current_track original_cp_track = self.current_cp_track
if self.state == self.STOPPED: if self.state == self.STOPPED:
return return
elif self.next_track is not None and self._next(self.next_track): elif self.next_cp_track is not None and self._next(self.next_track):
self.current_track = self.next_track self.current_cp_track = self.next_cp_track
self.state = self.PLAYING self.state = self.PLAYING
elif self.next_track is None: elif self.next_cp_track is None:
self.stop() self.stop()
self.current_track = None self.current_cp_track = None
# FIXME handle in play aswell? # FIXME handle in play aswell?
if self.consume: 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: if self.random and self.current_cp_track in self._shuffled:
self._shuffled.remove(self.current_track) self._shuffled.remove(self.current_cp_track)
def _next(self, track): def _next(self, track):
return self._play(track) return self._play(track)
@ -563,41 +622,42 @@ class BasePlaybackController(object):
def _pause(self): def _pause(self):
raise NotImplementedError raise NotImplementedError
def play(self, track=None): def play(self, cp_track=None):
""" """
Play the given track or the currently active track. Play the given track or the currently active track.
:param track: track to play :param cp_track: track to play
:type track: :class:`mopidy.models.Track` or :class:`None` :type cp_track: two-tuple (CPID integer, :class:`mopidy.models.Track`)
or :class:`None`
""" """
if track: if cp_track is not None:
assert track in self.backend.current_playlist.tracks assert cp_track in self.backend.current_playlist.cp_tracks
elif not self.current_track: elif not self.current_cp_track:
track = self.next_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() self.resume()
elif track is not None and self._play(track): elif cp_track is not None and self._play(cp_track[1]):
self.current_track = track self.current_cp_track = cp_track
self.state = self.PLAYING self.state = self.PLAYING
# TODO Do something sensible when _play() returns False, like calling # TODO Do something sensible when _play() returns False, like calling
# next(). Adding this todo instead of just implementing it as I want a # next(). Adding this todo instead of just implementing it as I want a
# test case first. # test case first.
if self.random and self.current_track in self._shuffled: if self.random and self.current_cp_track in self._shuffled:
self._shuffled.remove(self.current_track) self._shuffled.remove(self.current_cp_track)
def _play(self, track): def _play(self, track):
raise NotImplementedError raise NotImplementedError
def previous(self): def previous(self):
"""Play the previous track.""" """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.state != self.STOPPED
and self._previous(self.previous_track)): and self._previous(self.previous_track)):
self.current_track = self.previous_track self.current_cp_track = self.previous_cp_track
self.state = self.PLAYING self.state = self.PLAYING
def _previous(self, track): def _previous(self, track):

View File

@ -369,8 +369,8 @@ class MpdFrontend(object):
""" """
cpid = int(cpid) cpid = int(cpid)
to = int(to) to = int(to)
track = self.backend.current_playlist.get(cpid=cpid) cp_track = self.backend.current_playlist.get(cpid=cpid)
position = self.backend.current_playlist.tracks.index(track) position = self.backend.current_playlist.cp_tracks.index(cp_track)
self.backend.current_playlist.move(position, position + 1, to) self.backend.current_playlist.move(position, position + 1, to)
@handle_pattern(r'^playlist$') @handle_pattern(r'^playlist$')
@ -404,8 +404,8 @@ class MpdFrontend(object):
""" """
if tag == 'filename': if tag == 'filename':
try: try:
track = self.backend.current_playlist.get(uri=needle) cp_track = self.backend.current_playlist.get(uri=needle)
return track.mpd_format() return cp_track[1].mpd_format()
except LookupError: except LookupError:
return None return None
raise MpdNotImplemented # TODO raise MpdNotImplemented # TODO
@ -423,9 +423,10 @@ class MpdFrontend(object):
if cpid is not None: if cpid is not None:
try: try:
cpid = int(cpid) cpid = int(cpid)
track = self.backend.current_playlist.get(cpid=cpid) cp_track = self.backend.current_playlist.get(cpid=cpid)
position = self.backend.current_playlist.tracks.index(track) position = self.backend.current_playlist.cp_tracks.index(
return track.mpd_format(position=position, cpid=cpid) cp_track)
return cp_track[1].mpd_format(position=position, cpid=cpid)
except LookupError: except LookupError:
raise MpdNoExistError(u'No such song', command=u'playlistid') raise MpdNoExistError(u'No such song', command=u'playlistid')
else: else:
@ -571,10 +572,10 @@ class MpdFrontend(object):
""" """
cpid1 = int(cpid1) cpid1 = int(cpid1)
cpid2 = int(cpid2) cpid2 = int(cpid2)
track1 = self.backend.current_playlist.get(cpid=cpid1) cp_track1 = self.backend.current_playlist.get(cpid=cpid1)
track2 = self.backend.current_playlist.get(cpid=cpid2) cp_track2 = self.backend.current_playlist.get(cpid=cpid2)
position1 = self.backend.current_playlist.tracks.index(track1) position1 = self.backend.current_playlist.cp_tracks.index(cp_track1)
position2 = self.backend.current_playlist.tracks.index(track2) position2 = self.backend.current_playlist.cp_tracks.index(cp_track2)
self._current_playlist_swap(position1, position2) self._current_playlist_swap(position1, position2)
@handle_pattern(r'^$') @handle_pattern(r'^$')
@ -914,10 +915,10 @@ class MpdFrontend(object):
cpid = int(cpid) cpid = int(cpid)
try: try:
if cpid == -1: if cpid == -1:
track = self.backend.current_playlist.tracks[0] cp_track = self.backend.current_playlist.cp_tracks[0]
else: else:
track = self.backend.current_playlist.get(cpid=cpid) cp_track = self.backend.current_playlist.get(cpid=cpid)
return self.backend.playback.play(track) return self.backend.playback.play(cp_track)
except LookupError: except LookupError:
raise MpdNoExistError(u'No such song', command=u'playid') raise MpdNoExistError(u'No such song', command=u'playid')
@ -933,15 +934,16 @@ class MpdFrontend(object):
*MPoD:* *MPoD:*
- issues ``play "-1"`` after playlist replacement. - issues ``play "-1"`` after playlist replacement to start playback at
the first track.
""" """
songpos = int(songpos) songpos = int(songpos)
try: try:
if songpos == -1: if songpos == -1:
track = self.backend.current_playlist.tracks[0] cp_track = self.backend.current_playlist.cp_tracks[0]
else: else:
track = self.backend.current_playlist.tracks[songpos] cp_track = self.backend.current_playlist.cp_tracks[songpos]
return self.backend.playback.play(track) return self.backend.playback.play(cp_track)
except IndexError: except IndexError:
raise MpdArgError(u'Bad song index', command=u'play') raise MpdArgError(u'Bad song index', command=u'play')
@ -1365,9 +1367,8 @@ class MpdFrontend(object):
return int(self.backend.playback.single) return int(self.backend.playback.single)
def __status_status_songid(self): def __status_status_songid(self):
# TODO Replace track.id with CPID if self.backend.playback.current_cpid is not None:
if self.backend.playback.current_track.id is not None: return self.backend.playback.current_cpid
return self.backend.playback.current_track.id
else: else:
return self.__status_status_songpos() return self.__status_status_songpos()

View File

@ -63,14 +63,13 @@ class BaseCurrentPlaylistControllerTest(object):
@populate_playlist @populate_playlist
def test_get_by_cpid(self): def test_get_by_cpid(self):
track = self.controller.tracks[1] cp_track = self.controller.cp_tracks[1]
cpid = self.controller._cp_tracks[1][0] # XXX Messing in internals self.assertEqual(cp_track, self.controller.get(cpid=cp_track[0]))
self.assertEqual(track, self.controller.get(cpid=cpid))
@populate_playlist @populate_playlist
def test_get_by_id(self): def test_get_by_id(self):
track = self.controller.tracks[1] cp_track = self.controller.cp_tracks[1]
self.assertEqual(track, self.controller.get(id=track.id)) self.assertEqual(cp_track, self.controller.get(id=cp_track[1].id))
@populate_playlist @populate_playlist
def test_get_by_id_raises_error_for_invalid_id(self): def test_get_by_id_raises_error_for_invalid_id(self):
@ -78,8 +77,8 @@ class BaseCurrentPlaylistControllerTest(object):
@populate_playlist @populate_playlist
def test_get_by_uri(self): def test_get_by_uri(self):
track = self.controller.tracks[1] cp_track = self.controller.cp_tracks[1]
self.assertEqual(track, self.controller.get(uri=track.uri)) self.assertEqual(cp_track, self.controller.get(uri=cp_track[1].uri))
@populate_playlist @populate_playlist
def test_get_by_uri_raises_error_for_invalid_id(self): 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): def test_get_by_id_returns_unique_match(self):
track = Track(id=1) track = Track(id=1)
self.controller.load([Track(id=13), track, Track(id=17)]) 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): def test_get_by_id_raises_error_if_multiple_matches(self):
track = Track(id=1) track = Track(id=1)
@ -133,7 +132,7 @@ class BaseCurrentPlaylistControllerTest(object):
def test_get_by_uri_returns_unique_match(self): def test_get_by_uri_returns_unique_match(self):
track = Track(uri='a') track = Track(uri='a')
self.controller.load([Track(uri='z'), track, Track(uri='y')]) 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): def test_get_by_uri_raises_error_if_multiple_matches(self):
track = Track(uri='a') track = Track(uri='a')
@ -158,16 +157,16 @@ class BaseCurrentPlaylistControllerTest(object):
track2 = Track(id=1, uri='b') track2 = Track(id=1, uri='b')
track3 = Track(id=2, uri='b') track3 = Track(id=2, uri='b')
self.controller.load([track1, track2, track3]) self.controller.load([track1, track2, track3])
self.assertEqual(track1, self.controller.get(id=1, uri='a')) self.assertEqual(track1, self.controller.get(id=1, uri='a')[1])
self.assertEqual(track2, self.controller.get(id=1, uri='b')) self.assertEqual(track2, self.controller.get(id=1, uri='b')[1])
self.assertEqual(track3, self.controller.get(id=2, uri='b')) self.assertEqual(track3, self.controller.get(id=2, uri='b')[1])
def test_get_by_criteria_that_is_not_present_in_all_elements(self): def test_get_by_criteria_that_is_not_present_in_all_elements(self):
track1 = Track(id=1) track1 = Track(id=1)
track2 = Track(uri='b') track2 = Track(uri='b')
track3 = Track(id=2) track3 = Track(id=2)
self.controller.load([track1, track2, track3]) 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 @populate_playlist
def test_load_replaces_playlist(self): def test_load_replaces_playlist(self):
@ -316,6 +315,7 @@ class BasePlaybackControllerTest(object):
def setUp(self): def setUp(self):
self.backend = self.backend_class(mixer=DummyMixer()) self.backend = self.backend_class(mixer=DummyMixer())
self.playback = self.backend.playback self.playback = self.backend.playback
self.current_playlist = self.backend.current_playlist
assert len(self.tracks) >= 3, \ assert len(self.tracks) >= 3, \
'Need at least three tracks to run tests.' 'Need at least three tracks to run tests.'
@ -349,12 +349,13 @@ class BasePlaybackControllerTest(object):
@populate_playlist @populate_playlist
def test_play_track_state(self): def test_play_track_state(self):
self.assertEqual(self.playback.state, self.playback.STOPPED) 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) self.assertEqual(self.playback.state, self.playback.PLAYING)
@populate_playlist @populate_playlist
def test_play_track_return_value(self): 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 @populate_playlist
def test_play_when_playing(self): def test_play_when_playing(self):
@ -379,17 +380,17 @@ class BasePlaybackControllerTest(object):
@populate_playlist @populate_playlist
def test_play_track_sets_current_track(self): 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]) self.assertEqual(self.playback.current_track, self.tracks[-1])
@populate_playlist @populate_playlist
def test_current_track_after_completed_playlist(self): 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.playback.end_of_track_callback()
self.assertEqual(self.playback.state, self.playback.STOPPED) self.assertEqual(self.playback.state, self.playback.STOPPED)
self.assertEqual(self.playback.current_track, None) 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.playback.next()
self.assertEqual(self.playback.state, self.playback.STOPPED) self.assertEqual(self.playback.state, self.playback.STOPPED)
self.assertEqual(self.playback.current_track, None) self.assertEqual(self.playback.current_track, None)
@ -510,7 +511,7 @@ class BasePlaybackControllerTest(object):
@populate_playlist @populate_playlist
def test_next_track_at_end_of_playlist(self): def test_next_track_at_end_of_playlist(self):
self.playback.play() self.playback.play()
for track in self.tracks[1:]: for track in self.current_playlist.cp_tracks[1:]:
self.playback.next() self.playback.next()
self.assertEqual(self.playback.next_track, None) self.assertEqual(self.playback.next_track, None)
@ -602,7 +603,7 @@ class BasePlaybackControllerTest(object):
@populate_playlist @populate_playlist
def test_current_playlist_position_at_end_of_playlist(self): 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.playback.end_of_track_callback()
self.assertEqual(self.playback.current_playlist_position, None) self.assertEqual(self.playback.current_playlist_position, None)
@ -763,8 +764,8 @@ class BasePlaybackControllerTest(object):
@populate_playlist @populate_playlist
def test_seek_beyond_end_of_song_for_last_track(self): def test_seek_beyond_end_of_song_for_last_track(self):
self.playback.play(self.tracks[-1]) self.playback.play(self.current_playlist.cp_tracks[-1])
self.playback.seek(self.tracks[-1].length*100) self.playback.seek(self.current_playlist.tracks[-1].length * 100)
self.assertEqual(self.playback.state, self.playback.STOPPED) self.assertEqual(self.playback.state, self.playback.STOPPED)
@populate_playlist @populate_playlist
@ -884,7 +885,7 @@ class BasePlaybackControllerTest(object):
@populate_playlist @populate_playlist
def test_end_of_playlist_stops(self): 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.playback.end_of_track_callback()
self.assertEqual(self.playback.state, self.playback.STOPPED) self.assertEqual(self.playback.state, self.playback.STOPPED)

View File

@ -119,7 +119,7 @@ class StatusHandlerTest(unittest.TestCase):
def test_currentsong(self): def test_currentsong(self):
track = Track() track = Track()
self.b.current_playlist.load([track]) self.b.current_playlist.load([track])
self.b.playback.current_track = track self.b.playback.play()
result = self.h.handle_request(u'currentsong') result = self.h.handle_request(u'currentsong')
self.assert_(u'file: ' in result) self.assert_(u'file: ' in result)
self.assert_(u'Time: 0' 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'Track: 0' in result)
self.assert_(u'Date: ' in result) self.assert_(u'Date: ' in result)
self.assert_(u'Pos: 0' 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) self.assert_(u'OK' in result)
def test_currentsong_without_song(self): def test_currentsong_without_song(self):
@ -257,32 +257,22 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(result['state'], 'pause') self.assertEqual(result['state'], 'pause')
def test_status_method_when_playlist_loaded_contains_song(self): def test_status_method_when_playlist_loaded_contains_song(self):
track = Track() self.b.current_playlist.load([Track()])
self.b.current_playlist.load([track]) self.b.playback.play()
self.b.playback.current_track = track
result = dict(self.h._status_status()) result = dict(self.h._status_status())
self.assert_('song' in result) self.assert_('song' in result)
self.assert_(int(result['song']) >= 0) self.assert_(int(result['song']) >= 0)
def test_status_method_when_playlist_loaded_contains_pos_as_songid(self): def test_status_method_when_playlist_loaded_contains_cpid_as_songid(self):
track = Track() self.b.current_playlist.load([Track()])
self.b.current_playlist.load([track]) self.b.playback.play()
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
result = dict(self.h._status_status()) result = dict(self.h._status_status())
self.assert_('songid' in result) self.assert_('songid' in result)
self.assertEqual(int(result['songid']), 1) self.assertEqual(int(result['songid']), 1)
def test_status_method_when_playing_contains_time_with_no_length(self): def test_status_method_when_playing_contains_time_with_no_length(self):
self.b.playback.current_track = Track(length=None) self.b.current_playlist.load([Track(length=None)])
self.b.playback.state = self.b.playback.PLAYING self.b.playback.play()
result = dict(self.h._status_status()) result = dict(self.h._status_status())
self.assert_('time' in result) self.assert_('time' in result)
(position, total) = result['time'].split(':') (position, total) = result['time'].split(':')
@ -291,8 +281,8 @@ class StatusHandlerTest(unittest.TestCase):
self.assert_(position <= total) self.assert_(position <= total)
def test_status_method_when_playing_contains_time_with_length(self): def test_status_method_when_playing_contains_time_with_length(self):
self.b.playback.current_track = Track(length=10000) self.b.current_playlist.load([Track(length=10000)])
self.b.playback.state = self.b.playback.PLAYING self.b.playback.play()
result = dict(self.h._status_status()) result = dict(self.h._status_status())
self.assert_('time' in result) self.assert_('time' in result)
(position, total) = result['time'].split(':') (position, total) = result['time'].split(':')
@ -308,8 +298,8 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(int(result['elapsed']), 59123) self.assertEqual(int(result['elapsed']), 59123)
def test_status_method_when_playing_contains_bitrate(self): def test_status_method_when_playing_contains_bitrate(self):
self.b.playback.state = self.b.playback.PLAYING self.b.current_playlist.load([Track(bitrate=320)])
self.b.playback.current_track = Track(bitrate=320) self.b.playback.play()
result = dict(self.h._status_status()) result = dict(self.h._status_status())
self.assert_('bitrate' in result) self.assert_('bitrate' in result)
self.assertEqual(int(result['bitrate']), 320) self.assertEqual(int(result['bitrate']), 320)
@ -448,7 +438,6 @@ class PlaybackControlHandlerTest(unittest.TestCase):
def test_pause_off(self): def test_pause_off(self):
track = Track() track = Track()
self.b.current_playlist.load([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'play "0"')
self.h.handle_request(u'pause "1"') self.h.handle_request(u'pause "1"')
result = self.h.handle_request(u'pause "0"') result = self.h.handle_request(u'pause "0"')
@ -458,7 +447,6 @@ class PlaybackControlHandlerTest(unittest.TestCase):
def test_pause_on(self): def test_pause_on(self):
track = Track() track = Track()
self.b.current_playlist.load([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'play "0"')
result = self.h.handle_request(u'pause "1"') result = self.h.handle_request(u'pause "1"')
self.assert_(u'OK' in result) self.assert_(u'OK' in result)
@ -467,7 +455,6 @@ class PlaybackControlHandlerTest(unittest.TestCase):
def test_play_without_pos(self): def test_play_without_pos(self):
track = Track() track = Track()
self.b.current_playlist.load([track]) self.b.current_playlist.load([track])
self.b.playback.current_track = track
self.b.playback.state = self.b.playback.PAUSED self.b.playback.state = self.b.playback.PAUSED
result = self.h.handle_request(u'play') result = self.h.handle_request(u'play')
self.assert_(u'OK' in result) self.assert_(u'OK' in result)