Skip to content

Hide browser chrome + skip YouTube cookie banner - #24

Merged
queueeee merged 1 commit into
mainfrom
claude/review-claude-md-iOuUt
May 1, 2026
Merged

queueeee merged 1 commit into
mainfrom
claude/review-claude-md-iOuUt

Conversation

@queueeee

@queueeee queueeee commented May 1, 2026

Copy link
Copy Markdown
Owner

Symptom

After the join flow finally worked end-to-end, the operator caught two things on the very first viewer's frame:

man sah oben die nav bar vom browser und ein cookie Fenster von yt ist aufgetaucht

So we had the browser's own chrome (URL bar, tabs, menu) bleeding into the captured frame, plus YouTube's EU cookie-consent dialog covering the player.

Root causes

Chrome chrome visible: Chromium launches with --kiosk, but Xvfb has no window manager in our container. --kiosk is implemented by sending _NET_WM_STATE_FULLSCREEN (an EWMH hint) and waiting for the WM to honour it. With no WM, the hint falls on the floor; Chromium opens at the configured --window-size with default chrome.

YouTube consent dialog: We loaded the watch UI directly, which always serves the EU consent gate. The existing locator click for "Accept all" was racing against page render and frequently missed.

What ts6-manager does

ts6-manager sidesteps the entire browser problem by using yt-dlp to resolve the watch URL to a direct media stream, then feeding that into ffmpeg. No browser, no chrome, no consent dialogs.

We can't take that shortcut because the project's contract is "any URL the operator hands the bot" — Twitch streams, browser games, arbitrary pages — and we need the browser. So fix the browser side instead.

Fix

Add fluxbox (~1 MB) to the Docker image and start it before the app in entrypoint.sh. Tiniest window manager that honours kiosk hints; once it's up, our existing --kiosk / --start-maximized / --window-size flags actually take effect.

Switch YouTube to the embed URL form youtube.com/embed/<id>?autoplay=1&rel=0. The embed player:

  • Bypasses the EU cookie gate by design
  • Has no header / search bar / sidebar / related-videos overlay
  • Autoplays from a query param (no locator click needed)

Belt-and-suspenders: pre-seed the CONSENT=YES+ cookie on .youtube.com and .google.com before navigation, in case the rare regions that still gate on embed catch us out.

Test plan

  • pytest — 259 passed (5 new parameterised tests for _to_embed_url covering watch?v=, youtu.be, m.youtube.com, watch URLs with extra params, embed URLs that are already in the right form, and non-YouTube fallback).
  • mypy --strict clean.
  • ruff check clean.
  • Live: /play https://www.youtube.com/watch?v=dQw4w9WgXcQ → captured frame should show only the YouTube player content, no browser chrome at the top, no consent banner overlay.

Operator notes

cd /opt/ts6-stream-bot
git pull
docker compose -f docker-compose.yml -f docker-compose.host.yml down
docker compose -f docker-compose.yml -f docker-compose.host.yml up -d --build
docker compose -f docker-compose.yml -f docker-compose.host.yml logs -f

The image rebuild adds ~1 MB for fluxbox; should be fast since the rest of the layers cache. The new entrypoint logs [entrypoint] starting fluxbox between Xvfb and PulseAudio.

The YoutubeSource log line now shows both the original watch URL and the embed URL it rewrote to: youtube.open original=... embed=....

https://claude.ai/code/session_016DuCjRJK995Tj9aDhhB9at


Generated by Claude Code

Operator after the join flow finally worked end to end:

> man sah oben die nav bar vom browser und ein cookie Fenster
> von yt ist aufgetaucht

Two things our captured frame was carrying that shouldn't have been:

1. Chromium's own chrome (URL bar, tabs, menu) at the top. The
   browser launches with --kiosk, but Xvfb has no window manager
   in our container so the EWMH _NET_WM_STATE_FULLSCREEN hint that
   --kiosk relies on falls on the floor. Chromium opens at the
   configured --window-size with default chrome instead. Add
   fluxbox (~1 MB) and start it before the app: tiniest WM that
   honours kiosk hints, and the rest of our launch args now
   actually take effect.

2. YouTube's EU cookie-consent dialog covering the player on the
   first frame. We loaded the watch UI directly, which always
   serves the gate. Switch to the embed URL form
   ``youtube.com/embed/<id>?autoplay=1&rel=0``: the embed player
   bypasses the consent dialog by design, has no header / sidebar
   / related-videos clutter, and autoplays from a query param so
   we don't depend on locator clicks. Belt-and-suspenders: also
   pre-seed the ``CONSENT=YES+`` cookie on .youtube.com /
   .google.com for the rare regions where the gate still triggers
   on embed.

ts6-manager solves both by skipping the browser entirely (yt-dlp
resolves the watch URL, then ffmpeg pulls the media stream
directly). We can't take that shortcut because the project's
contract is "any URL the operator hands the bot" - Twitch, browser
games, arbitrary pages - so the browser is here to stay. These
fixes make the browser-rendered output as clean as the direct one.

Tests:
* ``test_youtube_to_embed_url_extracts_id`` covers the URL forms
  that hit the controller in practice: watch?v=, youtu.be/,
  m.youtube.com, watch URLs with extra params (timestamps, lists),
  and inputs that are already embed URLs.
* ``test_youtube_to_embed_url_passes_through_unrecognised``
  guards the non-YouTube fallback path.
@queueeee
queueeee merged commit 95bf7c8 into main May 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants