Merge branch 'develop' into feature/http-frontend
This commit is contained in:
commit
532a915db8
280
docs/changes.rst
280
docs/changes.rst
@ -5,14 +5,30 @@ Changes
|
||||
This change log is used to track all major changes to Mopidy.
|
||||
|
||||
|
||||
v0.9.0 (in development)
|
||||
=======================
|
||||
|
||||
**Multiple backends support**
|
||||
v0.9.0 (2012-11-21)
|
||||
===================
|
||||
|
||||
Support for using the local and Spotify backends simultaneously have for a very
|
||||
long time been our most requested feature. Finally, it's here!
|
||||
|
||||
**Dependencies**
|
||||
|
||||
- pyspotify >= 1.9, < 1.10 is now required for Spotify support.
|
||||
|
||||
**Documentation**
|
||||
|
||||
- New :ref:`installation` guides, organized by OS and distribution so that you
|
||||
can follow one concise list of instructions instead of jumping around the
|
||||
docs to look for instructions for each dependency.
|
||||
|
||||
- Moved :ref:`raspberrypi-installation` howto from the wiki to the docs.
|
||||
|
||||
- Updated :ref:`mpd-clients` overview.
|
||||
|
||||
- Added :ref:`mpris-clients` and :ref:`upnp-clients` overview.
|
||||
|
||||
**Multiple backends support**
|
||||
|
||||
- Both the local backend and the Spotify backend are now turned on by default.
|
||||
The local backend is listed first in the :attr:`mopidy.settings.BACKENDS`
|
||||
setting, and are thus given the highest priority in e.g. search results,
|
||||
@ -24,8 +40,85 @@ long time been our most requested feature. Finally, it's here!
|
||||
As always, see :mod:`mopidy.settings` for the full list of available
|
||||
settings.
|
||||
|
||||
**Spotify backend**
|
||||
|
||||
- The Spotify backend now includes release year and artist on albums.
|
||||
|
||||
- :issue:`233`: The Spotify backend now returns the track if you search for the
|
||||
Spotify track URI.
|
||||
|
||||
- Added support for connecting to the Spotify service through an HTTP or SOCKS
|
||||
proxy, which is supported by pyspotify >= 1.9.
|
||||
|
||||
- Subscriptions to other Spotify user's "starred" playlists are ignored, as
|
||||
they currently isn't fully supported by pyspotify.
|
||||
|
||||
**Local backend**
|
||||
|
||||
- :issue:`236`: The ``mopidy-scan`` command failed to include tags from ALAC
|
||||
files (Apple lossless) because it didn't support multiple tag messages from
|
||||
GStreamer per track it scanned.
|
||||
|
||||
- Added support for search by filename to local backend.
|
||||
|
||||
**MPD frontend**
|
||||
|
||||
- :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now
|
||||
accepts unquoted playlist names if they don't contain spaces.
|
||||
|
||||
- :issue:`246`: The MPD command ``list album artist ""`` and similar
|
||||
``search``, ``find``, and ``list`` commands with empty filter values caused a
|
||||
:exc:`LookupError`, but should have been ignored by the MPD server.
|
||||
|
||||
- The MPD frontend no longer lowercases search queries. This broke e.g. search
|
||||
by URI, where casing may be essential.
|
||||
|
||||
- The MPD command ``plchanges`` always returned the entire playlist. It now
|
||||
returns an empty response when the client has seen the latest version.
|
||||
|
||||
- The MPD commands ``search`` and ``find`` now allows the key ``file``, which
|
||||
is used by ncmpcpp instead of ``filename``.
|
||||
|
||||
- The MPD commands ``search`` and ``find`` now allow search query values to be
|
||||
empty strings.
|
||||
|
||||
- The MPD command ``listplaylists`` will no longer return playlists without a
|
||||
name. This could crash ncmpcpp.
|
||||
|
||||
- The MPD command ``list`` will no longer return artist names, album names, or
|
||||
dates that are blank.
|
||||
|
||||
- The MPD command ``decoders`` will now return an empty response instead of a
|
||||
"not implemented" error to make the ncmpcpp browse view work the first time
|
||||
it is opened.
|
||||
|
||||
**MPRIS frontend**
|
||||
|
||||
- The MPRIS playlists interface is now supported by our MPRIS frontend. This
|
||||
means that you now can select playlists to queue and play from the Ubuntu
|
||||
Sound Menu.
|
||||
|
||||
**Audio mixers**
|
||||
|
||||
- Made the :mod:`NAD mixer <mopidy.audio.mixers.nad>` responsive to interrupts
|
||||
during amplifier calibration. It will now quit immediately, while previously
|
||||
it completed the calibration first, and then quit, which could take more than
|
||||
15 seconds.
|
||||
|
||||
**Developer support**
|
||||
|
||||
- Added optional background thread for debugging deadlocks. When the feature is
|
||||
enabled via the ``--debug-thread`` option or
|
||||
:attr:`mopidy.settings.DEBUG_THREAD` setting a ``SIGUSR1`` signal will dump
|
||||
the traceback for all running threads.
|
||||
|
||||
- The settings validator will now allow any setting prefixed with ``CUSTOM_``
|
||||
to exist in the settings file.
|
||||
|
||||
**Internal changes**
|
||||
|
||||
Internally, Mopidy have seen a lot of changes to pave the way for multiple
|
||||
backends:
|
||||
backends and the future HTTP frontend.
|
||||
|
||||
- A new layer and actor, "core", has been added to our stack, inbetween the
|
||||
frontends and the backends. The responsibility of the core layer and actor is
|
||||
@ -36,12 +129,6 @@ backends:
|
||||
Frontends no longer know anything about the backends. They just use the
|
||||
:ref:`core-api`.
|
||||
|
||||
- The base playback provider has been updated with sane default behavior
|
||||
instead of empty functions. By default, the playback provider now lets
|
||||
GStreamer keep track of the current track's time position. The local backend
|
||||
simply uses the base playback provider without any changes. The same applies
|
||||
to any future backend that just needs GStreamer to play an URI for it.
|
||||
|
||||
- The dependency graph between the core controllers and the backend providers
|
||||
have been straightened out, so that we don't have any circular dependencies.
|
||||
The frontend, core, backend, and audio layers are now strictly separate. The
|
||||
@ -51,118 +138,113 @@ backends:
|
||||
broadcasting of events to listeners, through e.g.
|
||||
:class:`mopidy.core.CoreListener` and :class:`mopidy.audio.AudioListener`.
|
||||
|
||||
See :ref:`concepts` for more details and illustrations of all the relations.
|
||||
|
||||
- All dependencies are now explicitly passed to the constructors of the
|
||||
frontends, core, and the backends. This makes testing each layer with
|
||||
dummy/mocked lower layers easier than with the old variant, where
|
||||
dependencies where looked up in Pykka's actor registry.
|
||||
|
||||
- Renamed "current playlist" to "tracklist" everywhere, including the core API
|
||||
used by frontends.
|
||||
- All properties in the core API now got getters, and setters if setting them
|
||||
is allowed. They are not explictly listed in the docs as they have the same
|
||||
behavior as the documented properties, but they are available and may be
|
||||
used. This is useful for the future HTTP frontend.
|
||||
|
||||
- Renamed "stored playlists" to "playlists" everywhere, including the core API
|
||||
used by frontends.
|
||||
|
||||
- The playlists part of the core API has been revised to be more focused around
|
||||
the playlist URI, and some redundant functionality has been removed:
|
||||
|
||||
- :attr:`mopidy.core.PlaylistsController.playlists` no longer supports
|
||||
assignment to it. The `playlists` property on the backend layer still does,
|
||||
and all functionality is maintained by assigning to the playlists
|
||||
collections at the backend level.
|
||||
|
||||
- :meth:`mopidy.core.PlaylistsController.delete` now accepts an URI, and not
|
||||
a playlist object.
|
||||
|
||||
- :meth:`mopidy.core.PlaylistsController.save` now returns the saved
|
||||
playlist. The returned playlist may differ from the saved playlist, and
|
||||
should thus be used instead of the playlist passed to ``save()``.
|
||||
|
||||
- :meth:`mopidy.core.PlaylistsController.rename` has been removed, since
|
||||
renaming can be done with ``save()``.
|
||||
|
||||
**Changes**
|
||||
|
||||
- Made the :mod:`NAD mixer <mopidy.audio.mixers.nad>` responsive to interrupts
|
||||
during amplifier calibration. It will now quit immediately, while previously
|
||||
it completed the calibration first, and then quit, which could take more than
|
||||
15 seconds.
|
||||
*Models:*
|
||||
|
||||
- Added :attr:`mopidy.models.Album.date` attribute. It has the same format as
|
||||
the existing :attr:`mopidy.models.Track.date`.
|
||||
|
||||
- The Spotify backend now includes release year and artist on albums.
|
||||
- Added :class:`mopidy.models.ModelJSONEncoder` and
|
||||
:func:`mopidy.models.model_json_decoder` for automatic JSON serialization and
|
||||
deserialization of data structures which contains Mopidy models. This is
|
||||
useful for the future HTTP frontend.
|
||||
|
||||
- Added support for search by filename to local backend.
|
||||
*Library:*
|
||||
|
||||
- Added optional background thread for debugging deadlocks. When the feature is
|
||||
enabled via the ``--debug-thread`` option or
|
||||
:attr:`mopidy.settings.DEBUG_THREAD` setting a ``SIGUSR1`` signal will dump
|
||||
the traceback for all running threads.
|
||||
- :meth:`mopidy.core.LibraryController.find_exact` and
|
||||
:meth:`mopidy.core.LibraryController.search` now returns plain lists of
|
||||
tracks instead of playlist objects.
|
||||
|
||||
- Make the entire code base use unicode strings by default, and only fall back
|
||||
to bytestrings where it is required. Another step closer to Python 3.
|
||||
- :meth:`mopidy.core.LibraryController.lookup` now returns a list of tracks
|
||||
instead of a single track. This makes it possible to support lookup of
|
||||
artist or album URIs which then can expand to a list of tracks.
|
||||
|
||||
- The settings validator will now allow any setting prefixed with ``CUSTOM_``
|
||||
to exist in the settings file.
|
||||
*Playback:*
|
||||
|
||||
- The MPD commands ``search`` and ``find`` now allows the key ``file``, which
|
||||
is used by ncmpcpp instead of ``filename``.
|
||||
- The base playback provider has been updated with sane default behavior
|
||||
instead of empty functions. By default, the playback provider now lets
|
||||
GStreamer keep track of the current track's time position. The local backend
|
||||
simply uses the base playback provider without any changes. Any future
|
||||
backend that just feeds URIs to GStreamer to play can also use the base
|
||||
playback provider without any changes.
|
||||
|
||||
- The Spotify backend now returns the track if you search for the Spotify track
|
||||
URI. (Fixes: :issue:`233`)
|
||||
- Removed :attr:`mopidy.core.PlaybackController.track_at_previous`. Use
|
||||
:attr:`mopidy.core.PlaybackController.tl_track_at_previous` instead.
|
||||
|
||||
- :meth:`mopidy.core.TracklistController.append` now returns a list of the
|
||||
:class:`mopidy.models.TlTrack` instances that was added to the tracklist.
|
||||
This makes it easier to start playing one of the tracks that was just
|
||||
appended to the tracklist.
|
||||
- Removed :attr:`mopidy.core.PlaybackController.track_at_next`. Use
|
||||
:attr:`mopidy.core.PlaybackController.tl_track_at_next` instead.
|
||||
|
||||
- Removed :attr:`mopidy.core.PlaybackController.track_at_eot`. Use
|
||||
:attr:`mopidy.core.PlaybackController.tl_track_at_eot` instead.
|
||||
|
||||
- Removed :attr:`mopidy.core.PlaybackController.current_tlid`. Use
|
||||
:attr:`mopidy.core.PlaybackController.current_tl_track` instead.
|
||||
|
||||
*Playlists:*
|
||||
|
||||
The playlists part of the core API has been revised to be more focused around
|
||||
the playlist URI, and some redundant functionality has been removed:
|
||||
|
||||
- Renamed "stored playlists" to "playlists" everywhere, including the core API
|
||||
used by frontends.
|
||||
|
||||
- :attr:`mopidy.core.PlaylistsController.playlists` no longer supports
|
||||
assignment to it. The `playlists` property on the backend layer still does,
|
||||
and all functionality is maintained by assigning to the playlists collections
|
||||
at the backend level.
|
||||
|
||||
- :meth:`mopidy.core.PlaylistsController.delete` now accepts an URI, and not a
|
||||
playlist object.
|
||||
|
||||
- :meth:`mopidy.core.PlaylistsController.save` now returns the saved playlist.
|
||||
The returned playlist may differ from the saved playlist, and should thus be
|
||||
used instead of the playlist passed to
|
||||
:meth:`mopidy.core.PlaylistsController.save`.
|
||||
|
||||
- :meth:`mopidy.core.PlaylistsController.rename` has been removed, since
|
||||
renaming can be done with :meth:`mopidy.core.PlaylistsController.save`.
|
||||
|
||||
- :meth:`mopidy.core.PlaylistsController.get` has been replaced by
|
||||
:meth:`mopidy.core.PlaylistsController.filter`.
|
||||
|
||||
- The event :meth:`mopidy.core.CoreListener.playlist_changed` has been changed
|
||||
to include the playlist that was changed.
|
||||
|
||||
*Tracklist:*
|
||||
|
||||
- Renamed "current playlist" to "tracklist" everywhere, including the core API
|
||||
used by frontends.
|
||||
|
||||
- Removed :meth:`mopidy.core.TracklistController.append`. Use
|
||||
:meth:`mopidy.core.TracklistController.add` instead, which is now capable of
|
||||
adding multiple tracks.
|
||||
|
||||
- :meth:`mopidy.core.TracklistController.get` has been replaced by
|
||||
:meth:`mopidy.core.TracklistController.filter`.
|
||||
|
||||
- :meth:`mopidy.core.TracklistController.remove` can now remove multiple
|
||||
tracks, and returns the tracks it removed.
|
||||
|
||||
- When the tracklist is changed, we now trigger the new
|
||||
:meth:`mopidy.core.CoreListener.tracklist_changed` event. Previously we
|
||||
triggered :meth:`mopidy.core.CoreListener.playlist_changed`, which is
|
||||
intended for stored playlists, not the tracklist.
|
||||
|
||||
- The event :meth:`mopidy.core.CoreListener.playlist_changed` has been changed
|
||||
to include the playlist that was changed.
|
||||
*Towards Python 3 support:*
|
||||
|
||||
- The MPRIS playlists interface is now supported by our MPRIS frontend. This
|
||||
means that you now can select playlists to queue and play from the Ubuntu
|
||||
Sound Menu.
|
||||
|
||||
- :meth:`mopidy.core.LibraryController.find_exact` and
|
||||
:meth:`mopidy.core.LibraryController.search` now returns plain lists of
|
||||
tracks instead of playlist objects.
|
||||
|
||||
- :meth:`mopidy.core.TracklistController.get` has been replaced by
|
||||
:meth:`mopidy.core.TracklistController.filter`.
|
||||
|
||||
- :meth:`mopidy.core.PlaylistsController.get` has been replaced by
|
||||
:meth:`mopidy.core.PlaylistsController.filter`.
|
||||
|
||||
- :meth:`mopidy.core.TracklistController.remove` can now remove multiple
|
||||
tracks, and returns the tracks it removed.
|
||||
|
||||
- :meth:`mopidy.core.LibraryController.lookup` now returns a list of tracks.
|
||||
This makes it possible to support lookup of artist or album URIs which then
|
||||
can expand to a list of tracks.
|
||||
|
||||
**Bug fixes**
|
||||
|
||||
- :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now
|
||||
accepts unquotes playlist names if they don't contain spaces.
|
||||
|
||||
- The MPD command ``plchanges`` always returned the entire playlist. It now
|
||||
returns an empty response when the client has seen the latest version.
|
||||
|
||||
- MPD no longer lowercases search queries. This broke e.g. search by URI, where
|
||||
casing may be essential.
|
||||
|
||||
- :issue:`236`: The ``mopidy-scan`` command failed to include tags from ALAC
|
||||
files (Apple lossless) because it didn't support multiple tag messages from
|
||||
GStreamer per track it scanned.
|
||||
|
||||
- :issue:`246`: The MPD command ``list album artist ""`` and similar
|
||||
``search``, ``find``, and ``list`` commands with empty filter values caused a
|
||||
:exc:`LookupError`, but should have been ignored by the MPD server.
|
||||
- Make the entire code base use unicode strings by default, and only fall back
|
||||
to bytestrings where it is required. Another step closer to Python 3.
|
||||
|
||||
|
||||
v0.8.1 (2012-10-30)
|
||||
|
||||
@ -23,7 +23,7 @@ if (isinstance(pykka.__version__, basestring)
|
||||
warnings.filterwarnings('ignore', 'could not open display')
|
||||
|
||||
|
||||
__version__ = '0.8.1'
|
||||
__version__ = '0.9.0'
|
||||
|
||||
|
||||
from mopidy import settings as default_settings_module
|
||||
|
||||
@ -37,9 +37,11 @@ class DummyLibraryProvider(base.BaseLibraryProvider):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DummyLibraryProvider, self).__init__(*args, **kwargs)
|
||||
self.dummy_library = []
|
||||
self.dummy_find_exact_result = []
|
||||
self.dummy_search_result = []
|
||||
|
||||
def find_exact(self, **query):
|
||||
return []
|
||||
return self.dummy_find_exact_result
|
||||
|
||||
def lookup(self, uri):
|
||||
return filter(lambda t: uri == t.uri, self.dummy_library)
|
||||
@ -48,7 +50,7 @@ class DummyLibraryProvider(base.BaseLibraryProvider):
|
||||
pass
|
||||
|
||||
def search(self, **query):
|
||||
return []
|
||||
return self.dummy_search_result
|
||||
|
||||
|
||||
class DummyPlaybackProvider(base.BasePlaybackProvider):
|
||||
|
||||
@ -21,7 +21,7 @@ https://github.com/mopidy/mopidy/issues?labels=Spotify+backend
|
||||
**Dependencies:**
|
||||
|
||||
- libspotify >= 12, < 13 (libspotify12 package from apt.mopidy.com)
|
||||
- pyspotify >= 1.8, < 1.9 (python-spotify package from apt.mopidy.com)
|
||||
- pyspotify >= 1.9, < 1.10 (python-spotify package from apt.mopidy.com)
|
||||
|
||||
**Settings:**
|
||||
|
||||
|
||||
@ -31,9 +31,14 @@ class SpotifyBackend(pykka.ThreadingActor, base.Backend):
|
||||
# Fail early if settings are not present
|
||||
username = settings.SPOTIFY_USERNAME
|
||||
password = settings.SPOTIFY_PASSWORD
|
||||
proxy = settings.SPOTIFY_PROXY_HOST
|
||||
proxy_username = settings.SPOTIFY_PROXY_USERNAME
|
||||
proxy_password = settings.SPOTIFY_PROXY_PASSWORD
|
||||
|
||||
self.spotify = SpotifySessionManager(
|
||||
username, password, audio=audio, backend_ref=self.actor_ref)
|
||||
username, password, audio=audio, backend_ref=self.actor_ref,
|
||||
proxy=proxy, proxy_username=proxy_username,
|
||||
proxy_password=proxy_password)
|
||||
|
||||
def on_start(self):
|
||||
logger.info('Mopidy uses SPOTIFY(R) CORE')
|
||||
|
||||
@ -46,10 +46,11 @@ class SpotifyPlaybackProvider(base.BasePlaybackProvider):
|
||||
|
||||
def resume(self):
|
||||
time_position = self.get_time_position()
|
||||
|
||||
self._timer.resume()
|
||||
|
||||
return self.seek(time_position)
|
||||
self.audio.prepare_change()
|
||||
result = self.seek(time_position)
|
||||
self.audio.start_playback()
|
||||
return result
|
||||
|
||||
def seek(self, time_position):
|
||||
self.backend.spotify.session.seek(time_position)
|
||||
|
||||
@ -10,7 +10,7 @@ import threading
|
||||
|
||||
from spotify.manager import SpotifySessionManager as PyspotifySessionManager
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy import audio, settings
|
||||
from mopidy.backends.listener import BackendListener
|
||||
from mopidy.utils import process, versioning
|
||||
|
||||
@ -32,8 +32,12 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
|
||||
appkey_file = os.path.join(os.path.dirname(__file__), 'spotify_appkey.key')
|
||||
user_agent = 'Mopidy %s' % versioning.get_version()
|
||||
|
||||
def __init__(self, username, password, audio, backend_ref):
|
||||
PyspotifySessionManager.__init__(self, username, password)
|
||||
def __init__(self, username, password, audio, backend_ref, proxy=None,
|
||||
proxy_username=None, proxy_password=None):
|
||||
PyspotifySessionManager.__init__(
|
||||
self, username, password, proxy=proxy,
|
||||
proxy_username=proxy_username,
|
||||
proxy_password=proxy_password)
|
||||
process.BaseThread.__init__(self)
|
||||
self.name = 'SpotifyThread'
|
||||
|
||||
@ -88,7 +92,8 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
|
||||
logger.info('Spotify connection OK')
|
||||
else:
|
||||
logger.error('Spotify connection error: %s', error)
|
||||
self.backend.playback.pause()
|
||||
if self.audio.state.get() == audio.PlaybackState.PLAYING:
|
||||
self.backend.playback.pause()
|
||||
|
||||
def message_to_user(self, session, message):
|
||||
"""Callback used by pyspotify"""
|
||||
|
||||
@ -56,6 +56,10 @@ def to_mopidy_playlist(spotify_playlist):
|
||||
uri = str(Link.from_playlist(spotify_playlist))
|
||||
if not spotify_playlist.is_loaded():
|
||||
return Playlist(uri=uri, name='[loading...]')
|
||||
if not spotify_playlist.name():
|
||||
# Other user's "starred" playlists isn't handled properly by pyspotify
|
||||
# See https://github.com/mopidy/pyspotify/issues/81
|
||||
return
|
||||
return Playlist(
|
||||
uri=uri,
|
||||
name=spotify_playlist.name(),
|
||||
|
||||
@ -46,14 +46,15 @@ class Core(pykka.ThreadingActor, AudioListener, BackendListener):
|
||||
|
||||
self.tracklist = TracklistController(core=self)
|
||||
|
||||
@property
|
||||
def uri_schemes(self):
|
||||
"""List of URI schemes we can handle"""
|
||||
def get_uri_schemes(self):
|
||||
futures = [b.uri_schemes for b in self.backends]
|
||||
results = pykka.get_all(futures)
|
||||
uri_schemes = itertools.chain(*results)
|
||||
return sorted(uri_schemes)
|
||||
|
||||
uri_schemes = property(get_uri_schemes)
|
||||
"""List of URI schemes we can handle"""
|
||||
|
||||
def reached_end_of_stream(self):
|
||||
self.playback.on_end_of_track()
|
||||
|
||||
|
||||
@ -12,56 +12,12 @@ from . import listener
|
||||
logger = logging.getLogger('mopidy.core')
|
||||
|
||||
|
||||
def option_wrapper(name, default):
|
||||
def get_option(self):
|
||||
return getattr(self, name, default)
|
||||
|
||||
def set_option(self, value):
|
||||
if getattr(self, name, default) != value:
|
||||
# pylint: disable = W0212
|
||||
self._trigger_options_changed()
|
||||
# pylint: enable = W0212
|
||||
return setattr(self, name, value)
|
||||
|
||||
return property(get_option, set_option)
|
||||
|
||||
|
||||
class PlaybackController(object):
|
||||
# pylint: disable = R0902
|
||||
# Too many instance attributes
|
||||
|
||||
pykka_traversable = True
|
||||
|
||||
#: :class:`True`
|
||||
#: Tracks are removed from the playlist when they have been played.
|
||||
#: :class:`False`
|
||||
#: Tracks are not removed from the playlist.
|
||||
consume = option_wrapper('_consume', False)
|
||||
|
||||
#: The currently playing or selected :class:`mopidy.models.TlTrack`, or
|
||||
#: :class:`None`.
|
||||
current_tl_track = None
|
||||
|
||||
#: :class:`True`
|
||||
#: Tracks are selected at random from the playlist.
|
||||
#: :class:`False`
|
||||
#: Tracks are played in the order of the playlist.
|
||||
random = option_wrapper('_random', False)
|
||||
|
||||
#: :class:`True`
|
||||
#: The current playlist is played repeatedly. To repeat a single track,
|
||||
#: select both :attr:`repeat` and :attr:`single`.
|
||||
#: :class:`False`
|
||||
#: The current playlist is played once.
|
||||
repeat = option_wrapper('_repeat', False)
|
||||
|
||||
#: :class:`True`
|
||||
#: Playback is stopped after current song, unless in :attr:`repeat`
|
||||
#: mode.
|
||||
#: :class:`False`
|
||||
#: Playback continues after current song.
|
||||
single = option_wrapper('_single', False)
|
||||
|
||||
def __init__(self, audio, backends, core):
|
||||
self.audio = audio
|
||||
self.backends = backends
|
||||
@ -79,42 +35,128 @@ class PlaybackController(object):
|
||||
uri_scheme = urlparse.urlparse(uri).scheme
|
||||
return self.backends.with_playback_by_uri_scheme.get(uri_scheme, None)
|
||||
|
||||
def _get_tlid(self, tl_track):
|
||||
if tl_track is None:
|
||||
return None
|
||||
return tl_track.tlid
|
||||
### Properties
|
||||
|
||||
def _get_track(self, tl_track):
|
||||
if tl_track is None:
|
||||
return None
|
||||
return tl_track.track
|
||||
def get_consume(self):
|
||||
return getattr(self, '_consume', False)
|
||||
|
||||
@property
|
||||
def current_tlid(self):
|
||||
"""
|
||||
The TLID (tracklist ID) of the currently playing or selected
|
||||
track.
|
||||
def set_consume(self, value):
|
||||
if self.get_consume() != value:
|
||||
self._trigger_options_changed()
|
||||
return setattr(self, '_consume', value)
|
||||
|
||||
Read-only. Extracted from :attr:`current_tl_track` for convenience.
|
||||
"""
|
||||
return self._get_tlid(self.current_tl_track)
|
||||
consume = property(get_consume, set_consume)
|
||||
"""
|
||||
:class:`True`
|
||||
Tracks are removed from the playlist when they have been played.
|
||||
:class:`False`
|
||||
Tracks are not removed from the playlist.
|
||||
"""
|
||||
|
||||
@property
|
||||
def current_track(self):
|
||||
"""
|
||||
The currently playing or selected :class:`mopidy.models.Track`.
|
||||
current_tl_track = None
|
||||
"""
|
||||
The currently playing or selected :class:`mopidy.models.TlTrack`, or
|
||||
:class:`None`.
|
||||
"""
|
||||
|
||||
Read-only. Extracted from :attr:`current_tl_track` for convenience.
|
||||
"""
|
||||
return self._get_track(self.current_tl_track)
|
||||
def get_current_track(self):
|
||||
return self.current_tl_track and self.current_tl_track.track
|
||||
|
||||
@property
|
||||
def tracklist_position(self):
|
||||
"""
|
||||
The position of the current track in the tracklist.
|
||||
current_track = property(get_current_track)
|
||||
"""
|
||||
The currently playing or selected :class:`mopidy.models.Track`.
|
||||
|
||||
Read-only.
|
||||
"""
|
||||
Read-only. Extracted from :attr:`current_tl_track` for convenience.
|
||||
"""
|
||||
|
||||
def get_random(self):
|
||||
return getattr(self, '_random', False)
|
||||
|
||||
def set_random(self, value):
|
||||
if self.get_random() != value:
|
||||
self._trigger_options_changed()
|
||||
return setattr(self, '_random', value)
|
||||
|
||||
random = property(get_random, set_random)
|
||||
"""
|
||||
:class:`True`
|
||||
Tracks are selected at random from the playlist.
|
||||
:class:`False`
|
||||
Tracks are played in the order of the playlist.
|
||||
"""
|
||||
|
||||
def get_repeat(self):
|
||||
return getattr(self, '_repeat', False)
|
||||
|
||||
def set_repeat(self, value):
|
||||
if self.get_repeat() != value:
|
||||
self._trigger_options_changed()
|
||||
return setattr(self, '_repeat', value)
|
||||
|
||||
repeat = property(get_repeat, set_repeat)
|
||||
"""
|
||||
:class:`True`
|
||||
The current playlist is played repeatedly. To repeat a single track,
|
||||
select both :attr:`repeat` and :attr:`single`.
|
||||
:class:`False`
|
||||
The current playlist is played once.
|
||||
"""
|
||||
|
||||
def get_single(self):
|
||||
return getattr(self, '_single', False)
|
||||
|
||||
def set_single(self, value):
|
||||
if self.get_single() != value:
|
||||
self._trigger_options_changed()
|
||||
return setattr(self, '_single', value)
|
||||
|
||||
single = property(get_single, set_single)
|
||||
"""
|
||||
:class:`True`
|
||||
Playback is stopped after current song, unless in :attr:`repeat`
|
||||
mode.
|
||||
:class:`False`
|
||||
Playback continues after current song.
|
||||
"""
|
||||
|
||||
def get_state(self):
|
||||
return self._state
|
||||
|
||||
def set_state(self, new_state):
|
||||
(old_state, self._state) = (self.state, new_state)
|
||||
logger.debug('Changing state: %s -> %s', old_state, new_state)
|
||||
|
||||
self._trigger_playback_state_changed(old_state, new_state)
|
||||
|
||||
state = property(get_state, set_state)
|
||||
"""
|
||||
The playback state. Must be :attr:`PLAYING`, :attr:`PAUSED`, or
|
||||
:attr:`STOPPED`.
|
||||
|
||||
Possible states and transitions:
|
||||
|
||||
.. digraph:: state_transitions
|
||||
|
||||
"STOPPED" -> "PLAYING" [ label="play" ]
|
||||
"STOPPED" -> "PAUSED" [ label="pause" ]
|
||||
"PLAYING" -> "STOPPED" [ label="stop" ]
|
||||
"PLAYING" -> "PAUSED" [ label="pause" ]
|
||||
"PLAYING" -> "PLAYING" [ label="play" ]
|
||||
"PAUSED" -> "PLAYING" [ label="resume" ]
|
||||
"PAUSED" -> "STOPPED" [ label="stop" ]
|
||||
"""
|
||||
|
||||
def get_time_position(self):
|
||||
backend = self._get_backend()
|
||||
if backend:
|
||||
return backend.playback.get_time_position().get()
|
||||
else:
|
||||
return 0
|
||||
|
||||
time_position = property(get_time_position)
|
||||
"""Time position in milliseconds."""
|
||||
|
||||
def get_tracklist_position(self):
|
||||
if self.current_tl_track is None:
|
||||
return None
|
||||
try:
|
||||
@ -122,25 +164,14 @@ class PlaybackController(object):
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def track_at_eot(self):
|
||||
"""
|
||||
The track that will be played at the end of the current track.
|
||||
tracklist_position = property(get_tracklist_position)
|
||||
"""
|
||||
The position of the current track in the tracklist.
|
||||
|
||||
Read-only. A :class:`mopidy.models.Track` extracted from
|
||||
:attr:`tl_track_at_eot` for convenience.
|
||||
"""
|
||||
return self._get_track(self.tl_track_at_eot)
|
||||
Read-only.
|
||||
"""
|
||||
|
||||
@property
|
||||
def tl_track_at_eot(self):
|
||||
"""
|
||||
The track that will be played at the end of the current track.
|
||||
|
||||
Read-only. A :class:`mopidy.models.TlTrack`.
|
||||
|
||||
Not necessarily the same track as :attr:`tl_track_at_next`.
|
||||
"""
|
||||
def get_tl_track_at_eot(self):
|
||||
# pylint: disable = R0911
|
||||
# Too many return statements
|
||||
|
||||
@ -173,28 +204,16 @@ class PlaybackController(object):
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def track_at_next(self):
|
||||
"""
|
||||
The track that will be played if calling :meth:`next()`.
|
||||
tl_track_at_eot = property(get_tl_track_at_eot)
|
||||
"""
|
||||
The track that will be played at the end of the current track.
|
||||
|
||||
Read-only. A :class:`mopidy.models.Track` extracted from
|
||||
:attr:`tl_track_at_next` for convenience.
|
||||
"""
|
||||
return self._get_track(self.tl_track_at_next)
|
||||
Read-only. A :class:`mopidy.models.TlTrack`.
|
||||
|
||||
@property
|
||||
def tl_track_at_next(self):
|
||||
"""
|
||||
The track that will be played if calling :meth:`next()`.
|
||||
Not necessarily the same track as :attr:`tl_track_at_next`.
|
||||
"""
|
||||
|
||||
Read-only. A :class:`mopidy.models.TlTrack`.
|
||||
|
||||
For normal playback this is the next track in the playlist. If repeat
|
||||
is enabled the next track can loop around the playlist. When random is
|
||||
enabled this should be a random track, all tracks should be played once
|
||||
before the list repeats.
|
||||
"""
|
||||
def get_tl_track_at_next(self):
|
||||
tl_tracks = self.core.tracklist.tl_tracks
|
||||
|
||||
if not tl_tracks:
|
||||
@ -221,27 +240,19 @@ class PlaybackController(object):
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def track_at_previous(self):
|
||||
"""
|
||||
The track that will be played if calling :meth:`previous()`.
|
||||
tl_track_at_next = property(get_tl_track_at_next)
|
||||
"""
|
||||
The track that will be played if calling :meth:`next()`.
|
||||
|
||||
Read-only. A :class:`mopidy.models.Track` extracted from
|
||||
:attr:`tl_track_at_previous` for convenience.
|
||||
"""
|
||||
return self._get_track(self.tl_track_at_previous)
|
||||
Read-only. A :class:`mopidy.models.TlTrack`.
|
||||
|
||||
@property
|
||||
def tl_track_at_previous(self):
|
||||
"""
|
||||
The track that will be played if calling :meth:`previous()`.
|
||||
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.
|
||||
"""
|
||||
|
||||
A :class:`mopidy.models.TlTrack`.
|
||||
|
||||
For normal playback this is the previous track in the playlist. If
|
||||
random and/or consume is enabled it should return the current track
|
||||
instead.
|
||||
"""
|
||||
def get_tl_track_at_previous(self):
|
||||
if self.repeat or self.consume or self.random:
|
||||
return self.current_tl_track
|
||||
|
||||
@ -250,59 +261,36 @@ class PlaybackController(object):
|
||||
|
||||
return self.core.tracklist.tl_tracks[self.tracklist_position - 1]
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""
|
||||
The playback state. Must be :attr:`PLAYING`, :attr:`PAUSED`, or
|
||||
:attr:`STOPPED`.
|
||||
tl_track_at_previous = property(get_tl_track_at_previous)
|
||||
"""
|
||||
The track that will be played if calling :meth:`previous()`.
|
||||
|
||||
Possible states and transitions:
|
||||
A :class:`mopidy.models.TlTrack`.
|
||||
|
||||
.. digraph:: state_transitions
|
||||
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.
|
||||
"""
|
||||
|
||||
"STOPPED" -> "PLAYING" [ label="play" ]
|
||||
"STOPPED" -> "PAUSED" [ label="pause" ]
|
||||
"PLAYING" -> "STOPPED" [ label="stop" ]
|
||||
"PLAYING" -> "PAUSED" [ label="pause" ]
|
||||
"PLAYING" -> "PLAYING" [ label="play" ]
|
||||
"PAUSED" -> "PLAYING" [ label="resume" ]
|
||||
"PAUSED" -> "STOPPED" [ label="stop" ]
|
||||
"""
|
||||
return self._state
|
||||
|
||||
@state.setter # noqa
|
||||
def state(self, new_state):
|
||||
(old_state, self._state) = (self.state, new_state)
|
||||
logger.debug('Changing state: %s -> %s', old_state, new_state)
|
||||
|
||||
self._trigger_playback_state_changed(old_state, new_state)
|
||||
|
||||
@property
|
||||
def time_position(self):
|
||||
"""Time position in milliseconds."""
|
||||
backend = self._get_backend()
|
||||
if backend:
|
||||
return backend.playback.get_time_position().get()
|
||||
else:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
"""Volume as int in range [0..100] or :class:`None`"""
|
||||
def get_volume(self):
|
||||
if self.audio:
|
||||
return self.audio.get_volume().get()
|
||||
else:
|
||||
# For testing
|
||||
return self._volume
|
||||
|
||||
@volume.setter # noqa
|
||||
def volume(self, volume):
|
||||
def set_volume(self, volume):
|
||||
if self.audio:
|
||||
self.audio.set_volume(volume)
|
||||
else:
|
||||
# For testing
|
||||
self._volume = volume
|
||||
|
||||
volume = property(get_volume, set_volume)
|
||||
"""Volume as int in range [0..100] or :class:`None`"""
|
||||
|
||||
### Methods
|
||||
|
||||
def change_track(self, tl_track, on_error_step=1):
|
||||
"""
|
||||
Change to the given track, keeping the current playback state.
|
||||
@ -324,6 +312,8 @@ class PlaybackController(object):
|
||||
def on_end_of_track(self):
|
||||
"""
|
||||
Tell the playback controller that end of track is reached.
|
||||
|
||||
Used by event handler in :class:`mopidy.core.Core`.
|
||||
"""
|
||||
if self.state == PlaybackState.STOPPED:
|
||||
return
|
||||
@ -343,7 +333,7 @@ class PlaybackController(object):
|
||||
"""
|
||||
Tell the playback controller that the current playlist has changed.
|
||||
|
||||
Used by :class:`mopidy.core.CurrentPlaylistController`.
|
||||
Used by :class:`mopidy.core.TracklistController`.
|
||||
"""
|
||||
self._first_shuffle = True
|
||||
self._shuffled = []
|
||||
|
||||
@ -15,18 +15,19 @@ class PlaylistsController(object):
|
||||
self.backends = backends
|
||||
self.core = core
|
||||
|
||||
@property
|
||||
def playlists(self):
|
||||
"""
|
||||
The available playlists.
|
||||
|
||||
Read-only. List of :class:`mopidy.models.Playlist`.
|
||||
"""
|
||||
def get_playlists(self):
|
||||
futures = [
|
||||
b.playlists.playlists for b in self.backends.with_playlists]
|
||||
results = pykka.get_all(futures)
|
||||
return list(itertools.chain(*results))
|
||||
|
||||
playlists = property(get_playlists)
|
||||
"""
|
||||
The available playlists.
|
||||
|
||||
Read-only. List of :class:`mopidy.models.Playlist`.
|
||||
"""
|
||||
|
||||
def create(self, name, uri_scheme=None):
|
||||
"""
|
||||
Create a new playlist.
|
||||
|
||||
@ -20,89 +20,77 @@ class TracklistController(object):
|
||||
self._tl_tracks = []
|
||||
self._version = 0
|
||||
|
||||
@property
|
||||
def tl_tracks(self):
|
||||
"""
|
||||
List of :class:`mopidy.models.TlTrack`.
|
||||
|
||||
Read-only.
|
||||
"""
|
||||
def get_tl_tracks(self):
|
||||
return self._tl_tracks[:]
|
||||
|
||||
@property
|
||||
def tracks(self):
|
||||
"""
|
||||
List of :class:`mopidy.models.Track` in the tracklist.
|
||||
tl_tracks = property(get_tl_tracks)
|
||||
"""
|
||||
List of :class:`mopidy.models.TlTrack`.
|
||||
|
||||
Read-only.
|
||||
"""
|
||||
Read-only.
|
||||
"""
|
||||
|
||||
def get_tracks(self):
|
||||
return [tl_track.track for tl_track in self._tl_tracks]
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
"""
|
||||
Length of the tracklist.
|
||||
"""
|
||||
tracks = property(get_tracks)
|
||||
"""
|
||||
List of :class:`mopidy.models.Track` in the tracklist.
|
||||
|
||||
Read-only.
|
||||
"""
|
||||
|
||||
def get_length(self):
|
||||
return len(self._tl_tracks)
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
"""
|
||||
The tracklist version. Integer which is increased every time the
|
||||
tracklist is changed. Is not reset before Mopidy is restarted.
|
||||
"""
|
||||
length = property(get_length)
|
||||
"""Length of the tracklist."""
|
||||
|
||||
def get_version(self):
|
||||
return self._version
|
||||
|
||||
@version.setter # noqa
|
||||
def version(self, version):
|
||||
self._version = version
|
||||
def _increase_version(self):
|
||||
self._version += 1
|
||||
self._core.playback.on_tracklist_change()
|
||||
self._trigger_tracklist_changed()
|
||||
|
||||
def add(self, track, at_position=None, increase_version=True):
|
||||
version = property(get_version)
|
||||
"""
|
||||
The tracklist version.
|
||||
|
||||
Read-only. Integer which is increased every time the tracklist is changed.
|
||||
Is not reset before Mopidy is restarted.
|
||||
"""
|
||||
|
||||
def add(self, tracks, at_position=None):
|
||||
"""
|
||||
Add the track to the end of, or at the given position in the tracklist.
|
||||
Add the track or list of tracks to the tracklist.
|
||||
|
||||
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed`
|
||||
event.
|
||||
If ``at_position`` is given, the tracks placed at the given position in
|
||||
the tracklist. If ``at_position`` is not given, the tracks are appended
|
||||
to the end of the tracklist.
|
||||
|
||||
:param track: track to add
|
||||
:type track: :class:`mopidy.models.Track`
|
||||
Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
|
||||
|
||||
:param tracks: tracks to add
|
||||
:type tracks: list of :class:`mopidy.models.Track`
|
||||
:param at_position: position in tracklist to add track
|
||||
:type at_position: int or :class:`None`
|
||||
:param increase_version: if the tracklist version should be increased
|
||||
:type increase_version: :class:`True` or :class:`False`
|
||||
:rtype: :class:`mopidy.models.TlTrack` that was added to the tracklist
|
||||
"""
|
||||
assert at_position <= len(self._tl_tracks), \
|
||||
'at_position can not be greater than tracklist length'
|
||||
tl_track = TlTrack(self._next_tlid, track)
|
||||
if at_position is not None:
|
||||
self._tl_tracks.insert(at_position, tl_track)
|
||||
else:
|
||||
self._tl_tracks.append(tl_track)
|
||||
if increase_version:
|
||||
self.version += 1
|
||||
self._next_tlid += 1
|
||||
return tl_track
|
||||
|
||||
def append(self, tracks):
|
||||
"""
|
||||
Append the given tracks to the tracklist.
|
||||
|
||||
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed`
|
||||
event.
|
||||
|
||||
:param tracks: tracks to append
|
||||
:type tracks: list of :class:`mopidy.models.Track`
|
||||
:rtype: list of class:`mopidy.models.TlTrack`
|
||||
:rtype: list of :class:`mopidy.models.TlTrack`
|
||||
"""
|
||||
tl_tracks = []
|
||||
for track in tracks:
|
||||
tl_tracks.append(self.add(track, increase_version=False))
|
||||
tl_track = TlTrack(self._next_tlid, track)
|
||||
self._next_tlid += 1
|
||||
if at_position is not None:
|
||||
self._tl_tracks.insert(at_position, tl_track)
|
||||
at_position += 1
|
||||
else:
|
||||
self._tl_tracks.append(tl_track)
|
||||
tl_tracks.append(tl_track)
|
||||
|
||||
if tracks:
|
||||
self.version += 1
|
||||
if tl_tracks:
|
||||
self._increase_version()
|
||||
|
||||
return tl_tracks
|
||||
|
||||
@ -110,11 +98,10 @@ class TracklistController(object):
|
||||
"""
|
||||
Clear the tracklist.
|
||||
|
||||
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed`
|
||||
event.
|
||||
Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
|
||||
"""
|
||||
self._tl_tracks = []
|
||||
self.version += 1
|
||||
self._increase_version()
|
||||
|
||||
def filter(self, **criteria):
|
||||
"""
|
||||
@ -156,8 +143,7 @@ class TracklistController(object):
|
||||
"""
|
||||
Move the tracks in the slice ``[start:end]`` to ``to_position``.
|
||||
|
||||
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed`
|
||||
event.
|
||||
Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
|
||||
|
||||
:param start: position of first track to move
|
||||
:type start: int
|
||||
@ -184,7 +170,7 @@ class TracklistController(object):
|
||||
new_tl_tracks.insert(to_position, tl_track)
|
||||
to_position += 1
|
||||
self._tl_tracks = new_tl_tracks
|
||||
self.version += 1
|
||||
self._increase_version()
|
||||
|
||||
def remove(self, **criteria):
|
||||
"""
|
||||
@ -192,8 +178,7 @@ class TracklistController(object):
|
||||
|
||||
Uses :meth:`filter()` to lookup the tracks to remove.
|
||||
|
||||
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed`
|
||||
event.
|
||||
Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
|
||||
|
||||
:param criteria: on or more criteria to match by
|
||||
:type criteria: dict
|
||||
@ -203,7 +188,7 @@ class TracklistController(object):
|
||||
for tl_track in tl_tracks:
|
||||
position = self._tl_tracks.index(tl_track)
|
||||
del self._tl_tracks[position]
|
||||
self.version += 1
|
||||
self._increase_version()
|
||||
return tl_tracks
|
||||
|
||||
def shuffle(self, start=None, end=None):
|
||||
@ -211,8 +196,7 @@ class TracklistController(object):
|
||||
Shuffles the entire tracklist. If ``start`` and ``end`` is given only
|
||||
shuffles the slice ``[start:end]``.
|
||||
|
||||
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed`
|
||||
event.
|
||||
Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
|
||||
|
||||
:param start: position of first track to shuffle
|
||||
:type start: int or :class:`None`
|
||||
@ -236,7 +220,7 @@ class TracklistController(object):
|
||||
after = tl_tracks[end or len(tl_tracks):]
|
||||
random.shuffle(shuffled)
|
||||
self._tl_tracks = before + shuffled + after
|
||||
self.version += 1
|
||||
self._increase_version()
|
||||
|
||||
def slice(self, start, end):
|
||||
"""
|
||||
|
||||
@ -56,10 +56,11 @@ def handle_request(pattern, auth_required=True):
|
||||
if match is not None:
|
||||
mpd_commands.add(
|
||||
MpdCommand(name=match.group(), auth_required=auth_required))
|
||||
if pattern in request_handlers:
|
||||
compiled_pattern = re.compile(pattern, flags=re.UNICODE)
|
||||
if compiled_pattern in request_handlers:
|
||||
raise ValueError('Tried to redefine handler for %s with %s' % (
|
||||
pattern, func))
|
||||
request_handlers[pattern] = func
|
||||
request_handlers[compiled_pattern] = func
|
||||
func.__doc__ = ' - *Pattern:* ``%s``\n\n%s' % (
|
||||
pattern, func.__doc__ or '')
|
||||
return func
|
||||
|
||||
@ -24,7 +24,7 @@ def add(context, uri):
|
||||
return
|
||||
tracks = context.core.library.lookup(uri).get()
|
||||
if tracks:
|
||||
context.core.tracklist.append(tracks)
|
||||
context.core.tracklist.add(tracks)
|
||||
return
|
||||
raise MpdNoExistError('directory or file not found', command='add')
|
||||
|
||||
@ -57,14 +57,8 @@ def addid(context, uri, songpos=None):
|
||||
raise MpdNoExistError('No such song', command='addid')
|
||||
if songpos and songpos > context.core.tracklist.length.get():
|
||||
raise MpdArgError('Bad song index', command='addid')
|
||||
first_tl_track = None
|
||||
for track in tracks:
|
||||
tl_track = context.core.tracklist.add(track, at_position=songpos).get()
|
||||
if songpos is not None:
|
||||
songpos += 1
|
||||
if first_tl_track is None:
|
||||
first_tl_track = tl_track
|
||||
return ('Id', first_tl_track.tlid)
|
||||
tl_tracks = context.core.tracklist.add(tracks, at_position=songpos).get()
|
||||
return ('Id', tl_tracks[0].tlid)
|
||||
|
||||
|
||||
@handle_request(r'^delete "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
@ -110,7 +104,8 @@ def deleteid(context, tlid):
|
||||
Deletes the song ``SONGID`` from the playlist
|
||||
"""
|
||||
tlid = int(tlid)
|
||||
if context.core.playback.current_tlid.get() == tlid:
|
||||
tl_track = context.core.playback.current_tl_track.get()
|
||||
if tl_track and tl_track.tlid == tlid:
|
||||
context.core.playback.next()
|
||||
tl_tracks = context.core.tracklist.remove(tlid=tlid).get()
|
||||
if not tl_tracks:
|
||||
@ -237,7 +232,6 @@ def playlistid(context, tlid=None):
|
||||
|
||||
|
||||
@handle_request(r'^playlistinfo$')
|
||||
@handle_request(r'^playlistinfo "-1"$')
|
||||
@handle_request(r'^playlistinfo "(?P<songpos>-?\d+)"$')
|
||||
@handle_request(r'^playlistinfo "(?P<start>\d+):(?P<end>\d+)*"$')
|
||||
def playlistinfo(context, songpos=None, start=None, end=None):
|
||||
@ -255,6 +249,8 @@ def playlistinfo(context, songpos=None, start=None, end=None):
|
||||
- uses negative indexes, like ``playlistinfo "-1"``, to request
|
||||
the entire playlist
|
||||
"""
|
||||
if songpos == '-1':
|
||||
songpos = None
|
||||
if songpos is not None:
|
||||
songpos = int(songpos)
|
||||
tl_track = context.core.tracklist.tl_tracks.get()[songpos]
|
||||
@ -274,7 +270,7 @@ def playlistinfo(context, songpos=None, start=None, end=None):
|
||||
|
||||
|
||||
@handle_request(r'^playlistsearch "(?P<tag>[^"]+)" "(?P<needle>[^"]+)"$')
|
||||
@handle_request(r'^playlistsearch (?P<tag>\S+) "(?P<needle>[^"]+)"$')
|
||||
@handle_request(r'^playlistsearch (?P<tag>\w+) "(?P<needle>[^"]+)"$')
|
||||
def playlistsearch(context, tag, needle):
|
||||
"""
|
||||
*musicpd.org, current playlist section:*
|
||||
@ -376,7 +372,7 @@ def swap(context, songpos1, songpos2):
|
||||
del tracks[songpos2]
|
||||
tracks.insert(songpos2, song1)
|
||||
context.core.tracklist.clear()
|
||||
context.core.tracklist.append(tracks)
|
||||
context.core.tracklist.add(tracks)
|
||||
|
||||
|
||||
@handle_request(r'^swapid "(?P<tlid1>\d+)" "(?P<tlid2>\d+)"$')
|
||||
|
||||
@ -52,7 +52,7 @@ def count(context, tag, needle):
|
||||
|
||||
@handle_request(
|
||||
r'^find (?P<mpd_query>("?([Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ile[name]*|'
|
||||
r'[Tt]itle|[Aa]ny)"? "[^"]+"\s?)+)$')
|
||||
r'[Tt]itle|[Aa]ny)"? "[^"]*"\s?)+)$')
|
||||
def find(context, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
@ -250,7 +250,8 @@ def _list_artist(context, query):
|
||||
tracks = context.core.library.find_exact(**query).get()
|
||||
for track in tracks:
|
||||
for artist in track.artists:
|
||||
artists.add(('Artist', artist.name))
|
||||
if artist.name:
|
||||
artists.add(('Artist', artist.name))
|
||||
return artists
|
||||
|
||||
|
||||
@ -258,7 +259,7 @@ def _list_album(context, query):
|
||||
albums = set()
|
||||
tracks = context.core.library.find_exact(**query).get()
|
||||
for track in tracks:
|
||||
if track.album is not None:
|
||||
if track.album and track.album.name:
|
||||
albums.add(('Album', track.album.name))
|
||||
return albums
|
||||
|
||||
@ -267,7 +268,7 @@ def _list_date(context, query):
|
||||
dates = set()
|
||||
tracks = context.core.library.find_exact(**query).get()
|
||||
for track in tracks:
|
||||
if track.date is not None:
|
||||
if track.date:
|
||||
dates.add(('Date', track.date))
|
||||
return dates
|
||||
|
||||
@ -334,7 +335,7 @@ def rescan(context, uri=None):
|
||||
|
||||
@handle_request(
|
||||
r'^search (?P<mpd_query>("?([Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ile[name]*|'
|
||||
r'[Tt]itle|[Aa]ny)"? "[^"]+"\s?)+)$')
|
||||
r'[Tt]itle|[Aa]ny)"? "[^"]*"\s?)+)$')
|
||||
def search(context, mpd_query):
|
||||
"""
|
||||
*musicpd.org, music database section:*
|
||||
|
||||
@ -329,9 +329,9 @@ def seek(context, songpos, seconds):
|
||||
|
||||
- issues ``seek 1 120`` without quotes around the arguments.
|
||||
"""
|
||||
if context.core.playback.tracklist_position != songpos:
|
||||
if context.core.playback.tracklist_position.get() != songpos:
|
||||
playpos(context, songpos)
|
||||
context.core.playback.seek(int(seconds) * 1000)
|
||||
context.core.playback.seek(int(seconds) * 1000).get()
|
||||
|
||||
|
||||
@handle_request(r'^seekid "(?P<tlid>\d+)" "(?P<seconds>\d+)"$')
|
||||
@ -343,9 +343,10 @@ def seekid(context, tlid, seconds):
|
||||
|
||||
Seeks to the position ``TIME`` (in seconds) of song ``SONGID``.
|
||||
"""
|
||||
if context.core.playback.current_tlid != tlid:
|
||||
tl_track = context.core.playback.current_tl_track.get()
|
||||
if not tl_track or tl_track.tlid != tlid:
|
||||
playid(context, tlid)
|
||||
context.core.playback.seek(int(seconds) * 1000)
|
||||
context.core.playback.seek(int(seconds) * 1000).get()
|
||||
|
||||
|
||||
@handle_request(r'^setvol (?P<volume>[-+]*\d+)$')
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from mopidy.frontends.mpd.protocol import handle_request, mpd_commands
|
||||
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
|
||||
|
||||
@handle_request(r'^commands$', auth_required=False)
|
||||
@ -47,8 +46,15 @@ def decoders(context):
|
||||
mime_type: audio/mpeg
|
||||
plugin: mpcdec
|
||||
suffix: mpc
|
||||
|
||||
*Clarifications:*
|
||||
|
||||
- ncmpcpp asks for decoders the first time you open the browse view. By
|
||||
returning nothing and OK instead of an not implemented error, we avoid
|
||||
"Not implemented" showing up in the ncmpcpp interface, and we get the
|
||||
list of playlists without having to enter the browse interface twice.
|
||||
"""
|
||||
raise MpdNotImplemented # TODO
|
||||
return # TODO
|
||||
|
||||
|
||||
@handle_request(r'^notcommands$', auth_required=False)
|
||||
|
||||
@ -7,7 +7,7 @@ from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.translator import playlist_to_mpd_format
|
||||
|
||||
|
||||
@handle_request(r'^listplaylist (?P<name>\S+)$')
|
||||
@handle_request(r'^listplaylist (?P<name>\w+)$')
|
||||
@handle_request(r'^listplaylist "(?P<name>[^"]+)"$')
|
||||
def listplaylist(context, name):
|
||||
"""
|
||||
@ -29,7 +29,7 @@ def listplaylist(context, name):
|
||||
return ['file: %s' % t.uri for t in playlists[0].tracks]
|
||||
|
||||
|
||||
@handle_request(r'^listplaylistinfo (?P<name>\S+)$')
|
||||
@handle_request(r'^listplaylistinfo (?P<name>\w+)$')
|
||||
@handle_request(r'^listplaylistinfo "(?P<name>[^"]+)"$')
|
||||
def listplaylistinfo(context, name):
|
||||
"""
|
||||
@ -70,9 +70,16 @@ def listplaylists(context):
|
||||
Last-Modified: 2010-02-06T02:10:25Z
|
||||
playlist: b
|
||||
Last-Modified: 2010-02-06T02:11:08Z
|
||||
|
||||
*Clarifications:*
|
||||
|
||||
- ncmpcpp 0.5.10 segfaults if we return 'playlist: ' on a line, so we must
|
||||
ignore playlists without names, which isn't very useful anyway.
|
||||
"""
|
||||
result = []
|
||||
for playlist in context.core.playlists.playlists.get():
|
||||
if not playlist.name:
|
||||
continue
|
||||
result.append(('playlist', playlist.name))
|
||||
last_modified = (
|
||||
playlist.last_modified or dt.datetime.now()).isoformat()
|
||||
@ -101,7 +108,7 @@ def load(context, name):
|
||||
playlists = context.core.playlists.filter(name=name).get()
|
||||
if not playlists:
|
||||
raise MpdNoExistError('No such playlist', command='load')
|
||||
context.core.tracklist.append(playlists[0].tracks)
|
||||
context.core.tracklist.add(playlists[0].tracks)
|
||||
|
||||
|
||||
@handle_request(r'^playlistadd "(?P<name>[^"]+)" "(?P<uri>[^"]+)"$')
|
||||
|
||||
@ -281,7 +281,7 @@ class MprisObject(dbus.service.Object):
|
||||
# is added to the backend.
|
||||
tracks = self.core.library.lookup(uri).get()
|
||||
if tracks:
|
||||
tl_tracks = self.core.tracklist.append(tracks).get()
|
||||
tl_tracks = self.core.tracklist.add(tracks).get()
|
||||
self.core.playback.play(tl_tracks[0])
|
||||
else:
|
||||
logger.debug('Track with URI "%s" not found in library.', uri)
|
||||
@ -419,8 +419,8 @@ class MprisObject(dbus.service.Object):
|
||||
if not self.get_CanControl():
|
||||
return False
|
||||
return (
|
||||
self.core.playback.current_track.get() is not None or
|
||||
self.core.playback.track_at_next.get() is not None)
|
||||
self.core.playback.current_tl_track.get() is not None or
|
||||
self.core.playback.tl_track_at_next.get() is not None)
|
||||
|
||||
def get_CanPause(self):
|
||||
if not self.get_CanControl():
|
||||
@ -449,7 +449,7 @@ class MprisObject(dbus.service.Object):
|
||||
playlist_uri = self.get_playlist_uri(playlist_id)
|
||||
playlist = self.core.playlists.lookup(playlist_uri).get()
|
||||
if playlist and playlist.tracks:
|
||||
tl_tracks = self.core.tracklist.append(playlist.tracks).get()
|
||||
tl_tracks = self.core.tracklist.add(playlist.tracks).get()
|
||||
self.core.playback.play(tl_tracks[0])
|
||||
|
||||
@dbus.service.method(dbus_interface=PLAYLISTS_IFACE)
|
||||
|
||||
@ -251,3 +251,34 @@ SPOTIFY_PASSWORD = ''
|
||||
#:
|
||||
#: SPOTIFY_BITRATE = 160
|
||||
SPOTIFY_BITRATE = 160
|
||||
|
||||
#: Spotify proxy host.
|
||||
#:
|
||||
#: Used by :mod:`mopidy.backends.spotify`.
|
||||
#:
|
||||
#: Example::
|
||||
#:
|
||||
#: SPOTIFY_PROXY_HOST = u'protocol://host:port'
|
||||
#:
|
||||
#: Default::
|
||||
#:
|
||||
#: SPOTIFY_PROXY_HOST = None
|
||||
SPOTIFY_PROXY_HOST = None
|
||||
|
||||
#: Spotify proxy username.
|
||||
#:
|
||||
#: Used by :mod:`mopidy.backends.spotify`.
|
||||
#:
|
||||
#: Default::
|
||||
#:
|
||||
#: SPOTIFY_PROXY_USERNAME = None
|
||||
SPOTIFY_PROXY_USERNAME = None
|
||||
|
||||
#: Spotify proxy password.
|
||||
#:
|
||||
#: Used by :mod:`mopidy.backends.spotify`
|
||||
#:
|
||||
#: Default::
|
||||
#:
|
||||
#: SPOTIFY_PROXY_PASSWORD = None
|
||||
SPOTIFY_PROXY_PASSWORD = None
|
||||
|
||||
1
requirements/spotify.txt
Normal file
1
requirements/spotify.txt
Normal file
@ -0,0 +1 @@
|
||||
pyspotify >= 1.9, < 1.10
|
||||
@ -1,10 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
def populate_playlist(func):
|
||||
def populate_tracklist(func):
|
||||
def wrapper(self):
|
||||
for track in self.tracks:
|
||||
self.core.tracklist.add(track)
|
||||
self.tl_tracks = self.core.tracklist.add(self.tracks)
|
||||
return func(self)
|
||||
|
||||
wrapper.__name__ = func.__name__
|
||||
|
||||
@ -9,7 +9,7 @@ from mopidy.core import PlaybackState
|
||||
from mopidy.models import Track
|
||||
|
||||
from tests import unittest
|
||||
from tests.backends.base import populate_playlist
|
||||
from tests.backends.base import populate_tracklist
|
||||
|
||||
# TODO Test 'playlist repeat', e.g. repeat=1,single=0
|
||||
|
||||
@ -40,35 +40,35 @@ class PlaybackControllerTest(object):
|
||||
def test_play_with_empty_playlist_return_value(self):
|
||||
self.assertEqual(self.playback.play(), None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_state(self):
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_return_value(self):
|
||||
self.assertEqual(self.playback.play(), None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_track_state(self):
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
self.playback.play(self.tracklist.tl_tracks[-1])
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_track_return_value(self):
|
||||
self.assertEqual(self.playback.play(
|
||||
self.tracklist.tl_tracks[-1]), None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_when_playing(self):
|
||||
self.playback.play()
|
||||
track = self.playback.current_track
|
||||
self.playback.play()
|
||||
self.assertEqual(track, self.playback.current_track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_when_paused(self):
|
||||
self.playback.play()
|
||||
track = self.playback.current_track
|
||||
@ -77,7 +77,7 @@ class PlaybackControllerTest(object):
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
self.assertEqual(track, self.playback.current_track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_when_pause_after_next(self):
|
||||
self.playback.play()
|
||||
self.playback.next()
|
||||
@ -88,17 +88,17 @@ class PlaybackControllerTest(object):
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
self.assertEqual(track, self.playback.current_track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_sets_current_track(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_track_sets_current_track(self):
|
||||
self.playback.play(self.tracklist.tl_tracks[-1])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[-1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_skips_to_next_track_on_failure(self):
|
||||
# If backend's play() returns False, it is a failure.
|
||||
self.backend.playback.play = lambda track: track != self.tracks[0]
|
||||
@ -106,7 +106,7 @@ class PlaybackControllerTest(object):
|
||||
self.assertNotEqual(self.playback.current_track, self.tracks[0])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_current_track_after_completed_playlist(self):
|
||||
self.playback.play(self.tracklist.tl_tracks[-1])
|
||||
self.playback.on_end_of_track()
|
||||
@ -118,14 +118,14 @@ class PlaybackControllerTest(object):
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous(self):
|
||||
self.playback.play()
|
||||
self.playback.next()
|
||||
self.playback.previous()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_more(self):
|
||||
self.playback.play() # At track 0
|
||||
self.playback.next() # At track 1
|
||||
@ -133,13 +133,13 @@ class PlaybackControllerTest(object):
|
||||
self.playback.previous() # At track 1
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_return_value(self):
|
||||
self.playback.play()
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.previous(), None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_does_not_trigger_playback(self):
|
||||
self.playback.play()
|
||||
self.playback.next()
|
||||
@ -147,7 +147,7 @@ class PlaybackControllerTest(object):
|
||||
self.playback.previous()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_at_start_of_playlist(self):
|
||||
self.playback.previous()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
@ -158,7 +158,7 @@ class PlaybackControllerTest(object):
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_skips_to_previous_track_on_failure(self):
|
||||
# If backend's play() returns False, it is a failure.
|
||||
self.backend.playback.play = lambda track: track != self.tracks[1]
|
||||
@ -168,7 +168,7 @@ class PlaybackControllerTest(object):
|
||||
self.assertNotEqual(self.playback.current_track, self.tracks[1])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next(self):
|
||||
self.playback.play()
|
||||
|
||||
@ -181,17 +181,17 @@ class PlaybackControllerTest(object):
|
||||
self.playback.tracklist_position, old_position + 1)
|
||||
self.assertNotEqual(self.playback.current_track.uri, old_uri)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_return_value(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.next(), None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_does_not_trigger_playback(self):
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_at_end_of_playlist(self):
|
||||
self.playback.play()
|
||||
|
||||
@ -204,7 +204,7 @@ class PlaybackControllerTest(object):
|
||||
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_until_end_of_playlist_and_play_from_start(self):
|
||||
self.playback.play()
|
||||
|
||||
@ -222,7 +222,7 @@ class PlaybackControllerTest(object):
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_skips_to_next_track_on_failure(self):
|
||||
# If backend's play() returns False, it is a failure.
|
||||
self.backend.playback.play = lambda track: track != self.tracks[1]
|
||||
@ -232,54 +232,54 @@ class PlaybackControllerTest(object):
|
||||
self.assertNotEqual(self.playback.current_track, self.tracks[1])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[2])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_track_before_play(self):
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[0])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_track_during_play(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[1])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_track_after_previous(self):
|
||||
self.playback.play()
|
||||
self.playback.next()
|
||||
self.playback.previous()
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[1])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
||||
|
||||
def test_next_track_empty_playlist(self):
|
||||
self.assertEqual(self.playback.track_at_next, None)
|
||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_track_at_end_of_playlist(self):
|
||||
self.playback.play()
|
||||
for _ in self.tracklist.tl_tracks[1:]:
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.track_at_next, None)
|
||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_track_at_end_of_playlist_with_repeat(self):
|
||||
self.playback.repeat = True
|
||||
self.playback.play()
|
||||
for _ in self.tracks[1:]:
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[0])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_track_with_random(self):
|
||||
random.seed(1)
|
||||
self.playback.random = True
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[2])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_with_consume(self):
|
||||
self.playback.consume = True
|
||||
self.playback.play()
|
||||
self.playback.next()
|
||||
self.assertIn(self.tracks[0], self.tracklist.tracks)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_with_single_and_repeat(self):
|
||||
self.playback.single = True
|
||||
self.playback.repeat = True
|
||||
@ -287,7 +287,7 @@ class PlaybackControllerTest(object):
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_with_random(self):
|
||||
# FIXME feels very fragile
|
||||
random.seed(1)
|
||||
@ -296,15 +296,15 @@ class PlaybackControllerTest(object):
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_next_track_with_random_after_append_playlist(self):
|
||||
random.seed(1)
|
||||
self.playback.random = True
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[2])
|
||||
self.tracklist.append(self.tracks[:1])
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[1])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2])
|
||||
self.tracklist.add(self.tracks[:1])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track(self):
|
||||
self.playback.play()
|
||||
|
||||
@ -317,17 +317,17 @@ class PlaybackControllerTest(object):
|
||||
self.playback.tracklist_position, old_position + 1)
|
||||
self.assertNotEqual(self.playback.current_track.uri, old_uri)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_return_value(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.on_end_of_track(), None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_does_not_trigger_playback(self):
|
||||
self.playback.on_end_of_track()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_at_end_of_playlist(self):
|
||||
self.playback.play()
|
||||
|
||||
@ -340,7 +340,7 @@ class PlaybackControllerTest(object):
|
||||
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_until_end_of_playlist_and_play_from_start(self):
|
||||
self.playback.play()
|
||||
|
||||
@ -358,7 +358,7 @@ class PlaybackControllerTest(object):
|
||||
self.playback.on_end_of_track()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_skips_to_next_track_on_failure(self):
|
||||
# If backend's play() returns False, it is a failure.
|
||||
self.backend.playback.play = lambda track: track != self.tracks[1]
|
||||
@ -368,54 +368,54 @@ class PlaybackControllerTest(object):
|
||||
self.assertNotEqual(self.playback.current_track, self.tracks[1])
|
||||
self.assertEqual(self.playback.current_track, self.tracks[2])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_track_before_play(self):
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[0])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_track_during_play(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[1])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_track_after_previous(self):
|
||||
self.playback.play()
|
||||
self.playback.on_end_of_track()
|
||||
self.playback.previous()
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[1])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
||||
|
||||
def test_end_of_track_track_empty_playlist(self):
|
||||
self.assertEqual(self.playback.track_at_next, None)
|
||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_track_at_end_of_playlist(self):
|
||||
self.playback.play()
|
||||
for _ in self.tracklist.tl_tracks[1:]:
|
||||
self.playback.on_end_of_track()
|
||||
self.assertEqual(self.playback.track_at_next, None)
|
||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_track_at_end_of_playlist_with_repeat(self):
|
||||
self.playback.repeat = True
|
||||
self.playback.play()
|
||||
for _ in self.tracks[1:]:
|
||||
self.playback.on_end_of_track()
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[0])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_track_with_random(self):
|
||||
random.seed(1)
|
||||
self.playback.random = True
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[2])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_with_consume(self):
|
||||
self.playback.consume = True
|
||||
self.playback.play()
|
||||
self.playback.on_end_of_track()
|
||||
self.assertNotIn(self.tracks[0], self.tracklist.tracks)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_with_random(self):
|
||||
# FIXME feels very fragile
|
||||
random.seed(1)
|
||||
@ -424,87 +424,89 @@ class PlaybackControllerTest(object):
|
||||
self.playback.on_end_of_track()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_track_with_random_after_append_playlist(self):
|
||||
random.seed(1)
|
||||
self.playback.random = True
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[2])
|
||||
self.tracklist.append(self.tracks[:1])
|
||||
self.assertEqual(self.playback.track_at_next, self.tracks[1])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2])
|
||||
self.tracklist.add(self.tracks[:1])
|
||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_track_before_play(self):
|
||||
self.assertEqual(self.playback.track_at_previous, None)
|
||||
self.assertEqual(self.playback.tl_track_at_previous, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_track_after_play(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.track_at_previous, None)
|
||||
self.assertEqual(self.playback.tl_track_at_previous, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_track_after_next(self):
|
||||
self.playback.play()
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.track_at_previous, self.tracks[0])
|
||||
self.assertEqual(self.playback.tl_track_at_previous, self.tl_tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_track_after_previous(self):
|
||||
self.playback.play() # At track 0
|
||||
self.playback.next() # At track 1
|
||||
self.playback.next() # At track 2
|
||||
self.playback.previous() # At track 1
|
||||
self.assertEqual(self.playback.track_at_previous, self.tracks[0])
|
||||
self.assertEqual(self.playback.tl_track_at_previous, self.tl_tracks[0])
|
||||
|
||||
def test_previous_track_empty_playlist(self):
|
||||
self.assertEqual(self.playback.track_at_previous, None)
|
||||
self.assertEqual(self.playback.tl_track_at_previous, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_track_with_consume(self):
|
||||
self.playback.consume = True
|
||||
for _ in self.tracks:
|
||||
self.playback.next()
|
||||
self.assertEqual(
|
||||
self.playback.track_at_previous, self.playback.current_track)
|
||||
self.playback.tl_track_at_previous,
|
||||
self.playback.current_tl_track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_track_with_random(self):
|
||||
self.playback.random = True
|
||||
for _ in self.tracks:
|
||||
self.playback.next()
|
||||
self.assertEqual(
|
||||
self.playback.track_at_previous, self.playback.current_track)
|
||||
self.playback.tl_track_at_previous,
|
||||
self.playback.current_tl_track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_initial_current_track(self):
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_current_track_during_play(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_current_track_after_next(self):
|
||||
self.playback.play()
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_initial_tracklist_position(self):
|
||||
self.assertEqual(self.playback.tracklist_position, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_tracklist_position_during_play(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.tracklist_position, 0)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_tracklist_position_after_next(self):
|
||||
self.playback.play()
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.tracklist_position, 1)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_tracklist_position_at_end_of_playlist(self):
|
||||
self.playback.play(self.tracklist.tl_tracks[-1])
|
||||
self.playback.on_end_of_track()
|
||||
@ -519,12 +521,12 @@ class PlaybackControllerTest(object):
|
||||
wrapper.called = False
|
||||
|
||||
self.playback.on_tracklist_change = wrapper
|
||||
self.tracklist.append([Track()])
|
||||
self.tracklist.add([Track()])
|
||||
|
||||
self.assert_(wrapper.called)
|
||||
|
||||
@unittest.SkipTest # Blocks for 10ms
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_track_callback_gets_called(self):
|
||||
self.playback.play()
|
||||
result = self.playback.seek(self.tracks[0].length - 10)
|
||||
@ -532,78 +534,78 @@ class PlaybackControllerTest(object):
|
||||
message = self.core_queue.get(True, 1)
|
||||
self.assertEqual('end_of_track', message['command'])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_on_tracklist_change_when_playing(self):
|
||||
self.playback.play()
|
||||
current_track = self.playback.current_track
|
||||
self.tracklist.append([self.tracks[2]])
|
||||
self.tracklist.add([self.tracks[2]])
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
self.assertEqual(self.playback.current_track, current_track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_on_tracklist_change_when_stopped(self):
|
||||
self.tracklist.append([self.tracks[2]])
|
||||
self.tracklist.add([self.tracks[2]])
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_on_tracklist_change_when_paused(self):
|
||||
self.playback.play()
|
||||
self.playback.pause()
|
||||
current_track = self.playback.current_track
|
||||
self.tracklist.append([self.tracks[2]])
|
||||
self.tracklist.add([self.tracks[2]])
|
||||
self.assertEqual(self.playback.state, PlaybackState.PAUSED)
|
||||
self.assertEqual(self.playback.current_track, current_track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_pause_when_stopped(self):
|
||||
self.playback.pause()
|
||||
self.assertEqual(self.playback.state, PlaybackState.PAUSED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_pause_when_playing(self):
|
||||
self.playback.play()
|
||||
self.playback.pause()
|
||||
self.assertEqual(self.playback.state, PlaybackState.PAUSED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_pause_when_paused(self):
|
||||
self.playback.play()
|
||||
self.playback.pause()
|
||||
self.playback.pause()
|
||||
self.assertEqual(self.playback.state, PlaybackState.PAUSED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_pause_return_value(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.pause(), None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_resume_when_stopped(self):
|
||||
self.playback.resume()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_resume_when_playing(self):
|
||||
self.playback.play()
|
||||
self.playback.resume()
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_resume_when_paused(self):
|
||||
self.playback.play()
|
||||
self.playback.pause()
|
||||
self.playback.resume()
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_resume_return_value(self):
|
||||
self.playback.play()
|
||||
self.playback.pause()
|
||||
self.assertEqual(self.playback.resume(), None)
|
||||
|
||||
@unittest.SkipTest # Uses sleep and might not work with LocalBackend
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_resume_continues_from_right_position(self):
|
||||
self.playback.play()
|
||||
time.sleep(0.2)
|
||||
@ -611,12 +613,12 @@ class PlaybackControllerTest(object):
|
||||
self.playback.resume()
|
||||
self.assertNotEqual(self.playback.time_position, 0)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_when_stopped(self):
|
||||
result = self.playback.seek(1000)
|
||||
self.assert_(result, 'Seek return value was %s' % result)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_when_stopped_updates_position(self):
|
||||
self.playback.seek(1000)
|
||||
position = self.playback.time_position
|
||||
@ -629,18 +631,18 @@ class PlaybackControllerTest(object):
|
||||
self.playback.seek(0)
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_when_stopped_triggers_play(self):
|
||||
self.playback.seek(0)
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_when_playing(self):
|
||||
self.playback.play()
|
||||
result = self.playback.seek(self.tracks[0].length - 1000)
|
||||
self.assert_(result, 'Seek return value was %s' % result)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_when_playing_updates_position(self):
|
||||
length = self.tracklist.tracks[0].length
|
||||
self.playback.play()
|
||||
@ -648,14 +650,14 @@ class PlaybackControllerTest(object):
|
||||
position = self.playback.time_position
|
||||
self.assertGreaterEqual(position, length - 1010)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_when_paused(self):
|
||||
self.playback.play()
|
||||
self.playback.pause()
|
||||
result = self.playback.seek(self.tracks[0].length - 1000)
|
||||
self.assert_(result, 'Seek return value was %s' % result)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_when_paused_updates_position(self):
|
||||
length = self.tracklist.tracks[0].length
|
||||
self.playback.play()
|
||||
@ -664,7 +666,7 @@ class PlaybackControllerTest(object):
|
||||
position = self.playback.time_position
|
||||
self.assertGreaterEqual(position, length - 1010)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_when_paused_triggers_play(self):
|
||||
self.playback.play()
|
||||
self.playback.pause()
|
||||
@ -672,34 +674,34 @@ class PlaybackControllerTest(object):
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
|
||||
@unittest.SkipTest
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_beyond_end_of_song(self):
|
||||
# FIXME need to decide return value
|
||||
self.playback.play()
|
||||
result = self.playback.seek(self.tracks[0].length * 100)
|
||||
self.assert_(not result, 'Seek return value was %s' % result)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_beyond_end_of_song_jumps_to_next_song(self):
|
||||
self.playback.play()
|
||||
self.playback.seek(self.tracks[0].length * 100)
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_beyond_end_of_song_for_last_track(self):
|
||||
self.playback.play(self.tracklist.tl_tracks[-1])
|
||||
self.playback.seek(self.tracklist.tracks[-1].length * 100)
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@unittest.SkipTest
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_beyond_start_of_song(self):
|
||||
# FIXME need to decide return value
|
||||
self.playback.play()
|
||||
result = self.playback.seek(-1000)
|
||||
self.assert_(not result, 'Seek return value was %s' % result)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_seek_beyond_start_of_song_update_postion(self):
|
||||
self.playback.play()
|
||||
self.playback.seek(-1000)
|
||||
@ -707,18 +709,18 @@ class PlaybackControllerTest(object):
|
||||
self.assertGreaterEqual(position, 0)
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_stop_when_stopped(self):
|
||||
self.playback.stop()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_stop_when_playing(self):
|
||||
self.playback.play()
|
||||
self.playback.stop()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_stop_when_paused(self):
|
||||
self.playback.play()
|
||||
self.playback.pause()
|
||||
@ -736,7 +738,7 @@ class PlaybackControllerTest(object):
|
||||
|
||||
self.assertEqual(self.playback.time_position, 0)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_time_position_when_stopped_with_playlist(self):
|
||||
future = mock.Mock()
|
||||
future.get = mock.Mock(return_value=0)
|
||||
@ -745,7 +747,7 @@ class PlaybackControllerTest(object):
|
||||
self.assertEqual(self.playback.time_position, 0)
|
||||
|
||||
@unittest.SkipTest # Uses sleep and does might not work with LocalBackend
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_time_position_when_playing(self):
|
||||
self.playback.play()
|
||||
first = self.playback.time_position
|
||||
@ -754,7 +756,7 @@ class PlaybackControllerTest(object):
|
||||
self.assertGreater(second, first)
|
||||
|
||||
@unittest.SkipTest # Uses sleep
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_time_position_when_paused(self):
|
||||
self.playback.play()
|
||||
time.sleep(0.2)
|
||||
@ -764,13 +766,13 @@ class PlaybackControllerTest(object):
|
||||
second = self.playback.time_position
|
||||
self.assertEqual(first, second)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_with_consume(self):
|
||||
self.playback.consume = True
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_playlist_is_empty_after_all_tracks_are_played_with_consume(self):
|
||||
self.playback.consume = True
|
||||
self.playback.play()
|
||||
@ -778,14 +780,14 @@ class PlaybackControllerTest(object):
|
||||
self.playback.on_end_of_track()
|
||||
self.assertEqual(len(self.tracklist.tracks), 0)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_play_with_random(self):
|
||||
random.seed(1)
|
||||
self.playback.random = True
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[2])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_previous_with_random(self):
|
||||
random.seed(1)
|
||||
self.playback.random = True
|
||||
@ -795,13 +797,13 @@ class PlaybackControllerTest(object):
|
||||
self.playback.previous()
|
||||
self.assertEqual(self.playback.current_track, current_track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_song_starts_next_track(self):
|
||||
self.playback.play()
|
||||
self.playback.on_end_of_track()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_song_with_single_and_repeat_starts_same(self):
|
||||
self.playback.single = True
|
||||
self.playback.repeat = True
|
||||
@ -809,7 +811,7 @@ class PlaybackControllerTest(object):
|
||||
self.playback.on_end_of_track()
|
||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_end_of_playlist_stops(self):
|
||||
self.playback.play(self.tracklist.tl_tracks[-1])
|
||||
self.playback.on_end_of_track()
|
||||
@ -824,34 +826,34 @@ class PlaybackControllerTest(object):
|
||||
def test_consume_off_by_default(self):
|
||||
self.assertEqual(self.playback.consume, False)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_random_until_end_of_playlist(self):
|
||||
self.playback.random = True
|
||||
self.playback.play()
|
||||
for _ in self.tracks[1:]:
|
||||
self.playback.next()
|
||||
self.assertEqual(self.playback.track_at_next, None)
|
||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_random_until_end_of_playlist_and_play_from_start(self):
|
||||
self.playback.repeat = True
|
||||
for _ in self.tracks:
|
||||
self.playback.next()
|
||||
self.assertNotEqual(self.playback.track_at_next, None)
|
||||
self.assertNotEqual(self.playback.tl_track_at_next, None)
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_random_until_end_of_playlist_with_repeat(self):
|
||||
self.playback.repeat = True
|
||||
self.playback.random = True
|
||||
self.playback.play()
|
||||
for _ in self.tracks:
|
||||
self.playback.next()
|
||||
self.assertNotEqual(self.playback.track_at_next, None)
|
||||
self.assertNotEqual(self.playback.tl_track_at_next, None)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_played_track_during_random_not_played_again(self):
|
||||
self.playback.random = True
|
||||
self.playback.play()
|
||||
@ -861,7 +863,7 @@ class PlaybackControllerTest(object):
|
||||
played.append(self.playback.current_track)
|
||||
self.playback.next()
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_playing_track_that_isnt_in_playlist(self):
|
||||
test = lambda: self.playback.play((17, Track()))
|
||||
self.assertRaises(AssertionError, test)
|
||||
|
||||
@ -9,7 +9,7 @@ from mopidy import audio, core
|
||||
from mopidy.core import PlaybackState
|
||||
from mopidy.models import TlTrack, Playlist, Track
|
||||
|
||||
from tests.backends.base import populate_playlist
|
||||
from tests.backends.base import populate_tracklist
|
||||
|
||||
|
||||
class TracklistControllerTest(object):
|
||||
@ -30,54 +30,56 @@ class TracklistControllerTest(object):
|
||||
def test_length(self):
|
||||
self.assertEqual(0, len(self.controller.tl_tracks))
|
||||
self.assertEqual(0, self.controller.length)
|
||||
self.controller.append(self.tracks)
|
||||
self.controller.add(self.tracks)
|
||||
self.assertEqual(3, len(self.controller.tl_tracks))
|
||||
self.assertEqual(3, self.controller.length)
|
||||
|
||||
def test_add(self):
|
||||
for track in self.tracks:
|
||||
tl_track = self.controller.add(track)
|
||||
tl_tracks = self.controller.add([track])
|
||||
self.assertEqual(track, self.controller.tracks[-1])
|
||||
self.assertEqual(tl_track, self.controller.tl_tracks[-1])
|
||||
self.assertEqual(track, tl_track.track)
|
||||
self.assertEqual(tl_tracks[0], self.controller.tl_tracks[-1])
|
||||
self.assertEqual(track, tl_tracks[0].track)
|
||||
|
||||
def test_add_at_position(self):
|
||||
for track in self.tracks[:-1]:
|
||||
tl_track = self.controller.add(track, 0)
|
||||
tl_tracks = self.controller.add([track], 0)
|
||||
self.assertEqual(track, self.controller.tracks[0])
|
||||
self.assertEqual(tl_track, self.controller.tl_tracks[0])
|
||||
self.assertEqual(track, tl_track.track)
|
||||
self.assertEqual(tl_tracks[0], self.controller.tl_tracks[0])
|
||||
self.assertEqual(track, tl_tracks[0].track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_add_at_position_outside_of_playlist(self):
|
||||
test = lambda: self.controller.add(
|
||||
self.tracks[0], len(self.tracks) + 2)
|
||||
self.assertRaises(AssertionError, test)
|
||||
for track in self.tracks:
|
||||
tl_tracks = self.controller.add([track], len(self.tracks) + 2)
|
||||
self.assertEqual(track, self.controller.tracks[-1])
|
||||
self.assertEqual(tl_tracks[0], self.controller.tl_tracks[-1])
|
||||
self.assertEqual(track, tl_tracks[0].track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_filter_by_tlid(self):
|
||||
tl_track = self.controller.tl_tracks[1]
|
||||
self.assertEqual(
|
||||
[tl_track], self.controller.filter(tlid=tl_track.tlid))
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_filter_by_uri(self):
|
||||
tl_track = self.controller.tl_tracks[1]
|
||||
self.assertEqual(
|
||||
[tl_track], self.controller.filter(uri=tl_track.track.uri))
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_filter_by_uri_returns_nothing_for_invalid_uri(self):
|
||||
self.assertEqual([], self.controller.filter(uri='foobar'))
|
||||
|
||||
def test_filter_by_uri_returns_single_match(self):
|
||||
track = Track(uri='a')
|
||||
self.controller.append([Track(uri='z'), track, Track(uri='y')])
|
||||
self.controller.add([Track(uri='z'), track, Track(uri='y')])
|
||||
self.assertEqual(track, self.controller.filter(uri='a')[0].track)
|
||||
|
||||
def test_filter_by_uri_returns_multiple_matches(self):
|
||||
track = Track(uri='a')
|
||||
self.controller.append([Track(uri='z'), track, track])
|
||||
self.controller.add([Track(uri='z'), track, track])
|
||||
tl_tracks = self.controller.filter(uri='a')
|
||||
self.assertEqual(track, tl_tracks[0].track)
|
||||
self.assertEqual(track, tl_tracks[1].track)
|
||||
@ -91,7 +93,7 @@ class TracklistControllerTest(object):
|
||||
track1 = Track(uri='a', name='x')
|
||||
track2 = Track(uri='b', name='x')
|
||||
track3 = Track(uri='b', name='y')
|
||||
self.controller.append([track1, track2, track3])
|
||||
self.controller.add([track1, track2, track3])
|
||||
self.assertEqual(
|
||||
track1, self.controller.filter(uri='a', name='x')[0].track)
|
||||
self.assertEqual(
|
||||
@ -103,10 +105,10 @@ class TracklistControllerTest(object):
|
||||
track1 = Track()
|
||||
track2 = Track(uri='b')
|
||||
track3 = Track()
|
||||
self.controller.append([track1, track2, track3])
|
||||
self.controller.add([track1, track2, track3])
|
||||
self.assertEqual(track2, self.controller.filter(uri='b')[0].track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_clear(self):
|
||||
self.controller.clear()
|
||||
self.assertEqual(len(self.controller.tracks), 0)
|
||||
@ -115,49 +117,49 @@ class TracklistControllerTest(object):
|
||||
self.controller.clear()
|
||||
self.assertEqual(len(self.controller.tracks), 0)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_clear_when_playing(self):
|
||||
self.playback.play()
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
self.controller.clear()
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
|
||||
def test_append_appends_to_the_tracklist(self):
|
||||
self.controller.append([Track(uri='a'), Track(uri='b')])
|
||||
def test_add_appends_to_the_tracklist(self):
|
||||
self.controller.add([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(len(self.controller.tracks), 2)
|
||||
self.controller.append([Track(uri='c'), Track(uri='d')])
|
||||
self.controller.add([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_append_does_not_reset_version(self):
|
||||
def test_add_does_not_reset_version(self):
|
||||
version = self.controller.version
|
||||
self.controller.append([])
|
||||
self.controller.add([])
|
||||
self.assertEqual(self.controller.version, version)
|
||||
|
||||
@populate_playlist
|
||||
def test_append_preserves_playing_state(self):
|
||||
@populate_tracklist
|
||||
def test_add_preserves_playing_state(self):
|
||||
self.playback.play()
|
||||
track = self.playback.current_track
|
||||
self.controller.append(self.controller.tracks[1:2])
|
||||
self.controller.add(self.controller.tracks[1:2])
|
||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||
self.assertEqual(self.playback.current_track, track)
|
||||
|
||||
@populate_playlist
|
||||
def test_append_preserves_stopped_state(self):
|
||||
self.controller.append(self.controller.tracks[1:2])
|
||||
@populate_tracklist
|
||||
def test_add_preserves_stopped_state(self):
|
||||
self.controller.add(self.controller.tracks[1:2])
|
||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||
self.assertEqual(self.playback.current_track, None)
|
||||
|
||||
@populate_playlist
|
||||
def test_append_returns_the_tl_tracks_that_was_added(self):
|
||||
tl_tracks = self.controller.append(self.controller.tracks[1:2])
|
||||
@populate_tracklist
|
||||
def test_add_returns_the_tl_tracks_that_was_added(self):
|
||||
tl_tracks = self.controller.add(self.controller.tracks[1:2])
|
||||
self.assertEqual(tl_tracks[0].track, self.controller.tracks[1])
|
||||
|
||||
def test_index_returns_index_of_track(self):
|
||||
tl_tracks = self.controller.append(self.tracks)
|
||||
tl_tracks = self.controller.add(self.tracks)
|
||||
self.assertEquals(0, self.controller.index(tl_tracks[0]))
|
||||
self.assertEquals(1, self.controller.index(tl_tracks[1]))
|
||||
self.assertEquals(2, self.controller.index(tl_tracks[2]))
|
||||
@ -166,14 +168,14 @@ class TracklistControllerTest(object):
|
||||
test = lambda: self.controller.index(TlTrack(0, Track()))
|
||||
self.assertRaises(ValueError, test)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_move_single(self):
|
||||
self.controller.move(0, 0, 2)
|
||||
|
||||
tracks = self.controller.tracks
|
||||
self.assertEqual(tracks[2], self.tracks[0])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_move_group(self):
|
||||
self.controller.move(0, 2, 1)
|
||||
|
||||
@ -181,25 +183,25 @@ class TracklistControllerTest(object):
|
||||
self.assertEqual(tracks[1], self.tracks[0])
|
||||
self.assertEqual(tracks[2], self.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_moving_track_outside_of_playlist(self):
|
||||
tracks = len(self.controller.tracks)
|
||||
test = lambda: self.controller.move(0, 0, tracks + 5)
|
||||
self.assertRaises(AssertionError, test)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_move_group_outside_of_playlist(self):
|
||||
tracks = len(self.controller.tracks)
|
||||
test = lambda: self.controller.move(0, 2, tracks + 5)
|
||||
self.assertRaises(AssertionError, test)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_move_group_out_of_range(self):
|
||||
tracks = len(self.controller.tracks)
|
||||
test = lambda: self.controller.move(tracks + 2, tracks + 3, 0)
|
||||
self.assertRaises(AssertionError, test)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_move_group_invalid_group(self):
|
||||
test = lambda: self.controller.move(2, 1, 0)
|
||||
self.assertRaises(AssertionError, test)
|
||||
@ -209,7 +211,7 @@ class TracklistControllerTest(object):
|
||||
tracks2 = self.controller.tracks
|
||||
self.assertNotEqual(id(tracks1), id(tracks2))
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_remove(self):
|
||||
track1 = self.controller.tracks[1]
|
||||
track2 = self.controller.tracks[2]
|
||||
@ -219,14 +221,14 @@ class TracklistControllerTest(object):
|
||||
self.assertNotIn(track1, self.controller.tracks)
|
||||
self.assertEqual(track2, self.controller.tracks[1])
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_removing_track_that_does_not_exist_does_nothing(self):
|
||||
self.controller.remove(uri='/nonexistant')
|
||||
|
||||
def test_removing_from_empty_playlist_does_nothing(self):
|
||||
self.controller.remove(uri='/nonexistant')
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_shuffle(self):
|
||||
random.seed(1)
|
||||
self.controller.shuffle()
|
||||
@ -236,7 +238,7 @@ class TracklistControllerTest(object):
|
||||
self.assertNotEqual(self.tracks, shuffled_tracks)
|
||||
self.assertEqual(set(self.tracks), set(shuffled_tracks))
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_shuffle_subset(self):
|
||||
random.seed(1)
|
||||
self.controller.shuffle(1, 3)
|
||||
@ -247,18 +249,18 @@ class TracklistControllerTest(object):
|
||||
self.assertEqual(self.tracks[0], shuffled_tracks[0])
|
||||
self.assertEqual(set(self.tracks), set(shuffled_tracks))
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_shuffle_invalid_subset(self):
|
||||
test = lambda: self.controller.shuffle(3, 1)
|
||||
self.assertRaises(AssertionError, test)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_shuffle_superset(self):
|
||||
tracks = len(self.controller.tracks)
|
||||
test = lambda: self.controller.shuffle(1, tracks + 5)
|
||||
self.assertRaises(AssertionError, test)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_shuffle_open_subset(self):
|
||||
random.seed(1)
|
||||
self.controller.shuffle(1)
|
||||
@ -269,24 +271,24 @@ class TracklistControllerTest(object):
|
||||
self.assertEqual(self.tracks[0], shuffled_tracks[0])
|
||||
self.assertEqual(set(self.tracks), set(shuffled_tracks))
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_slice_returns_a_subset_of_tracks(self):
|
||||
track_slice = self.controller.slice(1, 3)
|
||||
self.assertEqual(2, len(track_slice))
|
||||
self.assertEqual(self.tracks[1], track_slice[0].track)
|
||||
self.assertEqual(self.tracks[2], track_slice[1].track)
|
||||
|
||||
@populate_playlist
|
||||
@populate_tracklist
|
||||
def test_slice_returns_empty_list_if_indexes_outside_tracks_list(self):
|
||||
self.assertEqual(0, len(self.controller.slice(7, 8)))
|
||||
self.assertEqual(0, len(self.controller.slice(-1, 1)))
|
||||
|
||||
def test_version_does_not_change_when_appending_nothing(self):
|
||||
def test_version_does_not_change_when_adding_nothing(self):
|
||||
version = self.controller.version
|
||||
self.controller.append([])
|
||||
self.controller.add([])
|
||||
self.assertEquals(version, self.controller.version)
|
||||
|
||||
def test_version_increases_when_appending_something(self):
|
||||
def test_version_increases_when_adding_something(self):
|
||||
version = self.controller.version
|
||||
self.controller.append([Track()])
|
||||
self.controller.add([Track()])
|
||||
self.assertLess(version, self.controller.version)
|
||||
|
||||
@ -27,7 +27,7 @@ class LocalPlaybackControllerTest(PlaybackControllerTest, unittest.TestCase):
|
||||
def add_track(self, path):
|
||||
uri = path_to_uri(path_to_data_dir(path))
|
||||
track = Track(uri=uri, length=4464)
|
||||
self.tracklist.add(track)
|
||||
self.tracklist.add([track])
|
||||
|
||||
def test_uri_scheme(self):
|
||||
self.assertIn('file', self.core.uri_schemes)
|
||||
|
||||
@ -26,14 +26,14 @@ class BackendEventsTest(unittest.TestCase):
|
||||
self.assertEqual(send.call_args[0][0], 'playlists_loaded')
|
||||
|
||||
def test_pause_sends_track_playback_paused_event(self, send):
|
||||
self.core.tracklist.add(Track(uri='dummy:a'))
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.play().get()
|
||||
send.reset_mock()
|
||||
self.core.playback.pause().get()
|
||||
self.assertEqual(send.call_args[0][0], 'track_playback_paused')
|
||||
|
||||
def test_resume_sends_track_playback_resumed(self, send):
|
||||
self.core.tracklist.add(Track(uri='dummy:a'))
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.pause().get()
|
||||
send.reset_mock()
|
||||
@ -41,20 +41,20 @@ class BackendEventsTest(unittest.TestCase):
|
||||
self.assertEqual(send.call_args[0][0], 'track_playback_resumed')
|
||||
|
||||
def test_play_sends_track_playback_started_event(self, send):
|
||||
self.core.tracklist.add(Track(uri='dummy:a'))
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
send.reset_mock()
|
||||
self.core.playback.play().get()
|
||||
self.assertEqual(send.call_args[0][0], 'track_playback_started')
|
||||
|
||||
def test_stop_sends_track_playback_ended_event(self, send):
|
||||
self.core.tracklist.add(Track(uri='dummy:a'))
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.play().get()
|
||||
send.reset_mock()
|
||||
self.core.playback.stop().get()
|
||||
self.assertEqual(send.call_args_list[0][0][0], 'track_playback_ended')
|
||||
|
||||
def test_seek_sends_seeked_event(self, send):
|
||||
self.core.tracklist.add(Track(uri='dummy:a', length=40000))
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play().get()
|
||||
send.reset_mock()
|
||||
self.core.playback.seek(1000).get()
|
||||
@ -62,35 +62,30 @@ class BackendEventsTest(unittest.TestCase):
|
||||
|
||||
def test_tracklist_add_sends_tracklist_changed_event(self, send):
|
||||
send.reset_mock()
|
||||
self.core.tracklist.add(Track(uri='dummy:a')).get()
|
||||
self.assertEqual(send.call_args[0][0], 'tracklist_changed')
|
||||
|
||||
def test_tracklist_append_sends_tracklist_changed_event(self, send):
|
||||
send.reset_mock()
|
||||
self.core.tracklist.append([Track(uri='dummy:a')]).get()
|
||||
self.core.tracklist.add([Track(uri='dummy:a')]).get()
|
||||
self.assertEqual(send.call_args[0][0], 'tracklist_changed')
|
||||
|
||||
def test_tracklist_clear_sends_tracklist_changed_event(self, send):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')]).get()
|
||||
self.core.tracklist.add([Track(uri='dummy:a')]).get()
|
||||
send.reset_mock()
|
||||
self.core.tracklist.clear().get()
|
||||
self.assertEqual(send.call_args[0][0], 'tracklist_changed')
|
||||
|
||||
def test_tracklist_move_sends_tracklist_changed_event(self, send):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(uri='dummy:a'), Track(uri='dummy:b')]).get()
|
||||
send.reset_mock()
|
||||
self.core.tracklist.move(0, 1, 1).get()
|
||||
self.assertEqual(send.call_args[0][0], 'tracklist_changed')
|
||||
|
||||
def test_tracklist_remove_sends_tracklist_changed_event(self, send):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')]).get()
|
||||
self.core.tracklist.add([Track(uri='dummy:a')]).get()
|
||||
send.reset_mock()
|
||||
self.core.tracklist.remove(uri='dummy:a').get()
|
||||
self.assertEqual(send.call_args[0][0], 'tracklist_changed')
|
||||
|
||||
def test_tracklist_shuffle_sends_tracklist_changed_event(self, send):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(uri='dummy:a'), Track(uri='dummy:b')]).get()
|
||||
send.reset_mock()
|
||||
self.core.tracklist.shuffle().get()
|
||||
|
||||
@ -35,7 +35,7 @@ class CorePlaybackTest(unittest.TestCase):
|
||||
|
||||
self.core = Core(audio=None, backends=[
|
||||
self.backend1, self.backend2, self.backend3])
|
||||
self.core.tracklist.append(self.tracks)
|
||||
self.core.tracklist.add(self.tracks)
|
||||
|
||||
self.tl_tracks = self.core.tracklist.tl_tracks
|
||||
self.unplayable_tl_track = self.tl_tracks[2]
|
||||
|
||||
@ -10,7 +10,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
needle = Track(uri='dummy://foo')
|
||||
self.backend.library.dummy_library = [
|
||||
Track(), Track(), needle, Track()]
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -33,7 +33,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
needle = Track(uri='dummy://foo')
|
||||
self.backend.library.dummy_library = [
|
||||
Track(), Track(), needle, Track()]
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -52,7 +52,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
needle = Track(uri='dummy://foo')
|
||||
self.backend.library.dummy_library = [
|
||||
Track(), Track(), needle, Track()]
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -67,7 +67,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
needle = Track(uri='dummy://foo')
|
||||
self.backend.library.dummy_library = [
|
||||
Track(), Track(), needle, Track()]
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -79,7 +79,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertEqualResponse('ACK [50@0] {addid} No such song')
|
||||
|
||||
def test_clear(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -89,7 +89,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_delete_songpos(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -99,7 +99,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_delete_songpos_out_of_bounds(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -108,7 +108,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertEqualResponse('ACK [2@0] {delete} Bad song index')
|
||||
|
||||
def test_delete_open_range(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -117,7 +117,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_delete_closed_range(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -126,7 +126,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_delete_range_out_of_bounds(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
|
||||
|
||||
@ -135,7 +135,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertEqualResponse('ACK [2@0] {delete} Bad song index')
|
||||
|
||||
def test_deleteid(self):
|
||||
self.core.tracklist.append([Track(), Track()])
|
||||
self.core.tracklist.add([Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 2)
|
||||
|
||||
self.sendRequest('deleteid "1"')
|
||||
@ -143,7 +143,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_deleteid_does_not_exist(self):
|
||||
self.core.tracklist.append([Track(), Track()])
|
||||
self.core.tracklist.add([Track(), Track()])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 2)
|
||||
|
||||
self.sendRequest('deleteid "12345"')
|
||||
@ -151,7 +151,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertEqualResponse('ACK [50@0] {deleteid} No such song')
|
||||
|
||||
def test_move_songpos(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -167,7 +167,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_move_open_range(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -183,7 +183,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_move_closed_range(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -199,7 +199,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_moveid(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -237,8 +237,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertEqualResponse('OK')
|
||||
|
||||
def test_playlistfind_by_filename_in_tracklist(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='file:///exists')])
|
||||
self.core.tracklist.add([Track(uri='file:///exists')])
|
||||
|
||||
self.sendRequest('playlistfind filename "file:///exists"')
|
||||
self.assertInResponse('file: file:///exists')
|
||||
@ -247,7 +246,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playlistid_without_songid(self):
|
||||
self.core.tracklist.append([Track(name='a'), Track(name='b')])
|
||||
self.core.tracklist.add([Track(name='a'), Track(name='b')])
|
||||
|
||||
self.sendRequest('playlistid')
|
||||
self.assertInResponse('Title: a')
|
||||
@ -255,7 +254,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playlistid_with_songid(self):
|
||||
self.core.tracklist.append([Track(name='a'), Track(name='b')])
|
||||
self.core.tracklist.add([Track(name='a'), Track(name='b')])
|
||||
|
||||
self.sendRequest('playlistid "1"')
|
||||
self.assertNotInResponse('Title: a')
|
||||
@ -265,13 +264,13 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playlistid_with_not_existing_songid_fails(self):
|
||||
self.core.tracklist.append([Track(name='a'), Track(name='b')])
|
||||
self.core.tracklist.add([Track(name='a'), Track(name='b')])
|
||||
|
||||
self.sendRequest('playlistid "25"')
|
||||
self.assertEqualResponse('ACK [50@0] {playlistid} No such song')
|
||||
|
||||
def test_playlistinfo_without_songpos_or_range(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -294,7 +293,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
def test_playlistinfo_with_songpos(self):
|
||||
# Make the track's CPID not match the playlist position
|
||||
self.core.tracklist.tlid = 17
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -320,7 +319,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertEqual(response1, response2)
|
||||
|
||||
def test_playlistinfo_with_open_range(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -341,7 +340,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playlistinfo_with_closed_range(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -372,7 +371,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertEqualResponse('ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_plchanges_with_lower_version_returns_changes(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
|
||||
self.sendRequest('plchanges "0"')
|
||||
@ -382,7 +381,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_plchanges_with_equal_version_returns_nothing(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
|
||||
self.assertEqual(self.core.tracklist.version.get(), 1)
|
||||
@ -393,7 +392,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_plchanges_with_greater_version_returns_nothing(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
|
||||
self.assertEqual(self.core.tracklist.version.get(), 1)
|
||||
@ -404,7 +403,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_plchanges_with_minus_one_returns_entire_playlist(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
|
||||
self.sendRequest('plchanges "-1"')
|
||||
@ -414,7 +413,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_plchanges_without_quotes_works(self):
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
|
||||
self.sendRequest('plchanges 0')
|
||||
@ -424,7 +423,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_plchangesposid(self):
|
||||
self.core.tracklist.append([Track(), Track(), Track()])
|
||||
self.core.tracklist.add([Track(), Track(), Track()])
|
||||
|
||||
self.sendRequest('plchangesposid "0"')
|
||||
tl_tracks = self.core.tracklist.tl_tracks.get()
|
||||
@ -437,7 +436,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_shuffle_without_range(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -448,7 +447,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_shuffle_with_open_range(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -464,7 +463,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_shuffle_with_closed_range(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -480,7 +479,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_swap(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -496,7 +495,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_swapid(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
@ -512,13 +511,13 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_swapid_with_first_id_unknown_should_ack(self):
|
||||
self.core.tracklist.append([Track()])
|
||||
self.core.tracklist.add([Track()])
|
||||
self.sendRequest('swapid "0" "4"')
|
||||
self.assertEqualResponse(
|
||||
'ACK [50@0] {swapid} No such song')
|
||||
|
||||
def test_swapid_with_second_id_unknown_should_ack(self):
|
||||
self.core.tracklist.append([Track()])
|
||||
self.core.tracklist.add([Track()])
|
||||
self.sendRequest('swapid "4" "0"')
|
||||
self.assertEqualResponse(
|
||||
'ACK [50@0] {swapid} No such song')
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from mopidy.models import Album, Artist, Track
|
||||
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
|
||||
@ -119,6 +121,10 @@ class MusicDatabaseFindTest(protocol.BaseTestCase):
|
||||
self.sendRequest('find album "album_what" artist "artist_what"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_find_without_filter_value(self):
|
||||
self.sendRequest('find "album" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
|
||||
class MusicDatabaseListTest(protocol.BaseTestCase):
|
||||
def test_list_foo_returns_ack(self):
|
||||
@ -173,6 +179,18 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
|
||||
'list "artist" "artist" "anartist" "album" "analbum"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_list_artist_without_filter_value(self):
|
||||
self.sendRequest('list "artist" "artist" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_list_artist_should_not_return_artists_without_names(self):
|
||||
self.backend.library.dummy_find_exact_result = [
|
||||
Track(artists=[Artist(name='')])]
|
||||
|
||||
self.sendRequest('list "artist"')
|
||||
self.assertNotInResponse('Artist: ')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
### Album
|
||||
|
||||
def test_list_album_with_quotes(self):
|
||||
@ -191,6 +209,10 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
|
||||
self.sendRequest('list "album" "anartist"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_list_album_with_artist_name_without_filter_value(self):
|
||||
self.sendRequest('list "album" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_list_album_by_artist(self):
|
||||
self.sendRequest('list "album" "artist" "anartist"')
|
||||
self.assertInResponse('OK')
|
||||
@ -216,6 +238,18 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
|
||||
'list "album" "artist" "anartist" "album" "analbum"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_list_album_without_filter_value(self):
|
||||
self.sendRequest('list "album" "artist" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_list_album_should_not_return_albums_without_names(self):
|
||||
self.backend.library.dummy_find_exact_result = [
|
||||
Track(album=Album(name=''))]
|
||||
|
||||
self.sendRequest('list "album"')
|
||||
self.assertNotInResponse('Album: ')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
### Date
|
||||
|
||||
def test_list_date_with_quotes(self):
|
||||
@ -259,6 +293,17 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
|
||||
self.sendRequest('list "date" "artist" "anartist" "album" "analbum"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_list_date_without_filter_value(self):
|
||||
self.sendRequest('list "date" "artist" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_list_date_should_not_return_blank_dates(self):
|
||||
self.backend.library.dummy_find_exact_result = [Track(date='')]
|
||||
|
||||
self.sendRequest('list "date"')
|
||||
self.assertNotInResponse('Date: ')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
### Genre
|
||||
|
||||
def test_list_genre_with_quotes(self):
|
||||
@ -303,6 +348,10 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
|
||||
'list "genre" "artist" "anartist" "album" "analbum"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_list_genre_without_filter_value(self):
|
||||
self.sendRequest('list "genre" "artist" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
|
||||
class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
def test_search_album(self):
|
||||
@ -313,6 +362,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
self.sendRequest('search album "analbum"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_album_without_filter_value(self):
|
||||
self.sendRequest('search "album" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_artist(self):
|
||||
self.sendRequest('search "artist" "anartist"')
|
||||
self.assertInResponse('OK')
|
||||
@ -321,6 +374,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
self.sendRequest('search artist "anartist"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_artist_without_filter_value(self):
|
||||
self.sendRequest('search "artist" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_filename(self):
|
||||
self.sendRequest('search "filename" "afilename"')
|
||||
self.assertInResponse('OK')
|
||||
@ -329,6 +386,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
self.sendRequest('search filename "afilename"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_filename_without_filter_value(self):
|
||||
self.sendRequest('search "filename" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_file(self):
|
||||
self.sendRequest('search "file" "afilename"')
|
||||
self.assertInResponse('OK')
|
||||
@ -337,6 +398,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
self.sendRequest('search file "afilename"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_file_without_filter_value(self):
|
||||
self.sendRequest('search "file" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_title(self):
|
||||
self.sendRequest('search "title" "atitle"')
|
||||
self.assertInResponse('OK')
|
||||
@ -345,6 +410,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
self.sendRequest('search title "atitle"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_title_without_filter_value(self):
|
||||
self.sendRequest('search "title" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_any(self):
|
||||
self.sendRequest('search "any" "anything"')
|
||||
self.assertInResponse('OK')
|
||||
@ -353,6 +422,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
self.sendRequest('search any "anything"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_any_without_filter_value(self):
|
||||
self.sendRequest('search "any" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_date(self):
|
||||
self.sendRequest('search "date" "2002-01-01"')
|
||||
self.assertInResponse('OK')
|
||||
@ -365,6 +438,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
self.sendRequest('search Date "2005"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_date_without_filter_value(self):
|
||||
self.sendRequest('search "date" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_else_should_fail(self):
|
||||
self.sendRequest('search "sometype" "something"')
|
||||
self.assertEqualResponse('ACK [2@0] {search} incorrect arguments')
|
||||
|
||||
@ -168,7 +168,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_pause_off(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
|
||||
self.sendRequest('play "0"')
|
||||
self.sendRequest('pause "1"')
|
||||
@ -177,7 +177,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_pause_on(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
|
||||
self.sendRequest('play "0"')
|
||||
self.sendRequest('pause "1"')
|
||||
@ -185,7 +185,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_pause_toggle(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
|
||||
self.sendRequest('play "0"')
|
||||
self.assertEqual(PLAYING, self.core.playback.state.get())
|
||||
@ -200,28 +200,28 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_play_without_pos(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
|
||||
self.sendRequest('play')
|
||||
self.assertEqual(PLAYING, self.core.playback.state.get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_play_with_pos(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
|
||||
self.sendRequest('play "0"')
|
||||
self.assertEqual(PLAYING, self.core.playback.state.get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_play_with_pos_without_quotes(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
|
||||
self.sendRequest('play 0')
|
||||
self.assertEqual(PLAYING, self.core.playback.state.get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_play_with_pos_out_of_bounds(self):
|
||||
self.core.tracklist.append([])
|
||||
self.core.tracklist.add([])
|
||||
|
||||
self.sendRequest('play "0"')
|
||||
self.assertEqual(STOPPED, self.core.playback.state.get())
|
||||
@ -229,10 +229,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
|
||||
def test_play_minus_one_plays_first_in_playlist_if_no_current_track(self):
|
||||
self.assertEqual(self.core.playback.current_track.get(), None)
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'),
|
||||
Track(uri='dummy:b'),
|
||||
])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
|
||||
self.sendRequest('play "-1"')
|
||||
self.assertEqual(PLAYING, self.core.playback.state.get())
|
||||
@ -241,10 +238,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_play_minus_one_plays_current_track_if_current_track_is_set(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'),
|
||||
Track(uri='dummy:b'),
|
||||
])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.assertEqual(self.core.playback.current_track.get(), None)
|
||||
self.core.playback.play()
|
||||
self.core.playback.next()
|
||||
@ -266,8 +260,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_play_minus_is_ignored_if_playing(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.seek(30000)
|
||||
self.assertGreaterEqual(
|
||||
self.core.playback.time_position.get(), 30000)
|
||||
@ -280,8 +273,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_play_minus_one_resumes_if_paused(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.seek(30000)
|
||||
self.assertGreaterEqual(
|
||||
self.core.playback.time_position.get(), 30000)
|
||||
@ -296,14 +288,14 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playid(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
|
||||
self.sendRequest('playid "0"')
|
||||
self.assertEqual(PLAYING, self.core.playback.state.get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playid_without_quotes(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
|
||||
self.sendRequest('playid 0')
|
||||
self.assertEqual(PLAYING, self.core.playback.state.get())
|
||||
@ -311,10 +303,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
|
||||
def test_playid_minus_1_plays_first_in_playlist_if_no_current_track(self):
|
||||
self.assertEqual(self.core.playback.current_track.get(), None)
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'),
|
||||
Track(uri='dummy:b'),
|
||||
])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
|
||||
self.sendRequest('playid "-1"')
|
||||
self.assertEqual(PLAYING, self.core.playback.state.get())
|
||||
@ -323,10 +312,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playid_minus_1_plays_current_track_if_current_track_is_set(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'),
|
||||
Track(uri='dummy:b'),
|
||||
])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.assertEqual(self.core.playback.current_track.get(), None)
|
||||
self.core.playback.play()
|
||||
self.core.playback.next()
|
||||
@ -348,7 +334,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playid_minus_is_ignored_if_playing(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.seek(30000)
|
||||
self.assertGreaterEqual(
|
||||
self.core.playback.time_position.get(), 30000)
|
||||
@ -361,7 +347,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playid_minus_one_resumes_if_paused(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.seek(30000)
|
||||
self.assertGreaterEqual(
|
||||
self.core.playback.time_position.get(), 30000)
|
||||
@ -376,7 +362,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_playid_which_does_not_exist(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
|
||||
self.sendRequest('playid "12345"')
|
||||
self.assertInResponse('ACK [50@0] {playid} No such song')
|
||||
@ -386,7 +372,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_seek(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
|
||||
self.sendRequest('seek "0"')
|
||||
self.sendRequest('seek "0" "30"')
|
||||
@ -395,7 +381,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
|
||||
def test_seek_with_songpos(self):
|
||||
seek_track = Track(uri='dummy:b', length=40000)
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(uri='dummy:a', length=40000), seek_track])
|
||||
|
||||
self.sendRequest('seek "1" "30"')
|
||||
@ -403,7 +389,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_seek_without_quotes(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
|
||||
self.sendRequest('seek 0')
|
||||
self.sendRequest('seek 0 30')
|
||||
@ -412,7 +398,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_seekid(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.sendRequest('seekid "0" "30"')
|
||||
self.assertGreaterEqual(
|
||||
self.core.playback.time_position.get(), 30000)
|
||||
@ -420,11 +406,11 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
|
||||
def test_seekid_with_tlid(self):
|
||||
seek_track = Track(uri='dummy:b', length=40000)
|
||||
self.core.tracklist.append(
|
||||
self.core.tracklist.add(
|
||||
[Track(uri='dummy:a', length=40000), seek_track])
|
||||
|
||||
self.sendRequest('seekid "1" "30"')
|
||||
self.assertEqual(1, self.core.playback.current_tlid.get())
|
||||
self.assertEqual(1, self.core.playback.current_tl_track.get().tlid)
|
||||
self.assertEqual(seek_track, self.core.playback.current_track.get())
|
||||
self.assertInResponse('OK')
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ class ReflectionHandlerTest(protocol.BaseTestCase):
|
||||
|
||||
def test_decoders(self):
|
||||
self.sendRequest('decoders')
|
||||
self.assertInResponse('ACK [0@0] {} Not implemented')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_notcommands_returns_only_kill_and_ok(self):
|
||||
response = self.sendRequest('notcommands')
|
||||
|
||||
@ -18,7 +18,7 @@ class IssueGH17RegressionTest(protocol.BaseTestCase):
|
||||
- Press next until you get to the unplayable track
|
||||
"""
|
||||
def test(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(uri='dummy:a'),
|
||||
Track(uri='dummy:b'),
|
||||
Track(uri='dummy:error'),
|
||||
@ -59,7 +59,7 @@ class IssueGH18RegressionTest(protocol.BaseTestCase):
|
||||
"""
|
||||
|
||||
def test(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b'), Track(uri='dummy:c'),
|
||||
Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')])
|
||||
random.seed(1)
|
||||
@ -95,7 +95,7 @@ class IssueGH22RegressionTest(protocol.BaseTestCase):
|
||||
"""
|
||||
|
||||
def test(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b'), Track(uri='dummy:c'),
|
||||
Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')])
|
||||
random.seed(1)
|
||||
@ -124,7 +124,7 @@ class IssueGH69RegressionTest(protocol.BaseTestCase):
|
||||
|
||||
def test(self):
|
||||
self.core.playlists.create('foo')
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b'), Track(uri='dummy:c'),
|
||||
Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')])
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ class StatusHandlerTest(protocol.BaseTestCase):
|
||||
|
||||
def test_currentsong(self):
|
||||
track = Track()
|
||||
self.core.tracklist.append([track])
|
||||
self.core.tracklist.add([track])
|
||||
self.core.playback.play()
|
||||
self.sendRequest('currentsong')
|
||||
self.assertInResponse('file: ')
|
||||
|
||||
@ -10,18 +10,18 @@ from tests.frontends.mpd import protocol
|
||||
class PlaylistsHandlerTest(protocol.BaseTestCase):
|
||||
def test_listplaylist(self):
|
||||
self.backend.playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
Playlist(name='name', tracks=[Track(uri='dummy:a')])]
|
||||
|
||||
self.sendRequest('listplaylist "name"')
|
||||
self.assertInResponse('file: file:///dev/urandom')
|
||||
self.assertInResponse('file: dummy:a')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_listplaylist_without_quotes(self):
|
||||
self.backend.playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
Playlist(name='name', tracks=[Track(uri='dummy:a')])]
|
||||
|
||||
self.sendRequest('listplaylist name')
|
||||
self.assertInResponse('file: file:///dev/urandom')
|
||||
self.assertInResponse('file: dummy:a')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_listplaylist_fails_if_no_playlist_is_found(self):
|
||||
@ -30,20 +30,20 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
|
||||
|
||||
def test_listplaylistinfo(self):
|
||||
self.backend.playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
Playlist(name='name', tracks=[Track(uri='dummy:a')])]
|
||||
|
||||
self.sendRequest('listplaylistinfo "name"')
|
||||
self.assertInResponse('file: file:///dev/urandom')
|
||||
self.assertInResponse('file: dummy:a')
|
||||
self.assertInResponse('Track: 0')
|
||||
self.assertNotInResponse('Pos: 0')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_listplaylistinfo_without_quotes(self):
|
||||
self.backend.playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
Playlist(name='name', tracks=[Track(uri='dummy:a')])]
|
||||
|
||||
self.sendRequest('listplaylistinfo name')
|
||||
self.assertInResponse('file: file:///dev/urandom')
|
||||
self.assertInResponse('file: dummy:a')
|
||||
self.assertInResponse('Track: 0')
|
||||
self.assertNotInResponse('Pos: 0')
|
||||
self.assertInResponse('OK')
|
||||
@ -64,8 +64,17 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('Last-Modified: 2001-03-17T13:41:17Z')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_listplaylists_ignores_playlists_without_name(self):
|
||||
last_modified = datetime.datetime(2001, 3, 17, 13, 41, 17, 12345)
|
||||
self.backend.playlists.playlists = [
|
||||
Playlist(name='', last_modified=last_modified)]
|
||||
|
||||
self.sendRequest('listplaylists')
|
||||
self.assertNotInResponse('playlist: ')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_load_known_playlist_appends_to_tracklist(self):
|
||||
self.core.tracklist.append([Track(uri='a'), Track(uri='b')])
|
||||
self.core.tracklist.add([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(len(self.core.tracklist.tracks.get()), 2)
|
||||
self.backend.playlists.playlists = [
|
||||
Playlist(name='A-list', tracks=[
|
||||
@ -87,7 +96,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
|
||||
self.assertEqualResponse('ACK [50@0] {load} No such playlist')
|
||||
|
||||
def test_playlistadd(self):
|
||||
self.sendRequest('playlistadd "name" "file:///dev/urandom"')
|
||||
self.sendRequest('playlistadd "name" "dummy:a"')
|
||||
self.assertEqualResponse('ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_playlistclear(self):
|
||||
|
||||
@ -131,21 +131,21 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(result['state'], 'pause')
|
||||
|
||||
def test_status_method_when_playlist_loaded_contains_song(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assertIn('song', result)
|
||||
self.assertGreaterEqual(int(result['song']), 0)
|
||||
|
||||
def test_status_method_when_playlist_loaded_contains_tlid_as_songid(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assertIn('songid', result)
|
||||
self.assertEqual(int(result['songid']), 0)
|
||||
|
||||
def test_status_method_when_playing_contains_time_with_no_length(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=None)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=None)])
|
||||
self.core.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assertIn('time', result)
|
||||
@ -155,7 +155,7 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assertLessEqual(position, total)
|
||||
|
||||
def test_status_method_when_playing_contains_time_with_length(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=10000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=10000)])
|
||||
self.core.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assertIn('time', result)
|
||||
@ -165,7 +165,7 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assertLessEqual(position, total)
|
||||
|
||||
def test_status_method_when_playing_contains_elapsed(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=60000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=60000)])
|
||||
self.core.playback.play()
|
||||
self.core.playback.pause()
|
||||
self.core.playback.seek(59123)
|
||||
@ -174,7 +174,7 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(result['elapsed'], '59.123')
|
||||
|
||||
def test_status_method_when_starting_playing_contains_elapsed_zero(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=10000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=10000)])
|
||||
self.core.playback.play()
|
||||
self.core.playback.pause()
|
||||
result = dict(status.status(self.context))
|
||||
@ -182,7 +182,7 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(result['elapsed'], '0.000')
|
||||
|
||||
def test_status_method_when_playing_contains_bitrate(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', bitrate=320)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', bitrate=320)])
|
||||
self.core.playback.play()
|
||||
result = dict(status.status(self.context))
|
||||
self.assertIn('bitrate', result)
|
||||
|
||||
@ -101,16 +101,14 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_set_rate_is_ignored_if_can_control_is_false(self):
|
||||
self.mpris.get_CanControl = lambda *_: False
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0)
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
|
||||
def test_set_rate_to_zero_pauses_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0)
|
||||
@ -150,7 +148,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(result['mpris:trackid'], '')
|
||||
|
||||
def test_get_metadata_has_trackid_based_on_tlid(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.play()
|
||||
(tlid, track) = self.core.playback.current_tl_track.get()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
|
||||
@ -159,28 +157,28 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
result['mpris:trackid'], '/com/mopidy/track/%d' % tlid)
|
||||
|
||||
def test_get_metadata_has_track_length(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
|
||||
self.assertIn('mpris:length', result.keys())
|
||||
self.assertEqual(result['mpris:length'], 40000000)
|
||||
|
||||
def test_get_metadata_has_track_uri(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
|
||||
self.assertIn('xesam:url', result.keys())
|
||||
self.assertEqual(result['xesam:url'], 'dummy:a')
|
||||
|
||||
def test_get_metadata_has_track_title(self):
|
||||
self.core.tracklist.append([Track(name='a')])
|
||||
self.core.tracklist.add([Track(name='a')])
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
|
||||
self.assertIn('xesam:title', result.keys())
|
||||
self.assertEqual(result['xesam:title'], 'a')
|
||||
|
||||
def test_get_metadata_has_track_artists(self):
|
||||
self.core.tracklist.append([Track(artists=[
|
||||
self.core.tracklist.add([Track(artists=[
|
||||
Artist(name='a'), Artist(name='b'), Artist(name=None)])])
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
|
||||
@ -188,14 +186,14 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(result['xesam:artist'], ['a', 'b'])
|
||||
|
||||
def test_get_metadata_has_track_album(self):
|
||||
self.core.tracklist.append([Track(album=Album(name='a'))])
|
||||
self.core.tracklist.add([Track(album=Album(name='a'))])
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
|
||||
self.assertIn('xesam:album', result.keys())
|
||||
self.assertEqual(result['xesam:album'], 'a')
|
||||
|
||||
def test_get_metadata_has_track_album_artists(self):
|
||||
self.core.tracklist.append([Track(album=Album(artists=[
|
||||
self.core.tracklist.add([Track(album=Album(artists=[
|
||||
Artist(name='a'), Artist(name='b'), Artist(name=None)]))])
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
|
||||
@ -203,7 +201,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(result['xesam:albumArtist'], ['a', 'b'])
|
||||
|
||||
def test_get_metadata_has_track_number_in_album(self):
|
||||
self.core.tracklist.append([Track(track_no=7)])
|
||||
self.core.tracklist.add([Track(track_no=7)])
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
|
||||
self.assertIn('xesam:trackNumber', result.keys())
|
||||
@ -246,7 +244,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.volume.get(), 10)
|
||||
|
||||
def test_get_position_returns_time_position_in_microseconds(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
self.core.playback.seek(10000)
|
||||
result_in_microseconds = self.mpris.Get(
|
||||
@ -270,15 +268,14 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_can_go_next_is_true_if_can_control_and_other_next_track(self):
|
||||
self.mpris.get_CanControl = lambda *_: True
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext')
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_can_go_next_is_false_if_next_track_is_the_same(self):
|
||||
self.mpris.get_CanControl = lambda *_: True
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.repeat = True
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext')
|
||||
@ -286,16 +283,14 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_can_go_next_is_false_if_can_control_is_false(self):
|
||||
self.mpris.get_CanControl = lambda *_: False
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext')
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_can_go_previous_is_true_if_can_control_and_previous_track(self):
|
||||
self.mpris.get_CanControl = lambda *_: True
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.next()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious')
|
||||
@ -303,7 +298,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_can_go_previous_is_false_if_previous_track_is_the_same(self):
|
||||
self.mpris.get_CanControl = lambda *_: True
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.repeat = True
|
||||
self.core.playback.play()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious')
|
||||
@ -311,8 +306,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_can_go_previous_is_false_if_can_control_is_false(self):
|
||||
self.mpris.get_CanControl = lambda *_: False
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.next()
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious')
|
||||
@ -320,7 +314,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_can_play_is_true_if_can_control_and_current_track(self):
|
||||
self.mpris.get_CanControl = lambda *_: True
|
||||
self.core.tracklist.append([Track(uri='dummy:a')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a')])
|
||||
self.core.playback.play()
|
||||
self.assertTrue(self.core.playback.current_track.get())
|
||||
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanPlay')
|
||||
@ -363,16 +357,14 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_next_is_ignored_if_can_go_next_is_false(self):
|
||||
self.mpris.get_CanGoNext = lambda *_: False
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
|
||||
self.mpris.Next()
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
|
||||
|
||||
def test_next_when_playing_skips_to_next_track_and_keep_playing(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
@ -381,8 +373,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
|
||||
def test_next_when_at_end_of_list_should_stop_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.next()
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b')
|
||||
@ -391,8 +382,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.state.get(), STOPPED)
|
||||
|
||||
def test_next_when_paused_should_skip_to_next_track_and_stay_paused(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.pause()
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
|
||||
@ -402,8 +392,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.state.get(), PAUSED)
|
||||
|
||||
def test_next_when_stopped_skips_to_next_track_and_stay_stopped(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.stop()
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
|
||||
@ -414,8 +403,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_previous_is_ignored_if_can_go_previous_is_false(self):
|
||||
self.mpris.get_CanGoPrevious = lambda *_: False
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.next()
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b')
|
||||
@ -423,8 +411,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b')
|
||||
|
||||
def test_previous_when_playing_skips_to_prev_track_and_keep_playing(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.next()
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b')
|
||||
@ -434,8 +421,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
|
||||
def test_previous_when_at_start_of_list_should_stop_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
@ -443,8 +429,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.state.get(), STOPPED)
|
||||
|
||||
def test_previous_when_paused_skips_to_previous_track_and_pause(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.next()
|
||||
self.core.playback.pause()
|
||||
@ -455,8 +440,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.state.get(), PAUSED)
|
||||
|
||||
def test_previous_when_stopped_skips_to_previous_track_and_stops(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.next()
|
||||
self.core.playback.stop()
|
||||
@ -468,24 +452,21 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_pause_is_ignored_if_can_pause_is_false(self):
|
||||
self.mpris.get_CanPause = lambda *_: False
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
self.mpris.Pause()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
|
||||
def test_pause_when_playing_should_pause_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
self.mpris.Pause()
|
||||
self.assertEqual(self.core.playback.state.get(), PAUSED)
|
||||
|
||||
def test_pause_when_paused_has_no_effect(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.pause()
|
||||
self.assertEqual(self.core.playback.state.get(), PAUSED)
|
||||
@ -494,24 +475,21 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_playpause_is_ignored_if_can_pause_is_false(self):
|
||||
self.mpris.get_CanPause = lambda *_: False
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
self.mpris.PlayPause()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
|
||||
def test_playpause_when_playing_should_pause_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
self.mpris.PlayPause()
|
||||
self.assertEqual(self.core.playback.state.get(), PAUSED)
|
||||
|
||||
def test_playpause_when_paused_should_resume_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.pause()
|
||||
|
||||
@ -526,32 +504,28 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertGreaterEqual(after_pause, at_pause)
|
||||
|
||||
def test_playpause_when_stopped_should_start_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.assertEqual(self.core.playback.state.get(), STOPPED)
|
||||
self.mpris.PlayPause()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
|
||||
def test_stop_is_ignored_if_can_control_is_false(self):
|
||||
self.mpris.get_CanControl = lambda *_: False
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
self.mpris.Stop()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
|
||||
def test_stop_when_playing_should_stop_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
self.mpris.Stop()
|
||||
self.assertEqual(self.core.playback.state.get(), STOPPED)
|
||||
|
||||
def test_stop_when_paused_should_stop_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.pause()
|
||||
self.assertEqual(self.core.playback.state.get(), PAUSED)
|
||||
@ -560,21 +534,19 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_play_is_ignored_if_can_play_is_false(self):
|
||||
self.mpris.get_CanPlay = lambda *_: False
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.assertEqual(self.core.playback.state.get(), STOPPED)
|
||||
self.mpris.Play()
|
||||
self.assertEqual(self.core.playback.state.get(), STOPPED)
|
||||
|
||||
def test_play_when_stopped_starts_playback(self):
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.assertEqual(self.core.playback.state.get(), STOPPED)
|
||||
self.mpris.Play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
|
||||
def test_play_after_pause_resumes_from_same_position(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
|
||||
before_pause = self.core.playback.time_position.get()
|
||||
@ -598,7 +570,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_seek_is_ignored_if_can_seek_is_false(self):
|
||||
self.mpris.get_CanSeek = lambda *_: False
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
|
||||
before_seek = self.core.playback.time_position.get()
|
||||
@ -614,7 +586,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertLess(after_seek, before_seek + milliseconds_to_seek)
|
||||
|
||||
def test_seek_seeks_given_microseconds_forward_in_the_current_track(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
|
||||
before_seek = self.core.playback.time_position.get()
|
||||
@ -631,7 +603,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertGreaterEqual(after_seek, before_seek + milliseconds_to_seek)
|
||||
|
||||
def test_seek_seeks_given_microseconds_backward_if_negative(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
self.core.playback.seek(20000)
|
||||
|
||||
@ -650,7 +622,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertLess(after_seek, before_seek)
|
||||
|
||||
def test_seek_seeks_to_start_of_track_if_new_position_is_negative(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
self.core.playback.seek(20000)
|
||||
|
||||
@ -670,7 +642,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertGreaterEqual(after_seek, 0)
|
||||
|
||||
def test_seek_skips_to_next_track_if_new_position_gt_track_length(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(uri='dummy:a', length=40000),
|
||||
Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
@ -695,7 +667,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
|
||||
def test_set_position_is_ignored_if_can_seek_is_false(self):
|
||||
self.mpris.get_CanSeek = lambda *_: False
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
|
||||
before_set_position = self.core.playback.time_position.get()
|
||||
@ -713,7 +685,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertLess(after_set_position, position_to_set_in_millisec)
|
||||
|
||||
def test_set_position_sets_the_current_track_position_in_microsecs(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
|
||||
before_set_position = self.core.playback.time_position.get()
|
||||
@ -734,7 +706,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
after_set_position, position_to_set_in_millisec)
|
||||
|
||||
def test_set_position_does_nothing_if_the_position_is_negative(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
self.core.playback.seek(20000)
|
||||
|
||||
@ -757,7 +729,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
|
||||
|
||||
def test_set_position_does_nothing_if_position_is_gt_track_length(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
self.core.playback.seek(20000)
|
||||
|
||||
@ -780,7 +752,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
|
||||
|
||||
def test_set_position_is_noop_if_track_id_isnt_current_track(self):
|
||||
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
|
||||
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
|
||||
self.core.playback.play()
|
||||
self.core.playback.seek(20000)
|
||||
|
||||
@ -826,8 +798,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
def test_open_uri_starts_playback_of_new_track_if_stopped(self):
|
||||
self.mpris.get_CanPlay = lambda *_: True
|
||||
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.assertEqual(self.core.playback.state.get(), STOPPED)
|
||||
|
||||
self.mpris.OpenUri('dummy:/test/uri')
|
||||
@ -839,8 +810,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
def test_open_uri_starts_playback_of_new_track_if_paused(self):
|
||||
self.mpris.get_CanPlay = lambda *_: True
|
||||
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.core.playback.pause()
|
||||
self.assertEqual(self.core.playback.state.get(), PAUSED)
|
||||
@ -855,8 +825,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
def test_open_uri_starts_playback_of_new_track_if_playing(self):
|
||||
self.mpris.get_CanPlay = lambda *_: True
|
||||
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
|
||||
self.core.tracklist.append([
|
||||
Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
|
||||
self.core.playback.play()
|
||||
self.assertEqual(self.core.playback.state.get(), PLAYING)
|
||||
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
|
||||
|
||||
@ -44,7 +44,7 @@ class PlayerInterfaceTest(unittest.TestCase):
|
||||
pykka.ActorRegistry.stop_all()
|
||||
|
||||
def test_activate_playlist_appends_tracks_to_tracklist(self):
|
||||
self.core.tracklist.append([
|
||||
self.core.tracklist.add([
|
||||
Track(uri='dummy:old-a'),
|
||||
Track(uri='dummy:old-b'),
|
||||
])
|
||||
|
||||
@ -30,5 +30,6 @@ class VersionTest(unittest.TestCase):
|
||||
self.assertLess(SV('0.7.1'), SV('0.7.2'))
|
||||
self.assertLess(SV('0.7.2'), SV('0.7.3'))
|
||||
self.assertLess(SV('0.7.3'), SV('0.8.0'))
|
||||
self.assertLess(SV('0.8.0'), SV(__version__))
|
||||
self.assertLess(SV(__version__), SV('0.8.2'))
|
||||
self.assertLess(SV('0.8.0'), SV('0.8.1'))
|
||||
self.assertLess(SV('0.8.1'), SV(__version__))
|
||||
self.assertLess(SV(__version__), SV('0.9.1'))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user