Add slice() method to CurrentPlaylistController to reduce copying of the playlist

This commit is contained in:
Stein Magnus Jodal 2011-12-28 02:38:07 +01:00
parent 4f8fbac44c
commit b0698d2e0a
5 changed files with 36 additions and 8 deletions

View File

@ -21,10 +21,11 @@ v0.7.0 (in development)
- The MPD command ``playlistinfo`` is now faster, thanks to John Bäckstrand. - The MPD command ``playlistinfo`` is now faster, thanks to John Bäckstrand.
- Added the method - Added the method
:meth:`mopidy.backends.base.CurrentPlaylistController.length()` and :meth:`mopidy.backends.base.CurrentPlaylistController.length()`,
:meth:`mopidy.backends.base.CurrentPlaylistController.index()` to reduce the :meth:`mopidy.backends.base.CurrentPlaylistController.index()`, and
:meth:`mopidy.backends.base.CurrentPlaylistController.slice()` to reduce the
need for copying the entire current playlist from one thread to another. need for copying the entire current playlist from one thread to another.
Thanks to John Bäckstrand. Thanks to John Bäckstrand for pinpointing the issue.
v0.6.0 (2011-10-09) v0.6.0 (2011-10-09)

View File

@ -223,6 +223,19 @@ class CurrentPlaylistController(object):
self._cp_tracks = before + shuffled + after self._cp_tracks = before + shuffled + after
self.version += 1 self.version += 1
def slice(self, start, end):
"""
Returns a slice of the current playlist, limited by the given
start and end positions.
:param start: position of first track to include in slice
:type start: int
:param end: position after last track to include in slice
:type end: int
:rtype: two-tuple of (CPID integer, :class:`mopidy.models.Track`)
"""
return [copy(cp_track) for cp_track in self._cp_tracks[start:end]]
def _trigger_playlist_changed(self): def _trigger_playlist_changed(self):
logger.debug(u'Triggering playlist changed event') logger.debug(u'Triggering playlist changed event')
BackendListener.send('playlist_changed') BackendListener.send('playlist_changed')

View File

@ -76,7 +76,7 @@ def delete_range(context, start, end=None):
end = int(end) end = int(end)
else: else:
end = context.backend.current_playlist.length.get() end = context.backend.current_playlist.length.get()
cp_tracks = context.backend.current_playlist.cp_tracks.get()[start:end] cp_tracks = context.backend.current_playlist.slice(start, end).get()
if not cp_tracks: if not cp_tracks:
raise MpdArgError(u'Bad song index', command=u'delete') raise MpdArgError(u'Bad song index', command=u'delete')
for (cpid, _) in cp_tracks: for (cpid, _) in cp_tracks:
@ -87,7 +87,8 @@ def delete_songpos(context, songpos):
"""See :meth:`delete_range`""" """See :meth:`delete_range`"""
try: try:
songpos = int(songpos) songpos = int(songpos)
(cpid, _) = context.backend.current_playlist.cp_tracks.get()[songpos] (cpid, _) = context.backend.current_playlist.slice(
songpos, songpos + 1).get()[0]
context.backend.current_playlist.remove(cpid=cpid) context.backend.current_playlist.remove(cpid=cpid)
except IndexError: except IndexError:
raise MpdArgError(u'Bad song index', command=u'delete') raise MpdArgError(u'Bad song index', command=u'delete')

View File

@ -178,7 +178,8 @@ def playpos(context, songpos):
if songpos == -1: if songpos == -1:
return _play_minus_one(context) return _play_minus_one(context)
try: try:
cp_track = context.backend.current_playlist.cp_tracks.get()[songpos] cp_track = context.backend.current_playlist.slice(
songpos, songpos + 1).get()[0]
return context.backend.playback.play(cp_track).get() return context.backend.playback.play(cp_track).get()
except IndexError: except IndexError:
raise MpdArgError(u'Bad song index', command=u'play') raise MpdArgError(u'Bad song index', command=u'play')
@ -191,8 +192,8 @@ def _play_minus_one(context):
elif context.backend.playback.current_cp_track.get() is not None: elif context.backend.playback.current_cp_track.get() is not None:
cp_track = context.backend.playback.current_cp_track.get() cp_track = context.backend.playback.current_cp_track.get()
return context.backend.playback.play(cp_track).get() return context.backend.playback.play(cp_track).get()
elif context.backend.current_playlist.cp_tracks.get(): elif context.backend.current_playlist.slice(0, 1).get():
cp_track = context.backend.current_playlist.cp_tracks.get()[0] cp_track = context.backend.current_playlist.slice(0, 1).get()[0]
return context.backend.playback.play(cp_track).get() return context.backend.playback.play(cp_track).get()
else: else:
return # Fail silently return # Fail silently

View File

@ -260,6 +260,18 @@ class CurrentPlaylistControllerTest(object):
self.assertEqual(self.tracks[0], shuffled_tracks[0]) self.assertEqual(self.tracks[0], shuffled_tracks[0])
self.assertEqual(set(self.tracks), set(shuffled_tracks)) self.assertEqual(set(self.tracks), set(shuffled_tracks))
@populate_playlist
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
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_appending_nothing(self):
version = self.controller.version version = self.controller.version
self.controller.append([]) self.controller.append([])