Hide browser chrome + skip YouTube cookie banner - #24
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
After the join flow finally worked end-to-end, the operator caught two things on the very first viewer's frame:
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.--kioskis 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-sizewith 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-sizeflags actually take effect.Switch YouTube to the embed URL form
youtube.com/embed/<id>?autoplay=1&rel=0. The embed player:Belt-and-suspenders: pre-seed the
CONSENT=YES+cookie on.youtube.comand.google.combefore 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_urlcovering 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 --strictclean.ruff checkclean./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
The image rebuild adds ~1 MB for fluxbox; should be fast since the rest of the layers cache. The new entrypoint logs
[entrypoint] starting fluxboxbetween 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