fix(stream): tie broadcast-helper to the stream lifecycle, enforce STREAM_BROWSER, fix fullscreen window

Addresses review of the STREAM_BROWSER / broadcast-defaults work:

- SelfbotStreamer now spawns broadcast-helper.mjs on stream start and kills it on
  stop/self-end (alongside capture + keepalive). The ad-skip, subtitle rule and
  fullscreen-toolbar-hide are therefore guaranteed broadcast-wide defaults tied
  to the broadcast - not a manual process. Fail-open: if node/Chrome deps are
  absent the stream runs without the helper. Verified the helper is a child of
  the broadcast holder and armed.
- Enforce STREAM_BROWSER at the streamer (start() returns early when
  screenBrowser===false), so EVERY caller including stream-hold.ts is voice-only
  when it's off, not just the slash command. stream-hold.ts reads STREAM_BROWSER.
- Fix broadcast-helper fullscreen: resolve the window of the tab actually in
  HTML5 fullscreen (via its CDP targetId) instead of the first HTTP tab, so the
  right Chrome window is toggled when multiple windows exist.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
javis-bot
2026-06-10 16:28:01 +09:00
parent ef6f6ff57d
commit 8aa2e4c9ba
3 changed files with 66 additions and 17 deletions

View File

@@ -92,30 +92,35 @@ async function session() {
for (const p of ctx.pages()) await arm(p);
ctx.on('page', arm); // new tabs
// Broadcast-wide: when any tab enters HTML5 fullscreen (a video 'f'), hide
// Chrome's toolbar by putting the WINDOW into Chrome-initiated fullscreen -
// xfwm4 won't hide it on HTML5 fullscreen alone, so the address bar would
// otherwise show on the broadcast. Restore when fullscreen exits.
let winFs = false;
// Broadcast-wide: when a tab enters HTML5 fullscreen (a video 'f'), hide
// Chrome's toolbar by putting THAT tab's window into Chrome-initiated
// fullscreen - xfwm4 won't hide it on HTML5 fullscreen alone, so the address
// bar would otherwise show on the broadcast. We resolve the exact window of
// the fullscreen tab (not just the first tab) and restore it on exit.
const cdp = await b.newBrowserCDPSession();
const setWindowFs = async (fs) => {
let fsWindowId = null;
const windowIdFor = async (page) => {
const s = await page.context().newCDPSession(page);
try {
const { targetInfos } = await cdp.send('Target.getTargets');
const t = targetInfos.find((x) => x.type === 'page' && x.url.startsWith('http'));
if (!t) return;
const { windowId } = await cdp.send('Browser.getWindowForTarget', { targetId: t.targetId });
await cdp.send('Browser.setWindowBounds', { windowId, bounds: { windowState: fs ? 'fullscreen' : 'normal' } });
winFs = fs;
} catch { /* best-effort */ }
const { targetInfo } = await s.send('Target.getTargetInfo');
const { windowId } = await cdp.send('Browser.getWindowForTarget', { targetId: targetInfo.targetId });
return windowId;
} finally { await s.detach().catch(() => {}); }
};
const fsTimer = setInterval(async () => {
try {
let anyFs = false;
let fsPage = null;
for (const p of ctx.pages()) {
if (await p.evaluate(() => !!document.fullscreenElement).catch(() => false)) { anyFs = true; break; }
if (await p.evaluate(() => !!document.fullscreenElement).catch(() => false)) { fsPage = p; break; }
}
if (fsPage && fsWindowId === null) {
const windowId = await windowIdFor(fsPage);
await cdp.send('Browser.setWindowBounds', { windowId, bounds: { windowState: 'fullscreen' } });
fsWindowId = windowId;
} else if (!fsPage && fsWindowId !== null) {
await cdp.send('Browser.setWindowBounds', { windowId: fsWindowId, bounds: { windowState: 'normal' } });
fsWindowId = null;
}
if (anyFs && !winFs) await setWindowFs(true);
else if (!anyFs && winFs) await setWindowFs(false);
} catch { /* best-effort */ }
}, 600);