diff --git a/code/modules/tgui_panel/tgui_panel.dm b/code/modules/tgui_panel/tgui_panel.dm index 2d270ac72aa..24b1e865ffc 100644 --- a/code/modules/tgui_panel/tgui_panel.dm +++ b/code/modules/tgui_panel/tgui_panel.dm @@ -12,6 +12,8 @@ var/datum/tgui_window/window var/broken = FALSE var/initialized_at + /// Each client notifies on protected playback, so this prevents spamming admins. + var/static/admins_warned = FALSE /datum/tgui_panel/New(client/client, id) src.client = client @@ -86,9 +88,18 @@ ), )) return TRUE + if(type == "audio/setAdminMusicVolume") client.admin_music_volume = payload["volume"] return TRUE + + if(type == "audio/protected") + if(!admins_warned) + message_admins(span_notice("Audio returned a protected playback error, likely due to being copyrighted.")) + admins_warned = TRUE + addtimer(VARSET_CALLBACK(src, admins_warned, FALSE), 10 SECONDS) + return TRUE + if(type == "telemetry") analyze_telemetry(payload) return TRUE diff --git a/html/changelogs/bubber_archive/2025-05.yml b/html/changelogs/bubber_archive/2025-05.yml index 1f1f4b47c68..90695000b5a 100644 --- a/html/changelogs/bubber_archive/2025-05.yml +++ b/html/changelogs/bubber_archive/2025-05.yml @@ -31,3 +31,8 @@ aggresively. - bugfix: Fixes feed detailed description lying about the time it takes to start feeding +2025-05-05: + LT3: + - bugfix: Everyone's chat log no longer bluescreens due to Admins' bad taste in + music. Their fatal attempts to make you listen to king gizzard the lizard + wizard will now fail silently diff --git a/tgui/packages/tgui-panel/audio/player.ts b/tgui/packages/tgui-panel/audio/player.ts index d75fab4487e..9f5f35987d2 100644 --- a/tgui/packages/tgui-panel/audio/player.ts +++ b/tgui/packages/tgui-panel/audio/player.ts @@ -14,6 +14,15 @@ type AudioOptions = { end?: number; }; +function isProtectedError(error: ErrorEvent): boolean { + return ( + typeof error === 'object' && + error !== null && + 'isTrusted' in error && + error.isTrusted + ); +} + export class AudioPlayer { element: HTMLAudioElement | null; options: AudioOptions; @@ -40,7 +49,13 @@ export class AudioPlayer { this.options = options; - const audio = (this.element = new Audio(url)); + const audio = new Audio(url); + if (!audio) { + logger.log('failed to create audio element'); + return; + } + this.element = audio; + audio.volume = this.volume; audio.playbackRate = this.options.pitch || 1; @@ -52,7 +67,11 @@ export class AudioPlayer { }); audio.addEventListener('error', (error) => { - logger.log('playback error', error); + if (isProtectedError(error)) { + Byond.sendMessage('audio/protected'); + } + logger.log('playback error:', JSON.stringify(error)); + this.stop(); }); if (this.options.end) { @@ -67,7 +86,10 @@ export class AudioPlayer { }); } - audio.play(); + audio.play()?.catch(() => { + // no error is passed here, it's sent to the event listener + logger.log('playback failed'); + }); this.onPlaySubscribers.forEach((subscriber) => subscriber()); } @@ -78,7 +100,7 @@ export class AudioPlayer { logger.log('stopping'); this.element.pause(); - this.element = null; + this.destroy(); this.onStopSubscribers.forEach((subscriber) => subscriber()); }