Skip to content

Fix Cinema Mode full-page blackout and Reverse Playlist not applying after refresh - #4354

Open
akshar27 wants to merge 1 commit into
code-charity:masterfrom
akshar27:fix/cinema-mode-and-reverse-playlist
Open

akshar27 wants to merge 1 commit into
code-charity:masterfrom
akshar27:fix/cinema-mode-and-reverse-playlist

Conversation

@akshar27

Copy link
Copy Markdown

Problem

Fixes #4353 — two independent bugs reported together.

1. Cinema Mode blacks out the entire page

Both player_cinema_mode_button (manual toggle) and the shortcutCinemaMode keyboard shortcut worked by appending a position: fixed, full-viewport, fully opaque overlay div, then trying to visually raise the player above it by setting z-index: 10000 + position: relative on three separate elements (player-full-bleed-container, player-container, ytd-player).

This no longer works on current YouTube. I inspected the live DOM: player-container's ancestor chain and the overlay's parent (full-bleed-container) chain both converge back up at ytd-watch-flexy with no intervening stacking context — so in principle the higher z-index should win — but empirically (confirmed via elementFromPoint on the real page) the fixed overlay always ends up on top. The player DOM nesting has drifted enough since this code was written that the z-index elevation no longer reaches the right stacking context, and the result is a full black screen with audio still playing but no video or controls, exactly as reported — and no way back short of a refresh.

2. Reverse Playlist doesn't survive a refresh

Toggling reverse-playlist on persists correctly (playlist_reversed_active in storage, and the button correctly shows "active" after a refresh), but the actual playlist order and next-video wiring silently stay unreversed. Root cause: playlistReverseUpdate()'s player-sync step calls ImprovedTube.elements.ytd_player.updatePlayerComponents(...) with no check that ytd_player is actually populated. That reference is set by a separate, async DOM walk (childHandler via a MutationObserver) and isn't guaranteed to be ready 100ms after yt-page-data-updated fires on a fresh page load — so the call throws and silently aborts the rest of the callback, including the direct playlist_panel.data = playlist patch a few lines later that's needed to make the panel actually re-render (playlist.contents itself had already been reversed earlier in the function — it just never reached the UI or the player's own next-video order).

Fix

Cinema Mode: replaced the overlay + z-index approach with a box-shadow "spotlight" drawn directly on #ytd-player: box-shadow: 0 0 0 9999px rgba(0,0,0,1). This tracks the real player size correctly in both the default and theater layouts (verified: 1048×590 vs 1512×658), and since it's painted as part of the player element's own box, it's automatically correctly stacked above the page — no z-index or stacking-context reasoning needed at all. All three call sites (manual button, the auto-enable/auto-disable-on-pause pair, and the keyboard shortcut) now share one ImprovedTube.cinemaModeSetVisible() helper and one ImprovedTube.cinemaModeActive flag instead of three separate copies of the old broken logic.

Reverse Playlist: added a fallback DOM query (ImprovedTube.elements.ytd_player || document.querySelector('ytd-player')) so a not-yet-cached reference doesn't crash the callback, and replaced the previous unguarded one-shot setTimeout(..., 100) with a bounded retry (up to 5 attempts, 200ms apart) so a still-initializing player on a fresh page load gets picked up shortly after instead of being silently missed forever.

Testing

  • Added tests/unit/cinema-mode-and-reverse-playlist.test.js (7 tests) matching this repo's existing test convention.
  • npm test: 123/123 passing (26 suites).
  • npm run lint: clean.
  • Live-verified both fixes against real YouTube, not just unit tests:
    • Cinema mode: reproduced the exact reported blackout by replicating the old overlay+z-index code against a live youtube.com/watch page (confirmed via elementFromPoint that the overlay wins despite the lower z-index), then confirmed the new box-shadow approach correctly darkens everything except the player in both layouts, with before/after screenshots, and that player controls stay fully interactive (loading spinner, scrubber, pause/play, etc. all hit-testable).
    • Reverse playlist: ran the actual fixed playlistReverseUpdate() against a real 9-video NASA playlist's live data, deliberately leaving ImprovedTube.elements.ytd_player uncached to reproduce the race — confirmed no throw, confirmed playlist.contents order actually reverses, and confirmed the playlist panel's rendered .data reflects the reversed order.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Snzt7njUYkxw4oxYJsENbQ

…resh

Two independent bugs reported together in code-charity#4353:

1. Cinema Mode (both the toggle button and the keyboard shortcut)
   darkened the page by appending a position:fixed, full-viewport
   overlay div and trying to out-rank it by raising z-index on
   player-full-bleed-container / player-container / ytd-player.
   YouTube's player DOM nesting has changed enough that this no
   longer works - the elevated z-index ends up compared against the
   overlay in a different effective stacking order - so the overlay
   covered the whole page, video and controls included, with no way
   back short of a refresh.

   Replaced it with a box-shadow "spotlight" drawn directly on
   #ytd-player (0 0 0 9999px rgba(0,0,0,1)), which tracks the real
   player size in both the default and theater layouts and is
   painted as part of the player's own, already-correctly-stacked
   box - no z-index or stacking-context guesswork needed. All three
   call sites (manual button, auto-enable/disable on pause, keyboard
   shortcut) now share one ImprovedTube.cinemaModeSetVisible() helper
   and one ImprovedTube.cinemaModeActive flag instead of three copies
   of the same broken logic.

2. Reverse Playlist: toggling it on persisted correctly
   (playlist_reversed_active in storage, button showed active after
   a refresh), but the actual playlist order and next-video wiring
   silently stayed unreversed. playlistReverseUpdate()'s player-sync
   step accessed ImprovedTube.elements.ytd_player without checking
   it was populated - that reference is set by a separate, async DOM
   walk and isn't guaranteed to be ready yet on a fresh page load, so
   the call threw and silently aborted the rest of the callback,
   including the direct playlist_panel.data patch that makes the
   panel actually re-render. Added a fallback DOM query for
   ytd_player and a bounded retry (5 attempts, 200ms apart) instead
   of the previous unguarded one-shot attempt.

Both fixes verified live against real YouTube pages (DOM/stacking
inspection, real playlist data through the actual reverse logic,
before/after screenshots), not just via the unit tests.

Fixes code-charity#4353

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Snzt7njUYkxw4oxYJsENbQ
@StudioNirin

Copy link
Copy Markdown

Amazing, thankyou. Hopefully this gets merged.

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.

Interesting plugin, couple bugs - 'Cinema Mode' and 'Reverse Playlist'🐞

2 participants