fix(voice): restore voice after screen share — pass pre-release status to callbacks

The restore callback previously read getVoiceStatus() AFTER disconnectGuild
had already cleared it, so it never knew which guild/channel to reconnect.
Now release/restore receive the status captured BEFORE the audio connection
is released, so reconnect actually happens after the GoLive stream ends.
This commit is contained in:
asepharyana
2026-08-03 09:07:57 +07:00
parent 55d28dc928
commit 9fd4ded9c8
2 changed files with 12 additions and 9 deletions
@@ -186,19 +186,18 @@ export class MediaHandler {
this.getVoiceStatus, this.getVoiceStatus,
// releaseVoice — disconnect the @discordjs/voice connection so the // releaseVoice — disconnect the @discordjs/voice connection so the
// dank074 Streamer can take over (Discord: one voice session/user). // dank074 Streamer can take over (Discord: one voice session/user).
async () => { async (status) => {
const vc = this.voiceControllerAccessor?.(); const vc = this.voiceControllerAccessor?.();
const guildId = vc?.getStatus().activeGuildId ?? null; const guildId = status.activeGuildId ?? null;
if (vc && guildId) { if (vc && guildId) {
await vc.disconnectGuild(guildId); await vc.disconnectGuild(guildId);
} }
}, },
// restoreVoice — reconnect the @discordjs audio connection after // restoreVoice — reconnect the @discordjs audio connection after
// the stream ends so mic/listen keep working. // the stream ends so mic/listen keep working.
async () => { async (status) => {
const vc = this.voiceControllerAccessor?.(); const vc = this.voiceControllerAccessor?.();
const status = vc?.getStatus(); if (vc && status.activeGuildId && status.activeChannelId) {
if (vc && status?.activeGuildId && status.activeChannelId) {
await vc.connect(status.activeGuildId, status.activeChannelId); await vc.connect(status.activeGuildId, status.activeChannelId);
} }
}, },
@@ -41,9 +41,13 @@ export class ScreenShareController {
/** Disconnect the @discordjs/voice connection so the Streamer can take /** Disconnect the @discordjs/voice connection so the Streamer can take
* over the voice channel (Discord allows only ONE voice session per user * over the voice channel (Discord allows only ONE voice session per user
* — two connections collide and the Streamer never gets VOICE_SERVER_UPDATE). */ * — two connections collide and the Streamer never gets VOICE_SERVER_UPDATE). */
private readonly releaseVoice: () => void | Promise<void>, private readonly releaseVoice: (
status: ScreenShareVoiceStatus,
) => void | Promise<void>,
/** Reconnect the @discordjs/voice connection after the stream ends. */ /** Reconnect the @discordjs/voice connection after the stream ends. */
private readonly restoreVoice: () => void | Promise<void>, private readonly restoreVoice: (
status: ScreenShareVoiceStatus,
) => void | Promise<void>,
) {} ) {}
isActive(): boolean { isActive(): boolean {
@@ -79,7 +83,7 @@ export class ScreenShareController {
// Free the @discordjs/voice connection BEFORE the Streamer joins, so // Free the @discordjs/voice connection BEFORE the Streamer joins, so
// the user has only one voice session (Discord requirement). // the user has only one voice session (Discord requirement).
await this.releaseVoice(); await this.releaseVoice(status);
await Promise.race([ await Promise.race([
this.streamer.joinVoiceChannel(channel), this.streamer.joinVoiceChannel(channel),
@@ -123,7 +127,7 @@ export class ScreenShareController {
/* already gone */ /* already gone */
} }
if (this.restoreVoice) { if (this.restoreVoice) {
Promise.resolve(this.restoreVoice()).catch((err) => { Promise.resolve(this.restoreVoice(status)).catch((err) => {
this.logger.warn( this.logger.warn(
{ error: err instanceof Error ? err.message : String(err) }, { error: err instanceof Error ? err.message : String(err) },
"Failed to restore voice connection after screen share", "Failed to restore voice connection after screen share",