Merge pull request #537 from jodal/tidy-up-core
Core playback and tracklist modularity improvement v2
This commit is contained in:
commit
c8f5c1aacf
2
.gitignore
vendored
2
.gitignore
vendored
@ -14,3 +14,5 @@ docs/_build/
|
|||||||
mopidy.log*
|
mopidy.log*
|
||||||
node_modules/
|
node_modules/
|
||||||
nosetests.xml
|
nosetests.xml
|
||||||
|
*~
|
||||||
|
*.orig
|
||||||
|
|||||||
@ -39,6 +39,45 @@ of the following extensions as well:
|
|||||||
|
|
||||||
**Core**
|
**Core**
|
||||||
|
|
||||||
|
- Parts of the functionality in :class:`mopidy.core.PlaybackController` have
|
||||||
|
been moved to :class:`mopidy.core.TracklistController`:
|
||||||
|
|
||||||
|
=================================== ==================================
|
||||||
|
Old location New location
|
||||||
|
=================================== ==================================
|
||||||
|
playback.get_consume() tracklist.get_consume()
|
||||||
|
playback.set_consume(v) tracklist.set_consume(v)
|
||||||
|
playback.consume tracklist.consume
|
||||||
|
|
||||||
|
playback.get_random() tracklist.get_random()
|
||||||
|
playback.set_random(v) tracklist.set_random(v)
|
||||||
|
playback.random tracklist.random
|
||||||
|
|
||||||
|
playback.get_repeat() tracklist.get_repeat()
|
||||||
|
playback.set_repeat(v) tracklist.set_repeat(v)
|
||||||
|
playback.repeat tracklist.repeat
|
||||||
|
|
||||||
|
playback.get_single() tracklist.get_single()
|
||||||
|
playback.set_single(v) tracklist.set_single(v)
|
||||||
|
playback.single tracklist.single
|
||||||
|
|
||||||
|
playback.get_tracklist_position() tracklist.index(tl_track)
|
||||||
|
playback.tracklist_position tracklist.index(tl_track)
|
||||||
|
|
||||||
|
playback.get_tl_track_at_eot() tracklist.eot_track(tl_track)
|
||||||
|
playback.tl_track_at_eot tracklist.eot_track(tl_track)
|
||||||
|
|
||||||
|
playback.get_tl_track_at_next() tracklist.next_track(tl_track)
|
||||||
|
playback.tl_track_at_next tracklist.next_track(tl_track)
|
||||||
|
|
||||||
|
playback.get_tl_track_at_previous() tracklist.previous_track(tl_track)
|
||||||
|
playback.tl_track_at_previous tracklist.previous_track(tl_track)
|
||||||
|
=================================== ==================================
|
||||||
|
|
||||||
|
The ``tl_track`` argument to the last four new functions are used as the
|
||||||
|
reference ``tl_track`` in the tracklist to find e.g. the next track. Usually,
|
||||||
|
this will be :attr:`~mopidy.core.PlaybackController.current_tl_track`.
|
||||||
|
|
||||||
- Added :attr:`mopidy.core.PlaybackController.mute` for muting and unmuting
|
- Added :attr:`mopidy.core.PlaybackController.mute` for muting and unmuting
|
||||||
audio. (Fixes: :issue:`186`)
|
audio. (Fixes: :issue:`186`)
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import random
|
|
||||||
import urlparse
|
import urlparse
|
||||||
|
|
||||||
from mopidy.audio import PlaybackState
|
from mopidy.audio import PlaybackState
|
||||||
@ -21,8 +20,6 @@ class PlaybackController(object):
|
|||||||
self.core = core
|
self.core = core
|
||||||
|
|
||||||
self._state = PlaybackState.STOPPED
|
self._state = PlaybackState.STOPPED
|
||||||
self._shuffled = []
|
|
||||||
self._first_shuffle = True
|
|
||||||
self._volume = None
|
self._volume = None
|
||||||
self._mute = False
|
self._mute = False
|
||||||
|
|
||||||
@ -35,22 +32,6 @@ class PlaybackController(object):
|
|||||||
|
|
||||||
### Properties
|
### Properties
|
||||||
|
|
||||||
def get_consume(self):
|
|
||||||
return getattr(self, '_consume', False)
|
|
||||||
|
|
||||||
def set_consume(self, value):
|
|
||||||
if self.get_consume() != value:
|
|
||||||
self._trigger_options_changed()
|
|
||||||
return setattr(self, '_consume', value)
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_current_tl_track(self):
|
def get_current_tl_track(self):
|
||||||
return self.current_tl_track
|
return self.current_tl_track
|
||||||
|
|
||||||
@ -70,56 +51,6 @@ class PlaybackController(object):
|
|||||||
Read-only. Extracted from :attr:`current_tl_track` for convenience.
|
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):
|
def get_state(self):
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
@ -157,119 +88,6 @@ class PlaybackController(object):
|
|||||||
time_position = property(get_time_position)
|
time_position = property(get_time_position)
|
||||||
"""Time position in milliseconds."""
|
"""Time position in milliseconds."""
|
||||||
|
|
||||||
def get_tracklist_position(self):
|
|
||||||
if self.current_tl_track is None:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
return self.core.tracklist.tl_tracks.index(self.current_tl_track)
|
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
tracklist_position = property(get_tracklist_position)
|
|
||||||
"""
|
|
||||||
The position of the current track in the tracklist.
|
|
||||||
|
|
||||||
Read-only.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_tl_track_at_eot(self):
|
|
||||||
tl_tracks = self.core.tracklist.tl_tracks
|
|
||||||
|
|
||||||
if not tl_tracks:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self.random and not self._shuffled:
|
|
||||||
if self.repeat or self._first_shuffle:
|
|
||||||
logger.debug('Shuffling tracks')
|
|
||||||
self._shuffled = tl_tracks
|
|
||||||
random.shuffle(self._shuffled)
|
|
||||||
self._first_shuffle = False
|
|
||||||
|
|
||||||
if self.random and self._shuffled:
|
|
||||||
return self._shuffled[0]
|
|
||||||
|
|
||||||
if self.current_tl_track is None:
|
|
||||||
return tl_tracks[0]
|
|
||||||
|
|
||||||
if self.repeat and self.single:
|
|
||||||
return tl_tracks[self.tracklist_position]
|
|
||||||
|
|
||||||
if self.repeat and not self.single:
|
|
||||||
return tl_tracks[(self.tracklist_position + 1) % len(tl_tracks)]
|
|
||||||
|
|
||||||
try:
|
|
||||||
return tl_tracks[self.tracklist_position + 1]
|
|
||||||
except IndexError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
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.TlTrack`.
|
|
||||||
|
|
||||||
Not necessarily the same track as :attr:`tl_track_at_next`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_tl_track_at_next(self):
|
|
||||||
tl_tracks = self.core.tracklist.tl_tracks
|
|
||||||
|
|
||||||
if not tl_tracks:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self.random and not self._shuffled:
|
|
||||||
if self.repeat or self._first_shuffle:
|
|
||||||
logger.debug('Shuffling tracks')
|
|
||||||
self._shuffled = tl_tracks
|
|
||||||
random.shuffle(self._shuffled)
|
|
||||||
self._first_shuffle = False
|
|
||||||
|
|
||||||
if self.random and self._shuffled:
|
|
||||||
return self._shuffled[0]
|
|
||||||
|
|
||||||
if self.current_tl_track is None:
|
|
||||||
return tl_tracks[0]
|
|
||||||
|
|
||||||
if self.repeat:
|
|
||||||
return tl_tracks[(self.tracklist_position + 1) % len(tl_tracks)]
|
|
||||||
|
|
||||||
try:
|
|
||||||
return tl_tracks[self.tracklist_position + 1]
|
|
||||||
except IndexError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
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.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_previous(self):
|
|
||||||
if self.repeat or self.consume or self.random:
|
|
||||||
return self.current_tl_track
|
|
||||||
|
|
||||||
if self.tracklist_position in (None, 0):
|
|
||||||
return None
|
|
||||||
|
|
||||||
return self.core.tracklist.tl_tracks[self.tracklist_position - 1]
|
|
||||||
|
|
||||||
tl_track_at_previous = property(get_tl_track_at_previous)
|
|
||||||
"""
|
|
||||||
The track that will be played if calling :meth:`previous()`.
|
|
||||||
|
|
||||||
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_volume(self):
|
def get_volume(self):
|
||||||
if self.audio:
|
if self.audio:
|
||||||
return self.audio.get_volume().get()
|
return self.audio.get_volume().get()
|
||||||
@ -339,15 +157,15 @@ class PlaybackController(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
original_tl_track = self.current_tl_track
|
original_tl_track = self.current_tl_track
|
||||||
|
next_tl_track = self.core.tracklist.eot_track(original_tl_track)
|
||||||
|
|
||||||
if self.tl_track_at_eot:
|
if next_tl_track:
|
||||||
self._trigger_track_playback_ended()
|
self._trigger_track_playback_ended()
|
||||||
self.play(self.tl_track_at_eot)
|
self.play(next_tl_track)
|
||||||
else:
|
else:
|
||||||
self.stop(clear_current_track=True)
|
self.stop(clear_current_track=True)
|
||||||
|
|
||||||
if self.consume:
|
self.core.tracklist.mark_played(original_tl_track)
|
||||||
self.core.tracklist.remove(tlid=original_tl_track.tlid)
|
|
||||||
|
|
||||||
def on_tracklist_change(self):
|
def on_tracklist_change(self):
|
||||||
"""
|
"""
|
||||||
@ -355,12 +173,7 @@ class PlaybackController(object):
|
|||||||
|
|
||||||
Used by :class:`mopidy.core.TracklistController`.
|
Used by :class:`mopidy.core.TracklistController`.
|
||||||
"""
|
"""
|
||||||
self._first_shuffle = True
|
if self.current_tl_track not in self.core.tracklist.tl_tracks:
|
||||||
self._shuffled = []
|
|
||||||
|
|
||||||
if (not self.core.tracklist.tl_tracks or
|
|
||||||
self.current_tl_track not in
|
|
||||||
self.core.tracklist.tl_tracks):
|
|
||||||
self.stop(clear_current_track=True)
|
self.stop(clear_current_track=True)
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
@ -370,9 +183,10 @@ class PlaybackController(object):
|
|||||||
The current playback state will be kept. If it was playing, playing
|
The current playback state will be kept. If it was playing, playing
|
||||||
will continue. If it was paused, it will still be paused, etc.
|
will continue. If it was paused, it will still be paused, etc.
|
||||||
"""
|
"""
|
||||||
if self.tl_track_at_next:
|
tl_track = self.core.tracklist.next_track(self.current_tl_track)
|
||||||
|
if tl_track:
|
||||||
self._trigger_track_playback_ended()
|
self._trigger_track_playback_ended()
|
||||||
self.change_track(self.tl_track_at_next)
|
self.change_track(tl_track)
|
||||||
else:
|
else:
|
||||||
self.stop(clear_current_track=True)
|
self.stop(clear_current_track=True)
|
||||||
|
|
||||||
@ -395,37 +209,40 @@ class PlaybackController(object):
|
|||||||
:type on_error_step: int, -1 or 1
|
:type on_error_step: int, -1 or 1
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if tl_track is not None:
|
assert on_error_step in (-1, 1)
|
||||||
assert tl_track in self.core.tracklist.tl_tracks
|
|
||||||
elif tl_track is None:
|
if tl_track is None:
|
||||||
if self.state == PlaybackState.PAUSED:
|
if self.state == PlaybackState.PAUSED:
|
||||||
return self.resume()
|
return self.resume()
|
||||||
elif self.current_tl_track is not None:
|
|
||||||
tl_track = self.current_tl_track
|
|
||||||
elif self.current_tl_track is None and on_error_step == 1:
|
|
||||||
tl_track = self.tl_track_at_next
|
|
||||||
elif self.current_tl_track is None and on_error_step == -1:
|
|
||||||
tl_track = self.tl_track_at_previous
|
|
||||||
|
|
||||||
if tl_track is not None:
|
if self.current_tl_track is not None:
|
||||||
self.current_tl_track = tl_track
|
tl_track = self.current_tl_track
|
||||||
self.state = PlaybackState.PLAYING
|
else:
|
||||||
backend = self._get_backend()
|
|
||||||
if not backend or not backend.playback.play(tl_track.track).get():
|
|
||||||
logger.warning('Track is not playable: %s', tl_track.track.uri)
|
|
||||||
if self.random and self._shuffled:
|
|
||||||
self._shuffled.remove(tl_track)
|
|
||||||
if on_error_step == 1:
|
if on_error_step == 1:
|
||||||
# TODO: can cause an endless loop for single track repeat.
|
tl_track = self.core.tracklist.next_track(tl_track)
|
||||||
self.next()
|
|
||||||
elif on_error_step == -1:
|
elif on_error_step == -1:
|
||||||
self.previous()
|
tl_track = self.core.tracklist.previous_track(tl_track)
|
||||||
|
|
||||||
|
if tl_track is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.random and self.current_tl_track in self._shuffled:
|
assert tl_track in self.core.tracklist.tl_tracks
|
||||||
self._shuffled.remove(self.current_tl_track)
|
|
||||||
|
|
||||||
self._trigger_track_playback_started()
|
self.current_tl_track = tl_track
|
||||||
|
self.state = PlaybackState.PLAYING
|
||||||
|
backend = self._get_backend()
|
||||||
|
success = backend and backend.playback.play(tl_track.track).get()
|
||||||
|
|
||||||
|
if success:
|
||||||
|
self.core.tracklist.mark_playing(tl_track)
|
||||||
|
self._trigger_track_playback_started()
|
||||||
|
else:
|
||||||
|
self.core.tracklist.mark_unplayable(tl_track)
|
||||||
|
if on_error_step == 1:
|
||||||
|
# TODO: can cause an endless loop for single track repeat.
|
||||||
|
self.next()
|
||||||
|
elif on_error_step == -1:
|
||||||
|
self.previous()
|
||||||
|
|
||||||
def previous(self):
|
def previous(self):
|
||||||
"""
|
"""
|
||||||
@ -435,7 +252,9 @@ class PlaybackController(object):
|
|||||||
will continue. If it was paused, it will still be paused, etc.
|
will continue. If it was paused, it will still be paused, etc.
|
||||||
"""
|
"""
|
||||||
self._trigger_track_playback_ended()
|
self._trigger_track_playback_ended()
|
||||||
self.change_track(self.tl_track_at_previous, on_error_step=-1)
|
tl_track = self.current_tl_track
|
||||||
|
self.change_track(
|
||||||
|
self.core.tracklist.previous_track(tl_track), on_error_step=-1)
|
||||||
|
|
||||||
def resume(self):
|
def resume(self):
|
||||||
"""If paused, resume playing the current track."""
|
"""If paused, resume playing the current track."""
|
||||||
@ -531,10 +350,6 @@ class PlaybackController(object):
|
|||||||
'playback_state_changed',
|
'playback_state_changed',
|
||||||
old_state=old_state, new_state=new_state)
|
old_state=old_state, new_state=new_state)
|
||||||
|
|
||||||
def _trigger_options_changed(self):
|
|
||||||
logger.debug('Triggering options changed event')
|
|
||||||
listener.CoreListener.send('options_changed')
|
|
||||||
|
|
||||||
def _trigger_volume_changed(self, volume):
|
def _trigger_volume_changed(self, volume):
|
||||||
logger.debug('Triggering volume changed event')
|
logger.debug('Triggering volume changed event')
|
||||||
listener.CoreListener.send('volume_changed', volume=volume)
|
listener.CoreListener.send('volume_changed', volume=volume)
|
||||||
|
|||||||
@ -15,11 +15,16 @@ class TracklistController(object):
|
|||||||
pykka_traversable = True
|
pykka_traversable = True
|
||||||
|
|
||||||
def __init__(self, core):
|
def __init__(self, core):
|
||||||
self._core = core
|
self.core = core
|
||||||
self._next_tlid = 0
|
self._next_tlid = 0
|
||||||
self._tl_tracks = []
|
self._tl_tracks = []
|
||||||
self._version = 0
|
self._version = 0
|
||||||
|
|
||||||
|
self._shuffled = []
|
||||||
|
self._first_shuffle = True
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
def get_tl_tracks(self):
|
def get_tl_tracks(self):
|
||||||
return self._tl_tracks[:]
|
return self._tl_tracks[:]
|
||||||
|
|
||||||
@ -51,7 +56,7 @@ class TracklistController(object):
|
|||||||
|
|
||||||
def _increase_version(self):
|
def _increase_version(self):
|
||||||
self._version += 1
|
self._version += 1
|
||||||
self._core.playback.on_tracklist_change()
|
self.core.playback.on_tracklist_change()
|
||||||
self._trigger_tracklist_changed()
|
self._trigger_tracklist_changed()
|
||||||
|
|
||||||
version = property(get_version)
|
version = property(get_version)
|
||||||
@ -62,6 +67,190 @@ class TracklistController(object):
|
|||||||
Is not reset before Mopidy is restarted.
|
Is not reset before Mopidy is restarted.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def get_consume(self):
|
||||||
|
return getattr(self, '_consume', False)
|
||||||
|
|
||||||
|
def set_consume(self, value):
|
||||||
|
if self.get_consume() != value:
|
||||||
|
self._trigger_options_changed()
|
||||||
|
return setattr(self, '_consume', value)
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
def index(self, tl_track):
|
||||||
|
"""
|
||||||
|
The position of the given track in the tracklist.
|
||||||
|
|
||||||
|
:param tl_track: the track to find the index of
|
||||||
|
:type tl_track: :class:`mopidy.models.TlTrack`
|
||||||
|
:rtype: :class:`int` or :class:`None`
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self._tl_tracks.index(tl_track)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def eot_track(self, tl_track):
|
||||||
|
"""
|
||||||
|
The track that will be played after the given track.
|
||||||
|
|
||||||
|
Not necessarily the same track as :meth:`next_track`.
|
||||||
|
|
||||||
|
:param tl_track: the reference track
|
||||||
|
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||||
|
:rtype: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||||
|
"""
|
||||||
|
if not self.tl_tracks:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if self.random and not self._shuffled:
|
||||||
|
if self.repeat or self._first_shuffle:
|
||||||
|
logger.debug('Shuffling tracks')
|
||||||
|
self._shuffled = self.tl_tracks
|
||||||
|
random.shuffle(self._shuffled)
|
||||||
|
self._first_shuffle = False
|
||||||
|
|
||||||
|
if self.random and self._shuffled:
|
||||||
|
return self._shuffled[0]
|
||||||
|
|
||||||
|
if tl_track is None:
|
||||||
|
return self.tl_tracks[0]
|
||||||
|
|
||||||
|
position = self.index(tl_track)
|
||||||
|
|
||||||
|
if self.repeat and self.single:
|
||||||
|
return self.tl_tracks[position]
|
||||||
|
|
||||||
|
if self.repeat and not self.single:
|
||||||
|
return self.tl_tracks[(position + 1) % len(self.tl_tracks)]
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.tl_tracks[position + 1]
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def next_track(self, tl_track):
|
||||||
|
"""
|
||||||
|
The track that will be played if calling
|
||||||
|
:meth:`mopidy.core.PlaybackController.next()`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
:param tl_track: the reference track
|
||||||
|
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||||
|
:rtype: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.tl_tracks:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if self.random and not self._shuffled:
|
||||||
|
if self.repeat or self._first_shuffle:
|
||||||
|
logger.debug('Shuffling tracks')
|
||||||
|
self._shuffled = self.tl_tracks
|
||||||
|
random.shuffle(self._shuffled)
|
||||||
|
self._first_shuffle = False
|
||||||
|
|
||||||
|
if self.random and self._shuffled:
|
||||||
|
return self._shuffled[0]
|
||||||
|
|
||||||
|
if tl_track is None:
|
||||||
|
return self.tl_tracks[0]
|
||||||
|
|
||||||
|
position = self.index(tl_track)
|
||||||
|
|
||||||
|
if self.repeat:
|
||||||
|
return self.tl_tracks[(position + 1) % len(self.tl_tracks)]
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.tl_tracks[position + 1]
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def previous_track(self, tl_track):
|
||||||
|
"""
|
||||||
|
Returns the track that will be played if calling
|
||||||
|
:meth:`mopidy.core.PlaybackController.previous()`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
:param tl_track: the reference track
|
||||||
|
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||||
|
:rtype: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||||
|
"""
|
||||||
|
if self.repeat or self.consume or self.random:
|
||||||
|
return tl_track
|
||||||
|
|
||||||
|
position = self.index(tl_track)
|
||||||
|
|
||||||
|
if position in (None, 0):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.tl_tracks[position - 1]
|
||||||
|
|
||||||
def add(self, tracks=None, at_position=None, uri=None):
|
def add(self, tracks=None, at_position=None, uri=None):
|
||||||
"""
|
"""
|
||||||
Add the track or list of tracks to the tracklist.
|
Add the track or list of tracks to the tracklist.
|
||||||
@ -87,7 +276,7 @@ class TracklistController(object):
|
|||||||
'tracks or uri must be provided'
|
'tracks or uri must be provided'
|
||||||
|
|
||||||
if tracks is None and uri is not None:
|
if tracks is None and uri is not None:
|
||||||
tracks = self._core.library.lookup(uri)
|
tracks = self.core.library.lookup(uri)
|
||||||
|
|
||||||
tl_tracks = []
|
tl_tracks = []
|
||||||
|
|
||||||
@ -151,18 +340,6 @@ class TracklistController(object):
|
|||||||
lambda ct: getattr(ct.track, key) == value, matches)
|
lambda ct: getattr(ct.track, key) == value, matches)
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
def index(self, tl_track):
|
|
||||||
"""
|
|
||||||
Get index of the given :class:`mopidy.models.TlTrack` in the tracklist.
|
|
||||||
|
|
||||||
Raises :exc:`ValueError` if not found.
|
|
||||||
|
|
||||||
:param tl_track: track to find the index of
|
|
||||||
:type tl_track: :class:`mopidy.models.TlTrack`
|
|
||||||
:rtype: int
|
|
||||||
"""
|
|
||||||
return self._tl_tracks.index(tl_track)
|
|
||||||
|
|
||||||
def move(self, start, end, to_position):
|
def move(self, start, end, to_position):
|
||||||
"""
|
"""
|
||||||
Move the tracks in the slice ``[start:end]`` to ``to_position``.
|
Move the tracks in the slice ``[start:end]`` to ``to_position``.
|
||||||
@ -259,6 +436,31 @@ class TracklistController(object):
|
|||||||
"""
|
"""
|
||||||
return self._tl_tracks[start:end]
|
return self._tl_tracks[start:end]
|
||||||
|
|
||||||
|
def mark_playing(self, tl_track):
|
||||||
|
"""Private method used by :class:`mopidy.core.PlaybackController`."""
|
||||||
|
if self.random and tl_track in self._shuffled:
|
||||||
|
self._shuffled.remove(tl_track)
|
||||||
|
|
||||||
|
def mark_unplayable(self, tl_track):
|
||||||
|
"""Private method used by :class:`mopidy.core.PlaybackController`."""
|
||||||
|
logger.warning('Track is not playable: %s', tl_track.track.uri)
|
||||||
|
if self.random and tl_track in self._shuffled:
|
||||||
|
self._shuffled.remove(tl_track)
|
||||||
|
|
||||||
|
def mark_played(self, tl_track):
|
||||||
|
"""Private method used by :class:`mopidy.core.PlaybackController`."""
|
||||||
|
if not self.consume:
|
||||||
|
return False
|
||||||
|
self.remove(tlid=tl_track.tlid)
|
||||||
|
return True
|
||||||
|
|
||||||
def _trigger_tracklist_changed(self):
|
def _trigger_tracklist_changed(self):
|
||||||
|
self._first_shuffle = True
|
||||||
|
self._shuffled = []
|
||||||
|
|
||||||
logger.debug('Triggering event: tracklist_changed()')
|
logger.debug('Triggering event: tracklist_changed()')
|
||||||
listener.CoreListener.send('tracklist_changed')
|
listener.CoreListener.send('tracklist_changed')
|
||||||
|
|
||||||
|
def _trigger_options_changed(self):
|
||||||
|
logger.debug('Triggering options changed event')
|
||||||
|
listener.CoreListener.send('options_changed')
|
||||||
|
|||||||
@ -19,9 +19,9 @@ def consume(context, state):
|
|||||||
playlist.
|
playlist.
|
||||||
"""
|
"""
|
||||||
if int(state):
|
if int(state):
|
||||||
context.core.playback.consume = True
|
context.core.tracklist.consume = True
|
||||||
else:
|
else:
|
||||||
context.core.playback.consume = False
|
context.core.tracklist.consume = False
|
||||||
|
|
||||||
|
|
||||||
@handle_request(r'^crossfade "(?P<seconds>\d+)"$')
|
@handle_request(r'^crossfade "(?P<seconds>\d+)"$')
|
||||||
@ -263,9 +263,9 @@ def random(context, state):
|
|||||||
Sets random state to ``STATE``, ``STATE`` should be 0 or 1.
|
Sets random state to ``STATE``, ``STATE`` should be 0 or 1.
|
||||||
"""
|
"""
|
||||||
if int(state):
|
if int(state):
|
||||||
context.core.playback.random = True
|
context.core.tracklist.random = True
|
||||||
else:
|
else:
|
||||||
context.core.playback.random = False
|
context.core.tracklist.random = False
|
||||||
|
|
||||||
|
|
||||||
@handle_request(r'^repeat (?P<state>[01])$')
|
@handle_request(r'^repeat (?P<state>[01])$')
|
||||||
@ -279,9 +279,9 @@ def repeat(context, state):
|
|||||||
Sets repeat state to ``STATE``, ``STATE`` should be 0 or 1.
|
Sets repeat state to ``STATE``, ``STATE`` should be 0 or 1.
|
||||||
"""
|
"""
|
||||||
if int(state):
|
if int(state):
|
||||||
context.core.playback.repeat = True
|
context.core.tracklist.repeat = True
|
||||||
else:
|
else:
|
||||||
context.core.playback.repeat = False
|
context.core.tracklist.repeat = False
|
||||||
|
|
||||||
|
|
||||||
@handle_request(r'^replay_gain_mode "(?P<mode>(off|track|album))"$')
|
@handle_request(r'^replay_gain_mode "(?P<mode>(off|track|album))"$')
|
||||||
@ -329,7 +329,8 @@ def seek(context, songpos, seconds):
|
|||||||
|
|
||||||
- issues ``seek 1 120`` without quotes around the arguments.
|
- issues ``seek 1 120`` without quotes around the arguments.
|
||||||
"""
|
"""
|
||||||
if context.core.playback.tracklist_position.get() != int(songpos):
|
tl_track = context.core.playback.current_tl_track.get()
|
||||||
|
if context.core.tracklist.index(tl_track).get() != int(songpos):
|
||||||
playpos(context, songpos)
|
playpos(context, songpos)
|
||||||
context.core.playback.seek(int(seconds) * 1000).get()
|
context.core.playback.seek(int(seconds) * 1000).get()
|
||||||
|
|
||||||
@ -404,9 +405,9 @@ def single(context, state):
|
|||||||
song is repeated if the ``repeat`` mode is enabled.
|
song is repeated if the ``repeat`` mode is enabled.
|
||||||
"""
|
"""
|
||||||
if int(state):
|
if int(state):
|
||||||
context.core.playback.single = True
|
context.core.tracklist.single = True
|
||||||
else:
|
else:
|
||||||
context.core.playback.single = False
|
context.core.tracklist.single = False
|
||||||
|
|
||||||
|
|
||||||
@handle_request(r'^stop$')
|
@handle_request(r'^stop$')
|
||||||
|
|||||||
@ -36,10 +36,10 @@ def currentsong(context):
|
|||||||
Displays the song info of the current song (same song that is
|
Displays the song info of the current song (same song that is
|
||||||
identified in status).
|
identified in status).
|
||||||
"""
|
"""
|
||||||
current_tl_track = context.core.playback.current_tl_track.get()
|
tl_track = context.core.playback.current_tl_track.get()
|
||||||
if current_tl_track is not None:
|
if tl_track is not None:
|
||||||
position = context.core.playback.tracklist_position.get()
|
position = context.core.tracklist.index(tl_track).get()
|
||||||
return track_to_mpd_format(current_tl_track, position=position)
|
return track_to_mpd_format(tl_track, position=position)
|
||||||
|
|
||||||
|
|
||||||
@handle_request(r'^idle$')
|
@handle_request(r'^idle$')
|
||||||
@ -178,14 +178,15 @@ def status(context):
|
|||||||
'tracklist.length': context.core.tracklist.length,
|
'tracklist.length': context.core.tracklist.length,
|
||||||
'tracklist.version': context.core.tracklist.version,
|
'tracklist.version': context.core.tracklist.version,
|
||||||
'playback.volume': context.core.playback.volume,
|
'playback.volume': context.core.playback.volume,
|
||||||
'playback.consume': context.core.playback.consume,
|
'tracklist.consume': context.core.tracklist.consume,
|
||||||
'playback.random': context.core.playback.random,
|
'tracklist.random': context.core.tracklist.random,
|
||||||
'playback.repeat': context.core.playback.repeat,
|
'tracklist.repeat': context.core.tracklist.repeat,
|
||||||
'playback.single': context.core.playback.single,
|
'tracklist.single': context.core.tracklist.single,
|
||||||
'playback.state': context.core.playback.state,
|
'playback.state': context.core.playback.state,
|
||||||
'playback.current_tl_track': context.core.playback.current_tl_track,
|
'playback.current_tl_track': context.core.playback.current_tl_track,
|
||||||
'playback.tracklist_position': (
|
'tracklist.index': (
|
||||||
context.core.playback.tracklist_position),
|
context.core.tracklist.index(
|
||||||
|
context.core.playback.current_tl_track.get())),
|
||||||
'playback.time_position': context.core.playback.time_position,
|
'playback.time_position': context.core.playback.time_position,
|
||||||
}
|
}
|
||||||
pykka.get_all(futures.values())
|
pykka.get_all(futures.values())
|
||||||
@ -218,7 +219,7 @@ def _status_bitrate(futures):
|
|||||||
|
|
||||||
|
|
||||||
def _status_consume(futures):
|
def _status_consume(futures):
|
||||||
if futures['playback.consume'].get():
|
if futures['tracklist.consume'].get():
|
||||||
return 1
|
return 1
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
@ -233,15 +234,15 @@ def _status_playlist_version(futures):
|
|||||||
|
|
||||||
|
|
||||||
def _status_random(futures):
|
def _status_random(futures):
|
||||||
return int(futures['playback.random'].get())
|
return int(futures['tracklist.random'].get())
|
||||||
|
|
||||||
|
|
||||||
def _status_repeat(futures):
|
def _status_repeat(futures):
|
||||||
return int(futures['playback.repeat'].get())
|
return int(futures['tracklist.repeat'].get())
|
||||||
|
|
||||||
|
|
||||||
def _status_single(futures):
|
def _status_single(futures):
|
||||||
return int(futures['playback.single'].get())
|
return int(futures['tracklist.single'].get())
|
||||||
|
|
||||||
|
|
||||||
def _status_songid(futures):
|
def _status_songid(futures):
|
||||||
@ -253,7 +254,7 @@ def _status_songid(futures):
|
|||||||
|
|
||||||
|
|
||||||
def _status_songpos(futures):
|
def _status_songpos(futures):
|
||||||
return futures['playback.tracklist_position'].get()
|
return futures['tracklist.index'].get()
|
||||||
|
|
||||||
|
|
||||||
def _status_state(futures):
|
def _status_state(futures):
|
||||||
|
|||||||
@ -212,13 +212,15 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
def test_next(self):
|
def test_next(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
|
|
||||||
old_position = self.playback.tracklist_position
|
tl_track = self.playback.current_tl_track
|
||||||
old_uri = self.playback.current_track.uri
|
old_position = self.tracklist.index(tl_track)
|
||||||
|
old_uri = tl_track.track.uri
|
||||||
|
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
|
|
||||||
|
tl_track = self.playback.current_tl_track
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.playback.tracklist_position, old_position + 1)
|
self.tracklist.index(tl_track), old_position + 1)
|
||||||
self.assertNotEqual(self.playback.current_track.uri, old_uri)
|
self.assertNotEqual(self.playback.current_track.uri, old_uri)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
@ -238,7 +240,8 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
for i, track in enumerate(self.tracks):
|
for i, track in enumerate(self.tracks):
|
||||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||||
self.assertEqual(self.playback.current_track, track)
|
self.assertEqual(self.playback.current_track, track)
|
||||||
self.assertEqual(self.playback.tracklist_position, i)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.index(tl_track), i)
|
||||||
|
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
|
|
||||||
@ -274,55 +277,67 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_next_track_before_play(self):
|
def test_next_track_before_play(self):
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[0])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_next_track_during_play(self):
|
def test_next_track_during_play(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[1])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_next_track_after_previous(self):
|
def test_next_track_after_previous(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.playback.previous()
|
self.playback.previous()
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[1])
|
||||||
|
|
||||||
def test_next_track_empty_playlist(self):
|
def test_next_track_empty_playlist(self):
|
||||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.next_track(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_next_track_at_end_of_playlist(self):
|
def test_next_track_at_end_of_playlist(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
for _ in self.tracklist.tl_tracks[1:]:
|
for _ in self.tracklist.tl_tracks[1:]:
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.next_track(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_next_track_at_end_of_playlist_with_repeat(self):
|
def test_next_track_at_end_of_playlist_with_repeat(self):
|
||||||
self.playback.repeat = True
|
self.tracklist.repeat = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
for _ in self.tracks[1:]:
|
for _ in self.tracks[1:]:
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[0])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_next_track_with_random(self):
|
def test_next_track_with_random(self):
|
||||||
random.seed(1)
|
random.seed(1)
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[2])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_next_with_consume(self):
|
def test_next_with_consume(self):
|
||||||
self.playback.consume = True
|
self.tracklist.consume = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertIn(self.tracks[0], self.tracklist.tracks)
|
self.assertIn(self.tracks[0], self.tracklist.tracks)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_next_with_single_and_repeat(self):
|
def test_next_with_single_and_repeat(self):
|
||||||
self.playback.single = True
|
self.tracklist.single = True
|
||||||
self.playback.repeat = True
|
self.tracklist.repeat = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||||
@ -331,7 +346,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
def test_next_with_random(self):
|
def test_next_with_random(self):
|
||||||
# FIXME feels very fragile
|
# FIXME feels very fragile
|
||||||
random.seed(1)
|
random.seed(1)
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||||
@ -339,22 +354,28 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_next_track_with_random_after_append_playlist(self):
|
def test_next_track_with_random_after_append_playlist(self):
|
||||||
random.seed(1)
|
random.seed(1)
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.next_track(tl_track),
|
||||||
|
self.tl_tracks[2])
|
||||||
self.tracklist.add(self.tracks[:1])
|
self.tracklist.add(self.tracks[:1])
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[1])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_track(self):
|
def test_end_of_track(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
|
|
||||||
old_position = self.playback.tracklist_position
|
tl_track = self.playback.current_tl_track
|
||||||
old_uri = self.playback.current_track.uri
|
old_position = self.tracklist.index(tl_track)
|
||||||
|
old_uri = tl_track.track.uri
|
||||||
|
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
|
|
||||||
|
tl_track = self.playback.current_tl_track
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.playback.tracklist_position, old_position + 1)
|
self.tracklist.index(tl_track), old_position + 1)
|
||||||
self.assertNotEqual(self.playback.current_track.uri, old_uri)
|
self.assertNotEqual(self.playback.current_track.uri, old_uri)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
@ -374,7 +395,8 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
for i, track in enumerate(self.tracks):
|
for i, track in enumerate(self.tracks):
|
||||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||||
self.assertEqual(self.playback.current_track, track)
|
self.assertEqual(self.playback.current_track, track)
|
||||||
self.assertEqual(self.playback.tracklist_position, i)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.index(tl_track), i)
|
||||||
|
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
|
|
||||||
@ -410,47 +432,59 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_track_track_before_play(self):
|
def test_end_of_track_track_before_play(self):
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[0])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_track_track_during_play(self):
|
def test_end_of_track_track_during_play(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[1])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_track_track_after_previous(self):
|
def test_end_of_track_track_after_previous(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
self.playback.previous()
|
self.playback.previous()
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[1])
|
||||||
|
|
||||||
def test_end_of_track_track_empty_playlist(self):
|
def test_end_of_track_track_empty_playlist(self):
|
||||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.next_track(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_track_track_at_end_of_playlist(self):
|
def test_end_of_track_track_at_end_of_playlist(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
for _ in self.tracklist.tl_tracks[1:]:
|
for _ in self.tracklist.tl_tracks[1:]:
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.next_track(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_track_track_at_end_of_playlist_with_repeat(self):
|
def test_end_of_track_track_at_end_of_playlist_with_repeat(self):
|
||||||
self.playback.repeat = True
|
self.tracklist.repeat = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
for _ in self.tracks[1:]:
|
for _ in self.tracks[1:]:
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[0])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_track_track_with_random(self):
|
def test_end_of_track_track_with_random(self):
|
||||||
random.seed(1)
|
random.seed(1)
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[2])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_track_with_consume(self):
|
def test_end_of_track_with_consume(self):
|
||||||
self.playback.consume = True
|
self.tracklist.consume = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
self.assertNotIn(self.tracks[0], self.tracklist.tracks)
|
self.assertNotIn(self.tracks[0], self.tracklist.tracks)
|
||||||
@ -459,7 +493,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
def test_end_of_track_with_random(self):
|
def test_end_of_track_with_random(self):
|
||||||
# FIXME feels very fragile
|
# FIXME feels very fragile
|
||||||
random.seed(1)
|
random.seed(1)
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
self.assertEqual(self.playback.current_track, self.tracks[1])
|
self.assertEqual(self.playback.current_track, self.tracks[1])
|
||||||
@ -467,25 +501,33 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_track_track_with_random_after_append_playlist(self):
|
def test_end_of_track_track_with_random_after_append_playlist(self):
|
||||||
random.seed(1)
|
random.seed(1)
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[2])
|
||||||
self.tracklist.add(self.tracks[:1])
|
self.tracklist.add(self.tracks[:1])
|
||||||
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.next_track(tl_track), self.tl_tracks[1])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_previous_track_before_play(self):
|
def test_previous_track_before_play(self):
|
||||||
self.assertEqual(self.playback.tl_track_at_previous, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.previous_track(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_previous_track_after_play(self):
|
def test_previous_track_after_play(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.assertEqual(self.playback.tl_track_at_previous, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.previous_track(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_previous_track_after_next(self):
|
def test_previous_track_after_next(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertEqual(self.playback.tl_track_at_previous, self.tl_tracks[0])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.previous_track(tl_track), self.tl_tracks[0])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_previous_track_after_previous(self):
|
def test_previous_track_after_previous(self):
|
||||||
@ -493,27 +535,32 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
self.playback.next() # At track 1
|
self.playback.next() # At track 1
|
||||||
self.playback.next() # At track 2
|
self.playback.next() # At track 2
|
||||||
self.playback.previous() # At track 1
|
self.playback.previous() # At track 1
|
||||||
self.assertEqual(self.playback.tl_track_at_previous, self.tl_tracks[0])
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(
|
||||||
|
self.tracklist.previous_track(tl_track), self.tl_tracks[0])
|
||||||
|
|
||||||
def test_previous_track_empty_playlist(self):
|
def test_previous_track_empty_playlist(self):
|
||||||
self.assertEqual(self.playback.tl_track_at_previous, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.previous_track(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_previous_track_with_consume(self):
|
def test_previous_track_with_consume(self):
|
||||||
self.playback.consume = True
|
self.tracklist.consume = True
|
||||||
for _ in self.tracks:
|
for _ in self.tracks:
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
|
tl_track = self.playback.current_tl_track
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.playback.tl_track_at_previous,
|
self.tracklist.previous_track(tl_track),
|
||||||
self.playback.current_tl_track)
|
self.playback.current_tl_track)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_previous_track_with_random(self):
|
def test_previous_track_with_random(self):
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
for _ in self.tracks:
|
for _ in self.tracks:
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
|
tl_track = self.playback.current_tl_track
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.playback.tl_track_at_previous,
|
self.tracklist.previous_track(tl_track),
|
||||||
self.playback.current_tl_track)
|
self.playback.current_tl_track)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
@ -533,24 +580,28 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_initial_tracklist_position(self):
|
def test_initial_tracklist_position(self):
|
||||||
self.assertEqual(self.playback.tracklist_position, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.index(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_tracklist_position_during_play(self):
|
def test_tracklist_position_during_play(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.assertEqual(self.playback.tracklist_position, 0)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.index(tl_track), 0)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_tracklist_position_after_next(self):
|
def test_tracklist_position_after_next(self):
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertEqual(self.playback.tracklist_position, 1)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.index(tl_track), 1)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_tracklist_position_at_end_of_playlist(self):
|
def test_tracklist_position_at_end_of_playlist(self):
|
||||||
self.playback.play(self.tracklist.tl_tracks[-1])
|
self.playback.play(self.tracklist.tl_tracks[-1])
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
self.assertEqual(self.playback.tracklist_position, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.index(tl_track), None)
|
||||||
|
|
||||||
def test_on_tracklist_change_gets_called(self):
|
def test_on_tracklist_change_gets_called(self):
|
||||||
callback = self.playback.on_tracklist_change
|
callback = self.playback.on_tracklist_change
|
||||||
@ -808,13 +859,13 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_play_with_consume(self):
|
def test_play_with_consume(self):
|
||||||
self.playback.consume = True
|
self.tracklist.consume = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_playlist_is_empty_after_all_tracks_are_played_with_consume(self):
|
def test_playlist_is_empty_after_all_tracks_are_played_with_consume(self):
|
||||||
self.playback.consume = True
|
self.tracklist.consume = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
for _ in range(len(self.tracklist.tracks)):
|
for _ in range(len(self.tracklist.tracks)):
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
@ -823,14 +874,14 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_play_with_random(self):
|
def test_play_with_random(self):
|
||||||
random.seed(1)
|
random.seed(1)
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.assertEqual(self.playback.current_track, self.tracks[2])
|
self.assertEqual(self.playback.current_track, self.tracks[2])
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_previous_with_random(self):
|
def test_previous_with_random(self):
|
||||||
random.seed(1)
|
random.seed(1)
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
current_track = self.playback.current_track
|
current_track = self.playback.current_track
|
||||||
@ -845,8 +896,8 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_end_of_song_with_single_and_repeat_starts_same(self):
|
def test_end_of_song_with_single_and_repeat_starts_same(self):
|
||||||
self.playback.single = True
|
self.tracklist.single = True
|
||||||
self.playback.repeat = True
|
self.tracklist.repeat = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.playback.on_end_of_track()
|
self.playback.on_end_of_track()
|
||||||
self.assertEqual(self.playback.current_track, self.tracks[0])
|
self.assertEqual(self.playback.current_track, self.tracks[0])
|
||||||
@ -858,44 +909,47 @@ class LocalPlaybackProviderTest(unittest.TestCase):
|
|||||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||||
|
|
||||||
def test_repeat_off_by_default(self):
|
def test_repeat_off_by_default(self):
|
||||||
self.assertEqual(self.playback.repeat, False)
|
self.assertEqual(self.tracklist.repeat, False)
|
||||||
|
|
||||||
def test_random_off_by_default(self):
|
def test_random_off_by_default(self):
|
||||||
self.assertEqual(self.playback.random, False)
|
self.assertEqual(self.tracklist.random, False)
|
||||||
|
|
||||||
def test_consume_off_by_default(self):
|
def test_consume_off_by_default(self):
|
||||||
self.assertEqual(self.playback.consume, False)
|
self.assertEqual(self.tracklist.consume, False)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_random_until_end_of_playlist(self):
|
def test_random_until_end_of_playlist(self):
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
for _ in self.tracks[1:]:
|
for _ in self.tracks[1:]:
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertEqual(self.playback.tl_track_at_next, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertEqual(self.tracklist.next_track(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_random_until_end_of_playlist_and_play_from_start(self):
|
def test_random_until_end_of_playlist_and_play_from_start(self):
|
||||||
self.playback.repeat = True
|
self.tracklist.repeat = True
|
||||||
for _ in self.tracks:
|
for _ in self.tracks:
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertNotEqual(self.playback.tl_track_at_next, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertNotEqual(self.tracklist.next_track(tl_track), None)
|
||||||
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
self.assertEqual(self.playback.state, PlaybackState.PLAYING)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_random_until_end_of_playlist_with_repeat(self):
|
def test_random_until_end_of_playlist_with_repeat(self):
|
||||||
self.playback.repeat = True
|
self.tracklist.repeat = True
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
for _ in self.tracks:
|
for _ in self.tracks:
|
||||||
self.playback.next()
|
self.playback.next()
|
||||||
self.assertNotEqual(self.playback.tl_track_at_next, None)
|
tl_track = self.playback.current_tl_track
|
||||||
|
self.assertNotEqual(self.tracklist.next_track(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_played_track_during_random_not_played_again(self):
|
def test_played_track_during_random_not_played_again(self):
|
||||||
self.playback.random = True
|
self.tracklist.random = True
|
||||||
self.playback.play()
|
self.playback.play()
|
||||||
played = []
|
played = []
|
||||||
for _ in self.tracks:
|
for _ in self.tracks:
|
||||||
|
|||||||
@ -171,13 +171,13 @@ class LocalTracklistProviderTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_index_returns_index_of_track(self):
|
def test_index_returns_index_of_track(self):
|
||||||
tl_tracks = self.controller.add(self.tracks)
|
tl_tracks = self.controller.add(self.tracks)
|
||||||
self.assertEquals(0, self.controller.index(tl_tracks[0]))
|
self.assertEqual(0, self.controller.index(tl_tracks[0]))
|
||||||
self.assertEquals(1, self.controller.index(tl_tracks[1]))
|
self.assertEqual(1, self.controller.index(tl_tracks[1]))
|
||||||
self.assertEquals(2, self.controller.index(tl_tracks[2]))
|
self.assertEqual(2, self.controller.index(tl_tracks[2]))
|
||||||
|
|
||||||
def test_index_raises_value_error_if_item_not_found(self):
|
def test_index_returns_none_if_item_not_found(self):
|
||||||
test = lambda: self.controller.index(TlTrack(0, Track()))
|
tl_track = TlTrack(0, Track())
|
||||||
self.assertRaises(ValueError, test)
|
self.assertEqual(self.controller.index(tl_track), None)
|
||||||
|
|
||||||
@populate_tracklist
|
@populate_tracklist
|
||||||
def test_move_single(self):
|
def test_move_single(self):
|
||||||
|
|||||||
@ -16,22 +16,22 @@ STOPPED = PlaybackState.STOPPED
|
|||||||
class PlaybackOptionsHandlerTest(protocol.BaseTestCase):
|
class PlaybackOptionsHandlerTest(protocol.BaseTestCase):
|
||||||
def test_consume_off(self):
|
def test_consume_off(self):
|
||||||
self.sendRequest('consume "0"')
|
self.sendRequest('consume "0"')
|
||||||
self.assertFalse(self.core.playback.consume.get())
|
self.assertFalse(self.core.tracklist.consume.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_consume_off_without_quotes(self):
|
def test_consume_off_without_quotes(self):
|
||||||
self.sendRequest('consume 0')
|
self.sendRequest('consume 0')
|
||||||
self.assertFalse(self.core.playback.consume.get())
|
self.assertFalse(self.core.tracklist.consume.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_consume_on(self):
|
def test_consume_on(self):
|
||||||
self.sendRequest('consume "1"')
|
self.sendRequest('consume "1"')
|
||||||
self.assertTrue(self.core.playback.consume.get())
|
self.assertTrue(self.core.tracklist.consume.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_consume_on_without_quotes(self):
|
def test_consume_on_without_quotes(self):
|
||||||
self.sendRequest('consume 1')
|
self.sendRequest('consume 1')
|
||||||
self.assertTrue(self.core.playback.consume.get())
|
self.assertTrue(self.core.tracklist.consume.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_crossfade(self):
|
def test_crossfade(self):
|
||||||
@ -40,42 +40,42 @@ class PlaybackOptionsHandlerTest(protocol.BaseTestCase):
|
|||||||
|
|
||||||
def test_random_off(self):
|
def test_random_off(self):
|
||||||
self.sendRequest('random "0"')
|
self.sendRequest('random "0"')
|
||||||
self.assertFalse(self.core.playback.random.get())
|
self.assertFalse(self.core.tracklist.random.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_random_off_without_quotes(self):
|
def test_random_off_without_quotes(self):
|
||||||
self.sendRequest('random 0')
|
self.sendRequest('random 0')
|
||||||
self.assertFalse(self.core.playback.random.get())
|
self.assertFalse(self.core.tracklist.random.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_random_on(self):
|
def test_random_on(self):
|
||||||
self.sendRequest('random "1"')
|
self.sendRequest('random "1"')
|
||||||
self.assertTrue(self.core.playback.random.get())
|
self.assertTrue(self.core.tracklist.random.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_random_on_without_quotes(self):
|
def test_random_on_without_quotes(self):
|
||||||
self.sendRequest('random 1')
|
self.sendRequest('random 1')
|
||||||
self.assertTrue(self.core.playback.random.get())
|
self.assertTrue(self.core.tracklist.random.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_repeat_off(self):
|
def test_repeat_off(self):
|
||||||
self.sendRequest('repeat "0"')
|
self.sendRequest('repeat "0"')
|
||||||
self.assertFalse(self.core.playback.repeat.get())
|
self.assertFalse(self.core.tracklist.repeat.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_repeat_off_without_quotes(self):
|
def test_repeat_off_without_quotes(self):
|
||||||
self.sendRequest('repeat 0')
|
self.sendRequest('repeat 0')
|
||||||
self.assertFalse(self.core.playback.repeat.get())
|
self.assertFalse(self.core.tracklist.repeat.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_repeat_on(self):
|
def test_repeat_on(self):
|
||||||
self.sendRequest('repeat "1"')
|
self.sendRequest('repeat "1"')
|
||||||
self.assertTrue(self.core.playback.repeat.get())
|
self.assertTrue(self.core.tracklist.repeat.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_repeat_on_without_quotes(self):
|
def test_repeat_on_without_quotes(self):
|
||||||
self.sendRequest('repeat 1')
|
self.sendRequest('repeat 1')
|
||||||
self.assertTrue(self.core.playback.repeat.get())
|
self.assertTrue(self.core.tracklist.repeat.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_setvol_below_min(self):
|
def test_setvol_below_min(self):
|
||||||
@ -115,22 +115,22 @@ class PlaybackOptionsHandlerTest(protocol.BaseTestCase):
|
|||||||
|
|
||||||
def test_single_off(self):
|
def test_single_off(self):
|
||||||
self.sendRequest('single "0"')
|
self.sendRequest('single "0"')
|
||||||
self.assertFalse(self.core.playback.single.get())
|
self.assertFalse(self.core.tracklist.single.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_single_off_without_quotes(self):
|
def test_single_off_without_quotes(self):
|
||||||
self.sendRequest('single 0')
|
self.sendRequest('single 0')
|
||||||
self.assertFalse(self.core.playback.single.get())
|
self.assertFalse(self.core.tracklist.single.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_single_on(self):
|
def test_single_on(self):
|
||||||
self.sendRequest('single "1"')
|
self.sendRequest('single "1"')
|
||||||
self.assertTrue(self.core.playback.single.get())
|
self.assertTrue(self.core.tracklist.single.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_single_on_without_quotes(self):
|
def test_single_on_without_quotes(self):
|
||||||
self.sendRequest('single 1')
|
self.sendRequest('single 1')
|
||||||
self.assertTrue(self.core.playback.single.get())
|
self.assertTrue(self.core.tracklist.single.get())
|
||||||
self.assertInResponse('OK')
|
self.assertInResponse('OK')
|
||||||
|
|
||||||
def test_replay_gain_mode_off(self):
|
def test_replay_gain_mode_off(self):
|
||||||
|
|||||||
@ -64,7 +64,7 @@ class StatusHandlerTest(unittest.TestCase):
|
|||||||
self.assertEqual(int(result['repeat']), 0)
|
self.assertEqual(int(result['repeat']), 0)
|
||||||
|
|
||||||
def test_status_method_contains_repeat_is_1(self):
|
def test_status_method_contains_repeat_is_1(self):
|
||||||
self.core.playback.repeat = 1
|
self.core.tracklist.repeat = 1
|
||||||
result = dict(status.status(self.context))
|
result = dict(status.status(self.context))
|
||||||
self.assertIn('repeat', result)
|
self.assertIn('repeat', result)
|
||||||
self.assertEqual(int(result['repeat']), 1)
|
self.assertEqual(int(result['repeat']), 1)
|
||||||
@ -75,7 +75,7 @@ class StatusHandlerTest(unittest.TestCase):
|
|||||||
self.assertEqual(int(result['random']), 0)
|
self.assertEqual(int(result['random']), 0)
|
||||||
|
|
||||||
def test_status_method_contains_random_is_1(self):
|
def test_status_method_contains_random_is_1(self):
|
||||||
self.core.playback.random = 1
|
self.core.tracklist.random = 1
|
||||||
result = dict(status.status(self.context))
|
result = dict(status.status(self.context))
|
||||||
self.assertIn('random', result)
|
self.assertIn('random', result)
|
||||||
self.assertEqual(int(result['random']), 1)
|
self.assertEqual(int(result['random']), 1)
|
||||||
@ -91,7 +91,7 @@ class StatusHandlerTest(unittest.TestCase):
|
|||||||
self.assertEqual(int(result['consume']), 0)
|
self.assertEqual(int(result['consume']), 0)
|
||||||
|
|
||||||
def test_status_method_contains_consume_is_1(self):
|
def test_status_method_contains_consume_is_1(self):
|
||||||
self.core.playback.consume = 1
|
self.core.tracklist.consume = 1
|
||||||
result = dict(status.status(self.context))
|
result = dict(status.status(self.context))
|
||||||
self.assertIn('consume', result)
|
self.assertIn('consume', result)
|
||||||
self.assertEqual(int(result['consume']), 1)
|
self.assertEqual(int(result['consume']), 1)
|
||||||
|
|||||||
@ -34,6 +34,9 @@ class Calculator(object):
|
|||||||
def _secret(self):
|
def _secret(self):
|
||||||
return 'Grand Unified Theory'
|
return 'Grand Unified Theory'
|
||||||
|
|
||||||
|
def fail(self):
|
||||||
|
raise ValueError('What did you expect?')
|
||||||
|
|
||||||
|
|
||||||
class JsonRpcTestBase(unittest.TestCase):
|
class JsonRpcTestBase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -266,12 +269,12 @@ class JsonRpcSingleNotificationTest(JsonRpcTestBase):
|
|||||||
|
|
||||||
class JsonRpcBatchTest(JsonRpcTestBase):
|
class JsonRpcBatchTest(JsonRpcTestBase):
|
||||||
def test_batch_of_only_commands_returns_all(self):
|
def test_batch_of_only_commands_returns_all(self):
|
||||||
self.core.playback.set_random(True).get()
|
self.core.tracklist.set_random(True).get()
|
||||||
|
|
||||||
request = [
|
request = [
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_repeat', 'id': 1},
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_repeat', 'id': 1},
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_random', 'id': 2},
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_random', 'id': 2},
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_single', 'id': 3},
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_single', 'id': 3},
|
||||||
]
|
]
|
||||||
response = self.jrw.handle_data(request)
|
response = self.jrw.handle_data(request)
|
||||||
|
|
||||||
@ -283,12 +286,12 @@ class JsonRpcBatchTest(JsonRpcTestBase):
|
|||||||
self.assertEqual(response[3]['result'], False)
|
self.assertEqual(response[3]['result'], False)
|
||||||
|
|
||||||
def test_batch_of_commands_and_notifications_returns_some(self):
|
def test_batch_of_commands_and_notifications_returns_some(self):
|
||||||
self.core.playback.set_random(True).get()
|
self.core.tracklist.set_random(True).get()
|
||||||
|
|
||||||
request = [
|
request = [
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_repeat'},
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_repeat'},
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_random', 'id': 2},
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_random', 'id': 2},
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_single', 'id': 3},
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_single', 'id': 3},
|
||||||
]
|
]
|
||||||
response = self.jrw.handle_data(request)
|
response = self.jrw.handle_data(request)
|
||||||
|
|
||||||
@ -300,12 +303,12 @@ class JsonRpcBatchTest(JsonRpcTestBase):
|
|||||||
self.assertEqual(response[3]['result'], False)
|
self.assertEqual(response[3]['result'], False)
|
||||||
|
|
||||||
def test_batch_of_only_notifications_returns_nothing(self):
|
def test_batch_of_only_notifications_returns_nothing(self):
|
||||||
self.core.playback.set_random(True).get()
|
self.core.tracklist.set_random(True).get()
|
||||||
|
|
||||||
request = [
|
request = [
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_repeat'},
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_repeat'},
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_random'},
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_random'},
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_single'},
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_single'},
|
||||||
]
|
]
|
||||||
response = self.jrw.handle_data(request)
|
response = self.jrw.handle_data(request)
|
||||||
|
|
||||||
@ -316,8 +319,8 @@ class JsonRpcSingleCommandErrorTest(JsonRpcTestBase):
|
|||||||
def test_application_error_response(self):
|
def test_application_error_response(self):
|
||||||
request = {
|
request = {
|
||||||
'jsonrpc': '2.0',
|
'jsonrpc': '2.0',
|
||||||
'method': 'core.tracklist.index',
|
'method': 'calc.fail',
|
||||||
'params': ['bogus'],
|
'params': [],
|
||||||
'id': 1,
|
'id': 1,
|
||||||
}
|
}
|
||||||
response = self.jrw.handle_data(request)
|
response = self.jrw.handle_data(request)
|
||||||
@ -330,7 +333,7 @@ class JsonRpcSingleCommandErrorTest(JsonRpcTestBase):
|
|||||||
|
|
||||||
data = error['data']
|
data = error['data']
|
||||||
self.assertEqual(data['type'], 'ValueError')
|
self.assertEqual(data['type'], 'ValueError')
|
||||||
self.assertIn('not in list', data['message'])
|
self.assertIn('What did you expect?', data['message'])
|
||||||
self.assertIn('traceback', data)
|
self.assertIn('traceback', data)
|
||||||
self.assertIn('Traceback (most recent call last):', data['traceback'])
|
self.assertIn('Traceback (most recent call last):', data['traceback'])
|
||||||
|
|
||||||
@ -522,10 +525,10 @@ class JsonRpcBatchErrorTest(JsonRpcTestBase):
|
|||||||
{'jsonrpc': '2.0', 'method': 'core.playback.set_volume',
|
{'jsonrpc': '2.0', 'method': 'core.playback.set_volume',
|
||||||
'params': [47], 'id': '1'},
|
'params': [47], 'id': '1'},
|
||||||
# Notification
|
# Notification
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.set_consume',
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.set_consume',
|
||||||
'params': [True]},
|
'params': [True]},
|
||||||
# Call with positional params
|
# Call with positional params
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.set_repeat',
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.set_repeat',
|
||||||
'params': [False], 'id': '2'},
|
'params': [False], 'id': '2'},
|
||||||
# Invalid request
|
# Invalid request
|
||||||
{'foo': 'boo'},
|
{'foo': 'boo'},
|
||||||
@ -533,7 +536,7 @@ class JsonRpcBatchErrorTest(JsonRpcTestBase):
|
|||||||
{'jsonrpc': '2.0', 'method': 'foo.get',
|
{'jsonrpc': '2.0', 'method': 'foo.get',
|
||||||
'params': {'name': 'myself'}, 'id': '5'},
|
'params': {'name': 'myself'}, 'id': '5'},
|
||||||
# Call without params
|
# Call without params
|
||||||
{'jsonrpc': '2.0', 'method': 'core.playback.get_random',
|
{'jsonrpc': '2.0', 'method': 'core.tracklist.get_random',
|
||||||
'id': '9'},
|
'id': '9'},
|
||||||
]
|
]
|
||||||
response = self.jrw.handle_data(request)
|
response = self.jrw.handle_data(request)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user