diff --git a/src/bot/audioProcedure.ts b/src/bot/audioProcedure.ts index cbc573b..95d4565 100644 --- a/src/bot/audioProcedure.ts +++ b/src/bot/audioProcedure.ts @@ -68,6 +68,17 @@ export class AudioProcedure { // Keep the real video tracks (from fake camera) realStream.getVideoTracks().forEach(t => combinedStream.addTrack(t)); + // Diagnostic signal for production logs: confirms override really feeds Teams. + try { + const audioTracks = combinedStream.getAudioTracks(); + const videoTracks = combinedStream.getVideoTracks(); + console.log( + `[AudioPlayback] getUserMedia override active: audioTracks=${audioTracks.length}, videoTracks=${videoTracks.length}, audioLabel="${audioTracks[0]?.label || 'n/a'}"`, + ); + } catch { + // ignore + } + return combinedStream; } diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts index 37631c1..17cac1d 100644 --- a/src/bot/orchestrator.ts +++ b/src/bot/orchestrator.ts @@ -286,6 +286,19 @@ export class BotOrchestrator { try { const urlObj = new URL(launchUrl); urlObj.searchParams.delete('anon'); + // Some Teams launcher URLs carry the real meeting path in an encoded "url" param. + // In auth mode that inner URL can still contain anon=true, which forces guest-like behavior. + const encodedInnerUrl = urlObj.searchParams.get('url'); + if (encodedInnerUrl) { + try { + const inner = new URL(encodedInnerUrl, 'https://teams.microsoft.com'); + inner.searchParams.delete('anon'); + const innerPath = `${inner.pathname}${inner.search}${inner.hash}`; + urlObj.searchParams.set('url', innerPath); + } catch { + // keep original inner URL if parsing fails + } + } launchUrl = urlObj.toString(); } catch { /* keep as-is */ }