merged jodal gstreamer with knutz3n singlerepeat branch
This commit is contained in:
commit
6905b81009
@ -35,12 +35,14 @@ greatly improved MPD client support.
|
||||
- Added new :mod:`mopidy.mixers.GStreamerSoftwareMixer` which now is the
|
||||
default mixer on all platforms.
|
||||
- New setting ``MIXER_MAX_VOLUME`` for capping the maximum output volume.
|
||||
- If failing to play a track, playback will skip to the next track.
|
||||
- MPD frontend:
|
||||
|
||||
- Relocate from :mod:`mopidy.mpd` to :mod:`mopidy.frontends.mpd`.
|
||||
- Split gigantic protocol implementation into eleven modules.
|
||||
- Search improvements, including support for multi-word search.
|
||||
- Fixed ``play "-1"`` and ``playid "-1"`` behaviour when playlist is empty.
|
||||
- Fixed ``play "-1"`` and ``playid "-1"`` behaviour when playlist is empty
|
||||
or when a current track is set.
|
||||
- Support ``plchanges "-1"`` to work better with MPDroid.
|
||||
- Support ``pause`` without arguments to work better with MPDroid.
|
||||
- Support ``plchanges``, ``play``, ``consume``, ``random``, ``repeat``, and
|
||||
@ -50,6 +52,7 @@ greatly improved MPD client support.
|
||||
- Implement ``seek`` and ``seekid``.
|
||||
- Fix ``playlistfind`` output so the correct song is played when playing
|
||||
songs directly from search results in GMPC.
|
||||
- Fix ``load`` so that one can append a playlist to the current playlist.
|
||||
|
||||
- Backends:
|
||||
|
||||
@ -73,7 +76,10 @@ greatly improved MPD client support.
|
||||
- :meth:`mopidy.backends.base.BaseBackend()` now accepts an
|
||||
``output_queue`` which it can use to send messages (i.e. audio data)
|
||||
to the output process.
|
||||
|
||||
- :meth:`mopidy.backends.base.BaseCurrentPlaylistController.load()` now
|
||||
appends to the existing playlist. Use
|
||||
:meth:`mopidy.backends.base.BaseCurrentPlaylistController.clear()` if you
|
||||
want to clear it first.
|
||||
|
||||
|
||||
0.1.0a3 (2010-08-03)
|
||||
|
||||
@ -2,15 +2,21 @@
|
||||
libspotify installation
|
||||
***********************
|
||||
|
||||
We are working on a
|
||||
`libspotify <http://developer.spotify.com/en/libspotify/overview/>`_ backend.
|
||||
To use the libspotify backend you must install libspotify and
|
||||
`pyspotify <http://github.com/winjer/pyspotify>`_.
|
||||
Mopidy uses `libspotify
|
||||
<http://developer.spotify.com/en/libspotify/overview/>`_ for playing music from
|
||||
the Spotify music service. To use :mod:`mopidy.backends.libspotify` you must
|
||||
install libspotify and `pyspotify <http://github.com/winjer/pyspotify>`_.
|
||||
|
||||
.. warning::
|
||||
|
||||
This backend requires a Spotify premium account, and it requires you to get
|
||||
an application key from Spotify before use.
|
||||
This backend requires a `Spotify premium account
|
||||
<http://www.spotify.com/no/get-spotify/premium/>`_.
|
||||
|
||||
.. note::
|
||||
|
||||
This product uses SPOTIFY CORE but is not endorsed, certified or otherwise
|
||||
approved in any way by Spotify. Spotify is the registered trade mark of the
|
||||
Spotify Group.
|
||||
|
||||
|
||||
Installing libspotify on Linux
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
Licenses
|
||||
********
|
||||
|
||||
For a list of contributors, see :ref:`authors`. For details on who have
|
||||
For a list of contributors, see :doc:`authors`. For details on who have
|
||||
contributed what, please refer to our git repository.
|
||||
|
||||
Source code license
|
||||
|
||||
@ -66,10 +66,9 @@ class BaseCurrentPlaylistController(object):
|
||||
|
||||
def clear(self):
|
||||
"""Clear the current playlist."""
|
||||
self.backend.playback.stop()
|
||||
self.backend.playback.current_cp_track = None
|
||||
self._cp_tracks = []
|
||||
self.version += 1
|
||||
self.backend.playback.on_current_playlist_change()
|
||||
|
||||
def get(self, **criteria):
|
||||
"""
|
||||
@ -107,16 +106,15 @@ class BaseCurrentPlaylistController(object):
|
||||
|
||||
def load(self, tracks):
|
||||
"""
|
||||
Replace the tracks in the current playlist with the given tracks.
|
||||
Append the given tracks to the current playlist.
|
||||
|
||||
:param tracks: tracks to load
|
||||
:type tracks: list of :class:`mopidy.models.Track`
|
||||
"""
|
||||
self._cp_tracks = []
|
||||
self.version += 1
|
||||
for track in tracks:
|
||||
self.add(track)
|
||||
self.backend.playback.new_playlist_loaded_callback()
|
||||
self.backend.playback.on_current_playlist_change()
|
||||
|
||||
def move(self, start, end, to_position):
|
||||
"""
|
||||
@ -148,6 +146,7 @@ class BaseCurrentPlaylistController(object):
|
||||
to_position += 1
|
||||
self._cp_tracks = new_cp_tracks
|
||||
self.version += 1
|
||||
self.backend.playback.on_current_playlist_change()
|
||||
|
||||
def remove(self, **criteria):
|
||||
"""
|
||||
@ -192,6 +191,7 @@ class BaseCurrentPlaylistController(object):
|
||||
random.shuffle(shuffled)
|
||||
self._cp_tracks = before + shuffled + after
|
||||
self.version += 1
|
||||
self.backend.playback.on_current_playlist_change()
|
||||
|
||||
def mpd_format(self, *args, **kwargs):
|
||||
"""Not a part of the generic backend API."""
|
||||
|
||||
@ -287,11 +287,9 @@ class BasePlaybackController(object):
|
||||
Typically called by :class:`mopidy.process.CoreProcess` after a message
|
||||
from a library thread is received.
|
||||
"""
|
||||
next_cp_track = self.cp_track_at_eot
|
||||
if next_cp_track is not None and self._next(next_cp_track[1]):
|
||||
original_cp_track = self.current_cp_track
|
||||
self.current_cp_track = next_cp_track
|
||||
self.state = self.PLAYING
|
||||
original_cp_track = self.current_cp_track
|
||||
if self.cp_track_at_eot:
|
||||
self.play(self.cp_track_at_eot)
|
||||
|
||||
if self.consume:
|
||||
self.backend.current_playlist.remove(cpid=original_cp_track[0])
|
||||
@ -302,48 +300,43 @@ class BasePlaybackController(object):
|
||||
self.stop()
|
||||
self.current_cp_track = None
|
||||
|
||||
def new_playlist_loaded_callback(self):
|
||||
def on_current_playlist_change(self):
|
||||
"""
|
||||
Tell the playback controller that a new playlist has been loaded.
|
||||
Tell the playback controller that the current playlist has changed.
|
||||
|
||||
Typically called by :class:`mopidy.process.CoreProcess` after a message
|
||||
from a library thread is received.
|
||||
Used by :class:`mopidy.backends.base.BaseCurrentPlaylistController`.
|
||||
"""
|
||||
self.current_cp_track = None
|
||||
self._first_shuffle = True
|
||||
self._shuffled = []
|
||||
|
||||
if self.state == self.PLAYING:
|
||||
if len(self.backend.current_playlist.tracks) > 0:
|
||||
self.play()
|
||||
else:
|
||||
self.stop()
|
||||
elif self.state == self.PAUSED:
|
||||
if not self.backend.current_playlist.cp_tracks:
|
||||
self.stop()
|
||||
self.current_cp_track = None
|
||||
elif (self.current_cp_track not in
|
||||
self.backend.current_playlist.cp_tracks):
|
||||
self.current_cp_track = None
|
||||
self.stop()
|
||||
|
||||
def next(self):
|
||||
"""Play the next track."""
|
||||
original_cp_track = self.current_cp_track
|
||||
|
||||
if self.state == self.STOPPED:
|
||||
return
|
||||
elif self.cp_track_at_next is not None and self._next(self.next_track):
|
||||
self.current_cp_track = self.cp_track_at_next
|
||||
self.state = self.PLAYING
|
||||
elif self.cp_track_at_next is None:
|
||||
|
||||
original_cp_track = self.current_cp_track
|
||||
if self.cp_track_at_next:
|
||||
self.play(self.cp_track_at_next)
|
||||
else:
|
||||
self.stop()
|
||||
self.current_cp_track = None
|
||||
|
||||
# FIXME handle in play aswell?
|
||||
# FIXME This should only be applied when reaching end of track, and not
|
||||
# when pressing "next"
|
||||
if self.consume:
|
||||
self.backend.current_playlist.remove(cpid=original_cp_track[0])
|
||||
|
||||
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)
|
||||
|
||||
def pause(self):
|
||||
"""Pause playback."""
|
||||
if self.state == self.PLAYING and self._pause():
|
||||
@ -352,13 +345,16 @@ class BasePlaybackController(object):
|
||||
def _pause(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def play(self, cp_track=None):
|
||||
def play(self, cp_track=None, on_error_step=1):
|
||||
"""
|
||||
Play the given track or the currently active track.
|
||||
|
||||
:param cp_track: track to play
|
||||
:type cp_track: two-tuple (CPID integer, :class:`mopidy.models.Track`)
|
||||
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
|
||||
"""
|
||||
|
||||
if cp_track is not None:
|
||||
@ -368,13 +364,14 @@ class BasePlaybackController(object):
|
||||
|
||||
if self.state == self.PAUSED and cp_track is None:
|
||||
self.resume()
|
||||
elif cp_track is not None and self._play(cp_track[1]):
|
||||
elif cp_track is not None:
|
||||
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 not self._play(cp_track[1]):
|
||||
if on_error_step == 1:
|
||||
self.next()
|
||||
elif on_error_step == -1:
|
||||
self.previous()
|
||||
|
||||
if self.random and self.current_cp_track in self._shuffled:
|
||||
self._shuffled.remove(self.current_cp_track)
|
||||
@ -384,14 +381,11 @@ class BasePlaybackController(object):
|
||||
|
||||
def previous(self):
|
||||
"""Play the previous track."""
|
||||
if (self.previous_cp_track is not None
|
||||
and self.state != self.STOPPED
|
||||
and self._previous(self.previous_track)):
|
||||
self.current_cp_track = self.previous_cp_track
|
||||
self.state = self.PLAYING
|
||||
|
||||
def _previous(self, track):
|
||||
return self._play(track)
|
||||
if self.previous_cp_track is None:
|
||||
return
|
||||
if self.state == self.STOPPED:
|
||||
return
|
||||
self.play(self.previous_cp_track, on_error_step=-1)
|
||||
|
||||
def resume(self):
|
||||
"""If paused, resume playing the current track."""
|
||||
|
||||
@ -9,14 +9,18 @@ ENCODING = 'utf-8'
|
||||
|
||||
class LibspotifyBackend(BaseBackend):
|
||||
"""
|
||||
A Spotify backend which uses the official `libspotify library
|
||||
<http://developer.spotify.com/en/libspotify/overview/>`_.
|
||||
|
||||
`pyspotify <http://github.com/winjer/pyspotify/>`_ is the Python bindings
|
||||
for libspotify. It got no documentation, but multiple examples are
|
||||
available. Like libspotify, pyspotify's calls are mostly asynchronous.
|
||||
A `Spotify <http://www.spotify.com/>`_ backend which uses the official
|
||||
`libspotify <http://developer.spotify.com/en/libspotify/overview/>`_
|
||||
library and the `pyspotify <http://github.com/winjer/pyspotify/>`_ Python
|
||||
bindings for libspotify.
|
||||
|
||||
**Issues:** http://github.com/jodal/mopidy/issues/labels/backend-libspotify
|
||||
|
||||
.. note::
|
||||
|
||||
This product uses SPOTIFY(R) CORE but is not endorsed, certified or
|
||||
otherwise approved in any way by Spotify. Spotify is the registered
|
||||
trade mark of the Spotify Group.
|
||||
"""
|
||||
|
||||
# Imports inside methods are to prevent loading of __init__.py to fail on
|
||||
@ -40,6 +44,7 @@ class LibspotifyBackend(BaseBackend):
|
||||
def _connect(self):
|
||||
from .session_manager import LibspotifySessionManager
|
||||
|
||||
logger.info(u'Mopidy uses SPOTIFY(R) CORE')
|
||||
logger.info(u'Connecting to Spotify')
|
||||
spotify = LibspotifySessionManager(
|
||||
settings.SPOTIFY_USERNAME, settings.SPOTIFY_PASSWORD,
|
||||
|
||||
@ -26,7 +26,7 @@ class LibspotifyPlaybackController(BasePlaybackController):
|
||||
def _play(self, track):
|
||||
self._set_output_state('READY')
|
||||
if self.state == self.PLAYING:
|
||||
self.stop()
|
||||
self.backend.spotify.session.play(0)
|
||||
if track.uri is None:
|
||||
return False
|
||||
try:
|
||||
|
||||
@ -39,7 +39,7 @@ class LibspotifyTranslator(object):
|
||||
track_no=spotify_track.index(),
|
||||
date=date,
|
||||
length=spotify_track.duration(),
|
||||
bitrate=320,
|
||||
bitrate=160,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -341,6 +341,7 @@ def swap(frontend, songpos1, songpos2):
|
||||
tracks.insert(songpos1, song2)
|
||||
del tracks[songpos2]
|
||||
tracks.insert(songpos2, song1)
|
||||
frontend.backend.current_playlist.clear()
|
||||
frontend.backend.current_playlist.load(tracks)
|
||||
|
||||
@handle_pattern(r'^swapid "(?P<cpid1>\d+)" "(?P<cpid2>\d+)"$')
|
||||
|
||||
@ -139,9 +139,7 @@ def playid(frontend, cpid):
|
||||
cpid = int(cpid)
|
||||
try:
|
||||
if cpid == -1:
|
||||
if not frontend.backend.current_playlist.cp_tracks:
|
||||
return # Fail silently
|
||||
cp_track = frontend.backend.current_playlist.cp_tracks[0]
|
||||
cp_track = _get_cp_track_for_play_minus_one(frontend)
|
||||
else:
|
||||
cp_track = frontend.backend.current_playlist.get(cpid=cpid)
|
||||
return frontend.backend.playback.play(cp_track)
|
||||
@ -158,10 +156,11 @@ def playpos(frontend, songpos):
|
||||
|
||||
Begins playing the playlist at song number ``SONGPOS``.
|
||||
|
||||
*MPoD:*
|
||||
*Many clients:*
|
||||
|
||||
- issues ``play "-1"`` after playlist replacement to start playback at
|
||||
the first track.
|
||||
- issue ``play "-1"`` after playlist replacement to start the current
|
||||
track. If the current track is not set, start playback at the first
|
||||
track.
|
||||
|
||||
*BitMPC:*
|
||||
|
||||
@ -170,15 +169,21 @@ def playpos(frontend, songpos):
|
||||
songpos = int(songpos)
|
||||
try:
|
||||
if songpos == -1:
|
||||
if not frontend.backend.current_playlist.cp_tracks:
|
||||
return # Fail silently
|
||||
cp_track = frontend.backend.current_playlist.cp_tracks[0]
|
||||
cp_track = _get_cp_track_for_play_minus_one(frontend)
|
||||
else:
|
||||
cp_track = frontend.backend.current_playlist.cp_tracks[songpos]
|
||||
return frontend.backend.playback.play(cp_track)
|
||||
except IndexError:
|
||||
raise MpdArgError(u'Bad song index', command=u'play')
|
||||
|
||||
def _get_cp_track_for_play_minus_one(frontend):
|
||||
if not frontend.backend.current_playlist.cp_tracks:
|
||||
return # Fail silently
|
||||
cp_track = frontend.backend.playback.current_cp_track
|
||||
if cp_track is None:
|
||||
cp_track = frontend.backend.current_playlist.cp_tracks[0]
|
||||
return cp_track
|
||||
|
||||
@handle_pattern(r'^previous$')
|
||||
def previous(frontend):
|
||||
"""
|
||||
|
||||
@ -86,6 +86,10 @@ def load(frontend, name):
|
||||
``load {NAME}``
|
||||
|
||||
Loads the playlist ``NAME.m3u`` from the playlist directory.
|
||||
|
||||
*Clarifications:*
|
||||
|
||||
- ``load`` appends the given playlist to the current playlist.
|
||||
"""
|
||||
matches = frontend.backend.stored_playlists.search(name)
|
||||
if matches:
|
||||
@ -139,9 +143,9 @@ def playlistmove(frontend, name, from_pos, to_pos):
|
||||
|
||||
*Clarifications:*
|
||||
|
||||
- The second argument is not a ``SONGID`` as used elsewhere in the
|
||||
protocol documentation, but just the ``SONGPOS`` to move *from*,
|
||||
i.e. ``playlistmove {NAME} {FROM_SONGPOS} {TO_SONGPOS}``.
|
||||
- The second argument is not a ``SONGID`` as used elsewhere in the protocol
|
||||
documentation, but just the ``SONGPOS`` to move *from*, i.e.
|
||||
``playlistmove {NAME} {FROM_SONGPOS} {TO_SONGPOS}``.
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
|
||||
|
||||
@ -91,12 +91,6 @@ class BaseCurrentPlaylistControllerTest(object):
|
||||
self.controller.clear()
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
|
||||
def test_load(self):
|
||||
tracks = []
|
||||
self.assertNotEqual(id(tracks), id(self.controller.tracks))
|
||||
self.controller.load(tracks)
|
||||
self.assertEqual(tracks, self.controller.tracks)
|
||||
|
||||
def test_get_by_uri_returns_unique_match(self):
|
||||
track = Track(uri='a')
|
||||
self.controller.load([Track(uri='z'), track, Track(uri='y')])
|
||||
@ -136,10 +130,15 @@ class BaseCurrentPlaylistControllerTest(object):
|
||||
self.controller.load([track1, track2, track3])
|
||||
self.assertEqual(track2, self.controller.get(uri='b')[1])
|
||||
|
||||
@populate_playlist
|
||||
def test_load_replaces_playlist(self):
|
||||
self.backend.current_playlist.load([])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks), 0)
|
||||
def test_load_appends_to_the_current_playlist(self):
|
||||
self.controller.load([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(len(self.controller.tracks), 2)
|
||||
self.controller.load([Track(uri='c'), Track(uri='d')])
|
||||
self.assertEqual(len(self.controller.tracks), 4)
|
||||
self.assertEqual(self.controller.tracks[0].uri, 'a')
|
||||
self.assertEqual(self.controller.tracks[1].uri, 'b')
|
||||
self.assertEqual(self.controller.tracks[2].uri, 'c')
|
||||
self.assertEqual(self.controller.tracks[3].uri, 'd')
|
||||
|
||||
def test_load_does_not_reset_version(self):
|
||||
version = self.controller.version
|
||||
@ -148,22 +147,17 @@ class BaseCurrentPlaylistControllerTest(object):
|
||||
|
||||
@populate_playlist
|
||||
def test_load_preserves_playing_state(self):
|
||||
tracks = self.controller.tracks
|
||||
playback = self.playback
|
||||
|
||||
self.playback.play()
|
||||
self.controller.load([tracks[1]])
|
||||
self.assertEqual(playback.state, playback.PLAYING)
|
||||
self.assertEqual(tracks[1], self.playback.current_track)
|
||||
track = self.playback.current_track
|
||||
self.controller.load(self.controller.tracks[1:2])
|
||||
self.assertEqual(self.playback.state, self.playback.PLAYING)
|
||||
self.assertEqual(self.playback.current_track, track)
|
||||
|
||||
@populate_playlist
|
||||
def test_load_preserves_stopped_state(self):
|
||||
tracks = self.controller.tracks
|
||||
playback = self.playback
|
||||
|
||||
self.controller.load([tracks[2]])
|
||||
self.assertEqual(playback.state, playback.STOPPED)
|
||||
self.assertEqual(None, self.playback.current_track)
|
||||
self.controller.load(self.controller.tracks[1:2])
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
|
||||
@populate_playlist
|
||||
def test_move_single(self):
|
||||
@ -351,6 +345,14 @@ class BasePlaybackControllerTest(object):
|
||||
self.playback.play(self.current_playlist.cp_tracks[-1])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[-1])
|
||||
|
||||
@populate_playlist
|
||||
def test_play_skips_to_next_track_on_failure(self):
|
||||
# If _play() returns False, it is a failure.
|
||||
self.playback._play = lambda track: track != self.tracks[0]
|
||||
self.playback.play()
|
||||
self.assertNotEqual(self.playback.current_track, self.tracks[0])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
def test_current_track_after_completed_playlist(self):
|
||||
self.playback.play(self.current_playlist.cp_tracks[-1])
|
||||
@ -417,6 +419,16 @@ class BasePlaybackControllerTest(object):
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
def test_next_skips_to_next_track_on_failure(self):
|
||||
# If _play() returns False, it is a failure.
|
||||
self.playback._play = lambda track: track != self.tracks[1]
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
self.playback.next()
|
||||
self.assertNotEqual(self.playback.current_track, self.tracks[1])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[2])
|
||||
|
||||
@populate_playlist
|
||||
def test_previous(self):
|
||||
self.playback.play()
|
||||
@ -457,6 +469,16 @@ class BasePlaybackControllerTest(object):
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
|
||||
@populate_playlist
|
||||
def test_previous_skips_to_previous_track_on_failure(self):
|
||||
# If _play() returns False, it is a failure.
|
||||
self.playback._play = lambda track: track != self.tracks[1]
|
||||
self.playback.play(self.current_playlist.cp_tracks[2])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[2])
|
||||
self.playback.previous()
|
||||
self.assertNotEqual(self.playback.current_track, self.tracks[1])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
def test_next_track_before_play(self):
|
||||
self.assertEqual(self.playback.next_track, self.tracks[0])
|
||||
@ -575,15 +597,15 @@ class BasePlaybackControllerTest(object):
|
||||
self.playback.end_of_track_callback()
|
||||
self.assertEqual(self.playback.current_playlist_position, None)
|
||||
|
||||
def test_new_playlist_loaded_callback_gets_called(self):
|
||||
callback = self.playback.new_playlist_loaded_callback
|
||||
def test_on_current_playlist_change_gets_called(self):
|
||||
callback = self.playback.on_current_playlist_change
|
||||
|
||||
def wrapper():
|
||||
wrapper.called = True
|
||||
return callback()
|
||||
wrapper.called = False
|
||||
|
||||
self.playback.new_playlist_loaded_callback = wrapper
|
||||
self.playback.on_current_playlist_change = wrapper
|
||||
self.backend.current_playlist.load([])
|
||||
|
||||
self.assert_(wrapper.called)
|
||||
@ -608,27 +630,28 @@ class BasePlaybackControllerTest(object):
|
||||
self.assert_(event.is_set())
|
||||
|
||||
@populate_playlist
|
||||
def test_new_playlist_loaded_callback_when_playing(self):
|
||||
def test_on_current_playlist_change_when_playing(self):
|
||||
self.playback.play()
|
||||
current_track = self.playback.current_track
|
||||
self.backend.current_playlist.load([self.tracks[2]])
|
||||
self.assertEqual(self.playback.state, self.playback.PLAYING)
|
||||
self.assertEqual(self.playback.current_track, self.tracks[2])
|
||||
self.assertEqual(self.playback.current_track, current_track)
|
||||
|
||||
@populate_playlist
|
||||
def test_new_playlist_loaded_callback_when_stopped(self):
|
||||
def test_on_current_playlist_change_when_stopped(self):
|
||||
current_track = self.playback.current_track
|
||||
self.backend.current_playlist.load([self.tracks[2]])
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
self.assertEqual(self.playback.next_track, self.tracks[2])
|
||||
|
||||
@populate_playlist
|
||||
def test_new_playlist_loaded_callback_when_paused(self):
|
||||
def test_on_current_playlist_change_when_paused(self):
|
||||
self.playback.play()
|
||||
self.playback.pause()
|
||||
current_track = self.playback.current_track
|
||||
self.backend.current_playlist.load([self.tracks[2]])
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
self.assertEqual(self.playback.next_track, self.tracks[2])
|
||||
self.assertEqual(self.playback.state, self.backend.playback.PAUSED)
|
||||
self.assertEqual(self.playback.current_track, current_track)
|
||||
|
||||
@populate_playlist
|
||||
def test_pause_when_stopped(self):
|
||||
@ -915,7 +938,7 @@ class BasePlaybackControllerTest(object):
|
||||
self.playback.random = True
|
||||
self.assertEqual(self.playback.next_track, self.tracks[2])
|
||||
self.backend.current_playlist.load(self.tracks[:1])
|
||||
self.assertEqual(self.playback.next_track, self.tracks[0])
|
||||
self.assertEqual(self.playback.next_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
def test_played_track_during_random_not_played_again(self):
|
||||
@ -927,13 +950,9 @@ class BasePlaybackControllerTest(object):
|
||||
played.append(self.playback.current_track)
|
||||
self.playback.next()
|
||||
|
||||
def test_playing_track_with_invalid_uri(self):
|
||||
self.backend.current_playlist.load([Track(uri='foobar')])
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.state, self.playback.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
def test_playing_track_that_isnt_in_playlist(self):
|
||||
test = lambda: self.playback.play(self.tracks[0])
|
||||
test = lambda: self.playback.play((17, Track()))
|
||||
self.assertRaises(AssertionError, test)
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -225,13 +225,25 @@ class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(result[0], u'ACK [2@0] {play} Bad song index')
|
||||
self.assertEqual(self.b.playback.STOPPED, self.b.playback.state)
|
||||
|
||||
def test_play_minus_one_plays_first_in_playlist(self):
|
||||
track = Track()
|
||||
self.b.current_playlist.load([track])
|
||||
def test_play_minus_one_plays_first_in_playlist_if_no_current_track(self):
|
||||
self.assertEqual(self.b.playback.current_track, None)
|
||||
self.b.current_playlist.load([Track(uri='a'), Track(uri='b')])
|
||||
result = self.h.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(self.b.playback.PLAYING, self.b.playback.state)
|
||||
self.assertEqual(self.b.playback.current_track, track)
|
||||
self.assertEqual(self.b.playback.current_track.uri, 'a')
|
||||
|
||||
def test_play_minus_one_plays_current_track_if_current_track_is_set(self):
|
||||
self.b.current_playlist.load([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(self.b.playback.current_track, None)
|
||||
self.b.playback.play()
|
||||
self.b.playback.next()
|
||||
self.b.playback.stop()
|
||||
self.assertNotEqual(self.b.playback.current_track, None)
|
||||
result = self.h.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(self.b.playback.PLAYING, self.b.playback.state)
|
||||
self.assertEqual(self.b.playback.current_track.uri, 'b')
|
||||
|
||||
def test_play_minus_one_on_empty_playlist_does_not_ack(self):
|
||||
self.b.current_playlist.clear()
|
||||
@ -246,13 +258,25 @@ class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(self.b.playback.PLAYING, self.b.playback.state)
|
||||
|
||||
def test_playid_minus_one_plays_first_in_playlist(self):
|
||||
track = Track()
|
||||
self.b.current_playlist.load([track])
|
||||
def test_playid_minus_one_plays_first_in_playlist_if_no_current_track(self):
|
||||
self.assertEqual(self.b.playback.current_track, None)
|
||||
self.b.current_playlist.load([Track(uri='a'), Track(uri='b')])
|
||||
result = self.h.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(self.b.playback.PLAYING, self.b.playback.state)
|
||||
self.assertEqual(self.b.playback.current_track, track)
|
||||
self.assertEqual(self.b.playback.current_track.uri, 'a')
|
||||
|
||||
def test_play_minus_one_plays_current_track_if_current_track_is_set(self):
|
||||
self.b.current_playlist.load([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(self.b.playback.current_track, None)
|
||||
self.b.playback.play()
|
||||
self.b.playback.next()
|
||||
self.b.playback.stop()
|
||||
self.assertNotEqual(self.b.playback.current_track, None)
|
||||
result = self.h.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(self.b.playback.PLAYING, self.b.playback.state)
|
||||
self.assertEqual(self.b.playback.current_track.uri, 'b')
|
||||
|
||||
def test_playid_minus_one_on_empty_playlist_does_not_ack(self):
|
||||
self.b.current_playlist.clear()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user