Add track-identity check to prevent stale radio event handlers from corrupting state

This commit is contained in:
esef committed 2026-07-05 19:02:07 +03:00
1 parent 19b51f16aa
commit a9359c3f85
1 file changed
+15
+15
View File
@@ -202,6 +202,12 @@ class Music(commands.Cog):
self._locks[guild_id] = asyncio.Lock() self._locks[guild_id] = asyncio.Lock()
return self._locks[guild_id] return self._locks[guild_id]
def _is_current_track(self, st: GuildState, event_track: wavelink.Playable | None) -> bool:
"""True if event_track is the same track currently stored in guild state."""
if st.current is None or event_track is None:
return False
return event_track.identifier == st.current.identifier
# ── channel status ──────────────────────────────────────────────────────── # ── channel status ────────────────────────────────────────────────────────
async def _set_channel_status(self, player: wavelink.Player, track: wavelink.Playable | None = None, *, title: str | None = None): async def _set_channel_status(self, player: wavelink.Player, track: wavelink.Playable | None = None, *, title: str | None = None):
"""Set (or clear) the voice channel status. Pass *title* for radio; otherwise uses track.title.""" """Set (or clear) the voice channel status. Pass *title* for radio; otherwise uses track.title."""
@@ -271,6 +277,11 @@ class Music(commands.Cog):
st = self.state(player.guild.id) st = self.state(player.guild.id)
# Ignore stale events for a track that is no longer current
# (e.g. track_end for an old radio stream arriving after a switch)
if not self._is_current_track(st, payload.track):
return
if st.loop == LOOP_TRACK and st.current: if st.loop == LOOP_TRACK and st.current:
await player.play(st.current) await player.play(st.current)
await self._set_channel_status(player, st.current) await self._set_channel_status(player, st.current)
@@ -306,6 +317,8 @@ class Music(commands.Cog):
return return
st = self.state(player.guild.id) st = self.state(player.guild.id)
if st.mode == MODE_RADIO: if st.mode == MODE_RADIO:
if not self._is_current_track(st, payload.track):
return
log.warning(f"Radio stream exception in guild {player.guild.id}: {payload.exception}") log.warning(f"Radio stream exception in guild {player.guild.id}: {payload.exception}")
await self._notify_radio_drop(player.guild.id) await self._notify_radio_drop(player.guild.id)
st.mode = None st.mode = None
@@ -318,6 +331,8 @@ class Music(commands.Cog):
return return
st = self.state(player.guild.id) st = self.state(player.guild.id)
if st.mode == MODE_RADIO: if st.mode == MODE_RADIO:
if not self._is_current_track(st, payload.track):
return
log.warning(f"Radio stream stuck in guild {player.guild.id}, threshold {payload.threshold_ms}ms") log.warning(f"Radio stream stuck in guild {player.guild.id}, threshold {payload.threshold_ms}ms")
await self._notify_radio_drop(player.guild.id) await self._notify_radio_drop(player.guild.id)
st.mode = None st.mode = None