From b4325c67db852c4cf961616be33e478a51a6f6b4 Mon Sep 17 00:00:00 2001 From: Johannes Knutsen Date: Sat, 14 Aug 2010 16:44:39 +0200 Subject: [PATCH 1/5] rename next_cp_track to cp_track_at_next to differ between next and end of track events --- mopidy/backends/base/playback.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mopidy/backends/base/playback.py b/mopidy/backends/base/playback.py index 2cf15629..08050523 100644 --- a/mopidy/backends/base/playback.py +++ b/mopidy/backends/base/playback.py @@ -100,16 +100,16 @@ class BasePlaybackController(object): """ The next track in the playlist. - A :class:`mopidy.models.Track` extracted from :attr:`next_cp_track` for + A :class:`mopidy.models.Track` extracted from :attr:`cp_track_at_next` for convenience. """ - next_cp_track = self.next_cp_track - if next_cp_track is None: + cp_track_at_next = self.cp_track_at_next + if cp_track_at_next is None: return None - return next_cp_track[1] + return cp_track_at_next[1] @property - def next_cp_track(self): + def cp_track_at_next(self): """ The next track in the playlist. @@ -247,7 +247,7 @@ class BasePlaybackController(object): Typically called by :class:`mopidy.process.CoreProcess` after a message from a library thread is received. """ - if self.next_cp_track is not None: + if self.cp_track_at_next is not None: self.next() else: self.stop() @@ -278,10 +278,10 @@ class BasePlaybackController(object): if self.state == self.STOPPED: return - elif self.next_cp_track is not None and self._next(self.next_track): - self.current_cp_track = self.next_cp_track + elif self.cp_track_at_next is not None and self._next(self.next_track): + self.current_cp_track = self.cp_track_at_next self.state = self.PLAYING - elif self.next_cp_track is None: + elif self.cp_track_at_next is None: self.stop() self.current_cp_track = None @@ -315,7 +315,7 @@ class BasePlaybackController(object): if cp_track is not None: assert cp_track in self.backend.current_playlist.cp_tracks elif not self.current_cp_track: - cp_track = self.next_cp_track + cp_track = self.cp_track_at_next if self.state == self.PAUSED and cp_track is None: self.resume() From a3b03b63565649a739b83731504f93f4fb4ec2ba Mon Sep 17 00:00:00 2001 From: Johannes Knutsen Date: Sat, 14 Aug 2010 17:16:28 +0200 Subject: [PATCH 2/5] copied cp_track_at_next to cp_track_at_end_of_track and let the end of track implement it's own next functionality instead of calling next --- mopidy/backends/base/playback.py | 49 ++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/mopidy/backends/base/playback.py b/mopidy/backends/base/playback.py index 08050523..e1167022 100644 --- a/mopidy/backends/base/playback.py +++ b/mopidy/backends/base/playback.py @@ -108,10 +108,46 @@ class BasePlaybackController(object): return None return cp_track_at_next[1] + @property + def cp_track_at_eot(self): + """ + The next track in the playlist which should be played when + we get an end of track event, such as when a track is finished playing. + + A two-tuple of (CPID integer, :class:`mopidy.models.Track`). + """ + cp_tracks = self.backend.current_playlist.cp_tracks + + if not cp_tracks: + return None + + if self.random and not self._shuffled: + if self.repeat or self._first_shuffle: + logger.debug('Shuffling tracks') + self._shuffled = cp_tracks + random.shuffle(self._shuffled) + self._first_shuffle = False + + if self._shuffled: + return self._shuffled[0] + + if self.current_cp_track is None: + return cp_tracks[0] + + if self.repeat: + return cp_tracks[ + (self.current_playlist_position + 1) % len(cp_tracks)] + + try: + return cp_tracks[self.current_playlist_position + 1] + except IndexError: + return None + @property def cp_track_at_next(self): """ - The next track in the playlist. + The next track in the playlist which should be played when we get a + event, such as a user clicking the next button. A two-tuple of (CPID integer, :class:`mopidy.models.Track`). @@ -247,8 +283,15 @@ class BasePlaybackController(object): Typically called by :class:`mopidy.process.CoreProcess` after a message from a library thread is received. """ - if self.cp_track_at_next is not None: - self.next() + if self.cp_track_at_eot is not None: + original_cp_track = self.current_cp_track + self.current_cp_track = self.cp_track_at_eot + + if self.consume: + self.backend.current_playlist.remove(cpid=original_cp_track[0]) + + if self.random and self.current_cp_track in self._shuffled: + self._shuffled.remove(self.current_cp_track) else: self.stop() self.current_cp_track = None From 622b96ef27b10a0dcb4b325e78a46a2ce67079b8 Mon Sep 17 00:00:00 2001 From: Johannes Knutsen Date: Sat, 14 Aug 2010 17:19:03 +0200 Subject: [PATCH 3/5] support single mode at end of track --- mopidy/backends/base/playback.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mopidy/backends/base/playback.py b/mopidy/backends/base/playback.py index e1167022..13f1e73b 100644 --- a/mopidy/backends/base/playback.py +++ b/mopidy/backends/base/playback.py @@ -134,6 +134,10 @@ class BasePlaybackController(object): if self.current_cp_track is None: return cp_tracks[0] + if self.repeat and self.single: + return cp_tracks[ + (self.current_playlist_position) % len(cp_tracks)] + if self.repeat: return cp_tracks[ (self.current_playlist_position + 1) % len(cp_tracks)] From 200cc3dd2a5b9e25ed0b10c155a98c0ca9cfa898 Mon Sep 17 00:00:00 2001 From: Johannes Knutsen Date: Sat, 14 Aug 2010 18:00:18 +0200 Subject: [PATCH 4/5] end of track to call play on the next track --- mopidy/backends/base/playback.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mopidy/backends/base/playback.py b/mopidy/backends/base/playback.py index 13f1e73b..08b7932d 100644 --- a/mopidy/backends/base/playback.py +++ b/mopidy/backends/base/playback.py @@ -287,9 +287,11 @@ class BasePlaybackController(object): Typically called by :class:`mopidy.process.CoreProcess` after a message from a library thread is received. """ - if self.cp_track_at_eot is not None: + next_cp_track = self.cp_track_at_eot + if next_cp_track is not None and self._next(next_cp_track[1]): original_cp_track = self.current_cp_track - self.current_cp_track = self.cp_track_at_eot + self.current_cp_track = next_cp_track + self.state = self.PLAYING if self.consume: self.backend.current_playlist.remove(cpid=original_cp_track[0]) From ca52dd6363b39a235a5baabdc53cfa6916b630cb Mon Sep 17 00:00:00 2001 From: Johannes Knutsen Date: Sat, 14 Aug 2010 18:30:22 +0200 Subject: [PATCH 5/5] added tests for next track in single and repeat mode --- tests/backends/base.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/backends/base.py b/tests/backends/base.py index 64ca7797..19f28ba5 100644 --- a/tests/backends/base.py +++ b/tests/backends/base.py @@ -811,6 +811,14 @@ class BasePlaybackControllerTest(object): self.playback.next() self.assert_(self.tracks[0] not in self.backend.current_playlist.tracks) + @populate_playlist + def test_next_with_single_and_repeat(self): + self.playback.single = True + self.playback.repeat = True + self.playback.play() + self.playback.next() + self.assertEqual(self.playback.current_track, self.tracks[1]) + @populate_playlist def test_playlist_is_empty_after_all_tracks_are_played_with_consume(self): self.playback.consume = True @@ -851,6 +859,14 @@ class BasePlaybackControllerTest(object): self.playback.end_of_track_callback() self.assertEqual(self.playback.current_track, self.tracks[1]) + @populate_playlist + def test_end_of_song_with_single_and_repeat_starts_same(self): + self.playback.single = True + self.playback.repeat = True + self.playback.play() + self.playback.end_of_track_callback() + self.assertEqual(self.playback.current_track, self.tracks[0]) + @populate_playlist def test_end_of_playlist_stops(self): self.playback.play(self.current_playlist.cp_tracks[-1])