Merge pull request #1136 from adamcik/feature/tlid-in-core-tracklist-calls
Support / use TLID in core tracklist calls
This commit is contained in:
commit
0dfa3df39b
@ -200,19 +200,52 @@ class TracklistController(object):
|
||||
|
||||
# Methods
|
||||
|
||||
def index(self, tl_track):
|
||||
def index(self, tl_track=None, tlid=None):
|
||||
"""
|
||||
The position of the given track in the tracklist.
|
||||
|
||||
If neither *tl_track* or *tlid* is given we return the index of
|
||||
the currently playing track.
|
||||
|
||||
:param tl_track: the track to find the index of
|
||||
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||
:param tlid: TLID of the track to find the index of
|
||||
:type tlid: :class:`int` or :class:`None`
|
||||
:rtype: :class:`int` or :class:`None`
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
Added the *tlid* parameter
|
||||
"""
|
||||
tl_track is None or validation.check_instance(tl_track, TlTrack)
|
||||
try:
|
||||
return self._tl_tracks.index(tl_track)
|
||||
except ValueError:
|
||||
return None
|
||||
tlid is None or validation.check_integer(tlid, min=0)
|
||||
|
||||
if tl_track is None and tlid is None:
|
||||
tl_track = self.core.playback.get_current_tl_track()
|
||||
|
||||
if tl_track is not None:
|
||||
try:
|
||||
return self._tl_tracks.index(tl_track)
|
||||
except ValueError:
|
||||
pass
|
||||
elif tlid is not None:
|
||||
for i, tl_track in enumerate(self._tl_tracks):
|
||||
if tl_track.tlid == tlid:
|
||||
return i
|
||||
return None
|
||||
|
||||
def get_eot_tlid(self):
|
||||
"""
|
||||
The TLID of the track that will be played after the given track.
|
||||
|
||||
Not necessarily the same TLID as returned by :meth:`get_next_tlid`.
|
||||
|
||||
:rtype: :class:`int` or :class:`None`
|
||||
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
|
||||
current_tl_track = self.core.playback.get_current_tl_track()
|
||||
return getattr(self.eot_track(current_tl_track), 'tlid', None)
|
||||
|
||||
def eot_track(self, tl_track):
|
||||
"""
|
||||
@ -224,6 +257,7 @@ class TracklistController(object):
|
||||
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||
:rtype: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||
"""
|
||||
deprecation.warn('core.tracklist.eot_track', pending=True)
|
||||
tl_track is None or validation.check_instance(tl_track, TlTrack)
|
||||
if self.get_single() and self.get_repeat():
|
||||
return tl_track
|
||||
@ -235,6 +269,23 @@ class TracklistController(object):
|
||||
# shared.
|
||||
return self.next_track(tl_track)
|
||||
|
||||
def get_next_tlid(self):
|
||||
"""
|
||||
The tlid of the track that will be played if calling
|
||||
:meth:`mopidy.core.PlaybackController.next()`.
|
||||
|
||||
For normal playback this is the next track in the tracklist. If repeat
|
||||
is enabled the next track can loop around the tracklist. When random is
|
||||
enabled this should be a random track, all tracks should be played once
|
||||
before the tracklist repeats.
|
||||
|
||||
:rtype: :class:`int` or :class:`None`
|
||||
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
current_tl_track = self.core.playback.get_current_tl_track()
|
||||
return getattr(self.next_track(current_tl_track), 'tlid', None)
|
||||
|
||||
def next_track(self, tl_track):
|
||||
"""
|
||||
The track that will be played if calling
|
||||
@ -249,35 +300,51 @@ class TracklistController(object):
|
||||
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||
:rtype: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||
"""
|
||||
deprecation.warn('core.tracklist.next_track', pending=True)
|
||||
tl_track is None or validation.check_instance(tl_track, TlTrack)
|
||||
|
||||
if not self.get_tl_tracks():
|
||||
if not self._tl_tracks:
|
||||
return None
|
||||
|
||||
if self.get_random() and not self._shuffled:
|
||||
if self.get_repeat() or not tl_track:
|
||||
logger.debug('Shuffling tracks')
|
||||
self._shuffled = self.get_tl_tracks()
|
||||
self._shuffled = self._tl_tracks[:]
|
||||
random.shuffle(self._shuffled)
|
||||
|
||||
if self.get_random():
|
||||
try:
|
||||
if self._shuffled:
|
||||
return self._shuffled[0]
|
||||
except IndexError:
|
||||
return None
|
||||
return None
|
||||
|
||||
if tl_track is None:
|
||||
return self.get_tl_tracks()[0]
|
||||
next_index = 0
|
||||
else:
|
||||
next_index = self.index(tl_track) + 1
|
||||
|
||||
next_index = self.index(tl_track) + 1
|
||||
if self.get_repeat():
|
||||
next_index %= len(self.get_tl_tracks())
|
||||
|
||||
try:
|
||||
return self.get_tl_tracks()[next_index]
|
||||
except IndexError:
|
||||
next_index %= len(self._tl_tracks)
|
||||
elif next_index >= len(self._tl_tracks):
|
||||
return None
|
||||
|
||||
return self._tl_tracks[next_index]
|
||||
|
||||
def get_previous_tlid(self):
|
||||
"""
|
||||
Returns the TLID of the track that will be played if calling
|
||||
:meth:`mopidy.core.PlaybackController.previous()`.
|
||||
|
||||
For normal playback this is the previous track in the tracklist. If
|
||||
random and/or consume is enabled it should return the current track
|
||||
instead.
|
||||
|
||||
:rtype: :class:`int` or :class:`None`
|
||||
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
current_tl_track = self.core.playback.get_current_tl_track()
|
||||
return getattr(self.previous_track(current_tl_track), 'tlid', None)
|
||||
|
||||
def previous_track(self, tl_track):
|
||||
"""
|
||||
Returns the track that will be played if calling
|
||||
@ -291,6 +358,7 @@ class TracklistController(object):
|
||||
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||
:rtype: :class:`mopidy.models.TlTrack` or :class:`None`
|
||||
"""
|
||||
deprecation.warn('core.tracklist.previous_track', pending=True)
|
||||
tl_track is None or validation.check_instance(tl_track, TlTrack)
|
||||
|
||||
if self.get_repeat() or self.get_consume() or self.get_random():
|
||||
@ -301,7 +369,9 @@ class TracklistController(object):
|
||||
if position in (None, 0):
|
||||
return None
|
||||
|
||||
return self.get_tl_tracks()[position - 1]
|
||||
# Since we know we are not at zero we have to be somewhere in the range
|
||||
# 1 - len(tracks) Thus 'position - 1' will always be within the list.
|
||||
return self._tl_tracks[position - 1]
|
||||
|
||||
def add(self, tracks=None, at_position=None, uri=None, uris=None):
|
||||
"""
|
||||
@ -554,7 +624,7 @@ class TracklistController(object):
|
||||
|
||||
def _trigger_tracklist_changed(self):
|
||||
if self.get_random():
|
||||
self._shuffled = self.get_tl_tracks()
|
||||
self._shuffled = self._tl_tracks[:]
|
||||
random.shuffle(self._shuffled)
|
||||
else:
|
||||
self._shuffled = []
|
||||
|
||||
@ -45,13 +45,27 @@ _MESSAGES = {
|
||||
'core.tracklist.remove:kwargs_criteria':
|
||||
'tracklist.remove() with "kwargs" as criteria is deprecated',
|
||||
|
||||
'core.tracklist.eot_track':
|
||||
'tracklist.eot_track() is pending deprecation, use '
|
||||
'tracklist.get_eot_tlid()',
|
||||
'core.tracklist.next_track':
|
||||
'tracklist.next_track() is pending deprecation, use '
|
||||
'tracklist.get_next_tlid()',
|
||||
'core.tracklist.previous_track':
|
||||
'tracklist.previous_track() is pending deprecation, use '
|
||||
'tracklist.get_previous_tlid()',
|
||||
|
||||
'models.immutable.copy':
|
||||
'ImmutableObject.copy() is deprecated, use ImmutableObject.replace()',
|
||||
}
|
||||
|
||||
|
||||
def warn(msg_id):
|
||||
warnings.warn(_MESSAGES.get(msg_id, msg_id), DeprecationWarning)
|
||||
def warn(msg_id, pending=False):
|
||||
if pending:
|
||||
category = PendingDeprecationWarning
|
||||
else:
|
||||
category = DeprecationWarning
|
||||
warnings.warn(_MESSAGES.get(msg_id, msg_id), category)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
||||
@ -5,7 +5,7 @@ import unittest
|
||||
import mock
|
||||
|
||||
from mopidy import backend, core
|
||||
from mopidy.models import Track
|
||||
from mopidy.models import TlTrack, Track
|
||||
from mopidy.utils import deprecation
|
||||
|
||||
|
||||
@ -102,3 +102,66 @@ class TracklistTest(unittest.TestCase):
|
||||
self.core.tracklist.filter({'uri': 'a'})
|
||||
|
||||
# TODO Extract tracklist tests from the local backend tests
|
||||
|
||||
|
||||
class TracklistIndexTest(unittest.TestCase):
|
||||
|
||||
def setUp(self): # noqa: N802
|
||||
self.tracks = [
|
||||
Track(uri='dummy1:a', name='foo'),
|
||||
Track(uri='dummy1:b', name='foo'),
|
||||
Track(uri='dummy1:c', name='bar'),
|
||||
]
|
||||
|
||||
def lookup(uris):
|
||||
return {u: [t for t in self.tracks if t.uri == u] for u in uris}
|
||||
|
||||
self.core = core.Core(mixer=None, backends=[])
|
||||
self.core.library = mock.Mock(spec=core.LibraryController)
|
||||
self.core.library.lookup.side_effect = lookup
|
||||
|
||||
self.core.playback = mock.Mock(spec=core.PlaybackController)
|
||||
|
||||
self.tl_tracks = self.core.tracklist.add(uris=[
|
||||
t.uri for t in self.tracks])
|
||||
|
||||
def test_index_returns_index_of_track(self):
|
||||
self.assertEqual(0, self.core.tracklist.index(self.tl_tracks[0]))
|
||||
self.assertEqual(1, self.core.tracklist.index(self.tl_tracks[1]))
|
||||
self.assertEqual(2, self.core.tracklist.index(self.tl_tracks[2]))
|
||||
|
||||
def test_index_returns_none_if_item_not_found(self):
|
||||
tl_track = TlTrack(0, Track())
|
||||
self.assertEqual(self.core.tracklist.index(tl_track), None)
|
||||
|
||||
def test_index_returns_none_if_called_with_none(self):
|
||||
self.assertEqual(self.core.tracklist.index(None), None)
|
||||
|
||||
def test_index_errors_out_for_invalid_tltrack(self):
|
||||
with self.assertRaises(ValueError):
|
||||
self.core.tracklist.index('abc')
|
||||
|
||||
def test_index_return_index_when_called_with_tlids(self):
|
||||
tl_tracks = self.tl_tracks
|
||||
self.assertEqual(0, self.core.tracklist.index(tlid=tl_tracks[0].tlid))
|
||||
self.assertEqual(1, self.core.tracklist.index(tlid=tl_tracks[1].tlid))
|
||||
self.assertEqual(2, self.core.tracklist.index(tlid=tl_tracks[2].tlid))
|
||||
|
||||
def test_index_returns_none_if_tlid_not_found(self):
|
||||
self.assertEqual(self.core.tracklist.index(tlid=123), None)
|
||||
|
||||
def test_index_returns_none_if_called_with_tlid_none(self):
|
||||
self.assertEqual(self.core.tracklist.index(tlid=None), None)
|
||||
|
||||
def test_index_errors_out_for_invalid_tlid(self):
|
||||
with self.assertRaises(ValueError):
|
||||
self.core.tracklist.index(tlid=-1)
|
||||
|
||||
def test_index_without_args_returns_current_tl_track_index(self):
|
||||
self.core.playback.get_current_tl_track.side_effect = [
|
||||
None, self.tl_tracks[0], self.tl_tracks[1], self.tl_tracks[2]]
|
||||
|
||||
self.assertEqual(None, self.core.tracklist.index())
|
||||
self.assertEqual(0, self.core.tracklist.index())
|
||||
self.assertEqual(1, self.core.tracklist.index())
|
||||
self.assertEqual(2, self.core.tracklist.index())
|
||||
|
||||
@ -8,7 +8,7 @@ import pykka
|
||||
from mopidy import core
|
||||
from mopidy.core import PlaybackState
|
||||
from mopidy.local import actor
|
||||
from mopidy.models import Playlist, TlTrack, Track
|
||||
from mopidy.models import Playlist, Track
|
||||
from mopidy.utils import deprecation
|
||||
|
||||
from tests import dummy_audio, path_to_data_dir
|
||||
@ -176,16 +176,6 @@ class LocalTracklistProviderTest(unittest.TestCase):
|
||||
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.add(self.tracks)
|
||||
self.assertEqual(0, self.controller.index(tl_tracks[0]))
|
||||
self.assertEqual(1, self.controller.index(tl_tracks[1]))
|
||||
self.assertEqual(2, self.controller.index(tl_tracks[2]))
|
||||
|
||||
def test_index_returns_none_if_item_not_found(self):
|
||||
tl_track = TlTrack(0, Track())
|
||||
self.assertEqual(self.controller.index(tl_track), None)
|
||||
|
||||
@populate_tracklist
|
||||
def test_move_single(self):
|
||||
self.controller.move(0, 0, 2)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user