fix: advance playback after track completion
This commit is contained in:
4 files changed
+83
-11
No files matched your search
@@ -90,8 +90,13 @@ class MusicPlayer:
|
||||
return discord.utils.get(self._bot.voice_clients, guild__id=guild_id)
|
||||
|
||||
def is_active(self, guild_id: int) -> bool:
|
||||
state = self.state(guild_id)
|
||||
voice_client = self.voice_client(guild_id)
|
||||
return bool(voice_client and (voice_client.is_playing() or voice_client.is_paused()))
|
||||
return bool(
|
||||
state.current
|
||||
and voice_client
|
||||
and (voice_client.is_playing() or voice_client.is_paused())
|
||||
)
|
||||
|
||||
def clear(self, guild_id: int) -> None:
|
||||
self._playback_tokens[guild_id] = self._playback_tokens.get(guild_id, 0) + 1
|
||||
@@ -130,6 +135,14 @@ class MusicPlayer:
|
||||
self._interrupted_playbacks.add(guild_id)
|
||||
voice_client.stop()
|
||||
|
||||
def _stop_stale_voice(self, guild_id: int, voice_client: discord.VoiceClient) -> None:
|
||||
"""Stop a voice client that still reports the source from a finished queue."""
|
||||
if not (voice_client.is_playing() or voice_client.is_paused()):
|
||||
return
|
||||
self._playback_tokens[guild_id] = self._playback_tokens.get(guild_id, 0) + 1
|
||||
self._interrupted_playbacks.discard(guild_id)
|
||||
voice_client.stop()
|
||||
|
||||
async def restore_queue(self, guild_id: int) -> int:
|
||||
if not self._queues:
|
||||
return 0
|
||||
@@ -281,7 +294,18 @@ class MusicPlayer:
|
||||
|
||||
async def _restart_playback(self, guild_id: int) -> None:
|
||||
voice_client = self.voice_client(guild_id)
|
||||
if voice_client and (voice_client.is_playing() or voice_client.is_paused()):
|
||||
voice_is_active = bool(
|
||||
voice_client and (voice_client.is_playing() or voice_client.is_paused())
|
||||
)
|
||||
if voice_is_active and not self.is_active(guild_id):
|
||||
# Po końcu kolejki discord.py może jeszcze przez chwilę trzymać stare źródło.
|
||||
# Unieważniamy jego callback i uruchamiamy wybrany utwór bez czekania na niego.
|
||||
self._playback_tokens[guild_id] = self._playback_tokens.get(guild_id, 0) + 1
|
||||
self._interrupted_playbacks.discard(guild_id)
|
||||
voice_client.stop()
|
||||
await self.play_next(guild_id, advance=False)
|
||||
return
|
||||
if self.is_active(guild_id):
|
||||
self._interrupted_playbacks.add(guild_id)
|
||||
voice_client.stop() # `after` wywoła play_next, flaga przeskoku zablokuje advance
|
||||
else:
|
||||
@@ -325,6 +349,7 @@ class MusicPlayer:
|
||||
if not state.queue:
|
||||
state.index = -1
|
||||
state.current = None
|
||||
self._stop_stale_voice(guild_id, voice_client)
|
||||
await self.trigger_state_change()
|
||||
return
|
||||
|
||||
@@ -337,6 +362,7 @@ class MusicPlayer:
|
||||
if next_index >= len(state.queue):
|
||||
state.index = next_index
|
||||
state.current = None
|
||||
self._stop_stale_voice(guild_id, voice_client)
|
||||
await self.trigger_state_change()
|
||||
await self.notify(guild_id, "Kolejka zakończona.")
|
||||
return
|
||||
@@ -405,15 +431,30 @@ class MusicPlayer:
|
||||
return index + 1
|
||||
|
||||
def _schedule_next(self, guild_id: int, error: Exception | None, playback_token: int) -> None:
|
||||
if self._playback_tokens.get(guild_id) != playback_token:
|
||||
logger.info(
|
||||
"Zakończono odtwarzanie na serwerze %s (token=%s, error=%s)",
|
||||
guild_id,
|
||||
playback_token,
|
||||
error,
|
||||
)
|
||||
if self._playback_tokens.get(guild_id, 0) != playback_token:
|
||||
logger.info("Pomijam nieaktualny callback odtwarzania na serwerze %s", guild_id)
|
||||
return
|
||||
consume_current = guild_id not in self._interrupted_playbacks
|
||||
self._interrupted_playbacks.discard(guild_id)
|
||||
if error:
|
||||
logger.error("Odtwarzanie zakończone błędem na serwerze %s: %s", guild_id, error)
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
future = asyncio.run_coroutine_threadsafe(
|
||||
self.play_next(guild_id, advance=True, consume_current=consume_current), self._bot.loop
|
||||
)
|
||||
future.add_done_callback(self._log_playback_failure)
|
||||
|
||||
@staticmethod
|
||||
def _log_playback_failure(future: asyncio.Future) -> None:
|
||||
try:
|
||||
future.result()
|
||||
except Exception:
|
||||
logger.exception("Nie udało się przejść do kolejnego utworu")
|
||||
|
||||
def _record_history(self, guild_id: int, track: YTDLSource) -> None:
|
||||
if not track.title:
|
||||
|
||||
@@ -65,8 +65,8 @@ class StatusService:
|
||||
notification_channel = self._player.notification_channel(guild.id)
|
||||
|
||||
return {
|
||||
"playing": bool(voice_client and voice_client.is_playing()),
|
||||
"paused": bool(voice_client and voice_client.is_paused()),
|
||||
"playing": bool(self._player.is_active(guild.id) and voice_client.is_playing()),
|
||||
"paused": bool(self._player.is_active(guild.id) and voice_client.is_paused()),
|
||||
"loop_single": state.loop_mode.loops_track,
|
||||
"loop_playlist": state.loop_mode.loops_queue,
|
||||
"volume": int(state.volume * 100),
|
||||
|
||||
Reference in new issue
Block a user