diff --git a/tests/test_music_player.py b/tests/test_music_player.py index 541b710..5b31e76 100644 --- a/tests/test_music_player.py +++ b/tests/test_music_player.py @@ -25,15 +25,36 @@ class MusicPlayerQueueTest(unittest.IsolatedAsyncioTestCase): self.assertIs(voice_client.playing, track) self.assertEqual(player.state(GUILD).index, 0) - async def test_single_play_budget_drops_track_from_queue(self): + async def test_last_play_remains_pointed_to_until_playback_finishes(self): player, _ = build_player(default_plays=1) track = FakeTrack("a") await player.enqueue(GUILD, track) state = player.state(GUILD) + self.assertEqual(state.queue, [track]) + self.assertEqual(state.index, 0) + self.assertIs(state.current, track) + + await player.play_next(GUILD, advance=True) + self.assertEqual(state.queue, []) self.assertEqual(state.index, -1) - self.assertIs(state.current, track) + self.assertIsNone(state.current) + + async def test_last_play_of_recreated_track_is_removed_after_callback(self): + player, _, state = await self._queue_abc(default_plays=2) + state.loop_mode = LoopMode.TRACK + + await player.play_next(GUILD, advance=True) + self.assertEqual([track.id for track in state.queue], ["a", "b", "c"]) + self.assertEqual(state.index, 0) + self.assertEqual(state.current.plays_left, 0) + + await player.play_next(GUILD, advance=True) + + self.assertEqual([track.id for track in state.queue], ["b", "c"]) + self.assertEqual(state.index, 0) + self.assertEqual(state.current.id, "b") async def test_enqueue_while_playing_only_appends(self): _, _, state = await self._queue_abc() diff --git a/zyrarbot/music/music_player.py b/zyrarbot/music/music_player.py index bff824c..7e61f6d 100644 --- a/zyrarbot/music/music_player.py +++ b/zyrarbot/music/music_player.py @@ -189,12 +189,30 @@ class MusicPlayer: if state.consume_jump(): advance = False + # Utwór z ostatnim odtworzeniem musi pozostać w kolejce do chwili + # zakończenia źródła. W przeciwnym razie panel dostaje `current`, ale + # `queue_index` wskazuje pozycję spoza kolejki podczas faktycznego + # odtwarzania ostatniej kopii. + retired_index: int | None = None + if advance and state.current is not None and 0 <= state.index < len(state.queue): + current = state.queue[state.index] + if current is state.current and self.plays_left(current) <= 0: + retired_index = state.index + state.queue.pop(state.index) + state.index -= 1 + if not state.queue: state.index = -1 state.current = None + await self.trigger_state_change() return - next_index = self._next_index(state, advance=advance) + if retired_index is not None: + next_index = retired_index + if state.loop_mode.loops_queue: + next_index %= len(state.queue) + else: + next_index = self._next_index(state, advance=advance) if next_index >= len(state.queue): state.index = next_index state.current = None @@ -220,13 +238,12 @@ class MusicPlayer: track.plays_left = self.plays_left(track) - 1 track.volume = state.volume + state.queue[next_index] = track if track.plays_left <= 0: - state.queue.pop(next_index) - state.index = next_index - 1 + # Usunięcie nastąpi dopiero po callbacku discord.py, po tym jak + # ostatnie odtworzenie rzeczywiście się zakończy. if state.loop_mode.loops_track: state.loop_mode = LoopMode.OFF - else: - state.queue[next_index] = track state.current = track self._record_history(guild_id, track)