After INC1 fixed the Abspielen + first-fold backdrop, owner reported black
band hiding artwork in More from Season 1 / below-fold sections. Two more
patches required:
INC2 — pin .backdropContainer + .backgroundContainer position:fixed; height
100vh so backdrop persists during scroll. Added vertical fade ::after.
INC3 — extend transparent-scope to ALL detail-page sub-sections
(.detailVerticalSection, .scrollSlider, .padded-bottom-page,
.itemsContainer etc) so section wrappers don't paint over the pinned
backdrop section by section.
bin/headless-test.py now takes top + scrolled viewport screenshots.
full_page=True hides position:fixed regressions, dual-screenshot exposes
them. Use both to bisect.
bin/apply-26-incident-fixes.sh updated with INC2+INC3.
Open: AV1+Opus playback (Mike Nolan Show) still tracked for 10.11.8
migration. .detailLogo regression possible — test in actual browser.
Symptoms: Page Unresponsive on poster grid, posters missing then black
backdrops, 'Abspielen' German Play button surviving Traefik+force-english
chases, video black-screen on play.
Root causes (different from initial guesses):
- Browser hangs: deployed index.html drifted ahead of repo; uncommitted
forceEnglishUI() text-walker MutationObserver froze main thread on poster
lazy-load. Reverted to repo HEAD.
- 'Abspielen': Cineplex theme HARDCODES German via 'content:' ::after rule
-- not a Jellyfin locale issue. Doc 25 already proved per-user UICulture
is theatre. Override CSS with content: 'Play'.
- Backdrops black: BLACK-PASS CustomCss block paints #000 !important on
.layout-desktop / .pageContainer -- occludes backdrop layer (z-index:-1).
Existing transparent-scope rule used body.itemDetailPage selector that
doesn't match in 10.10.3 (body class is libraryDocument). Replaced with
:has(.itemDetailPage) ancestor scoping.
- HLS 499: encoding.xml had EnableThrottling+EnableSegmentDeletion=true,
segments reaped before browser re-request. Disabled both.
Verified via new bin/headless-test.py (playwright Chromium login + screenshot
+ computed-style probe). Fixes idempotent and re-runnable via new
bin/apply-26-incident-fixes.sh.
Open: AV1+Opus items still black-screen in Chrome due to DirectStream
codec-tag mislabel bug. Tracked for 10.11.8 migration.
AudioLanguagePreference=eng, SubtitleLanguagePreference=eng,
SubtitleMode=Default, PlayDefaultAudioTrack=true, UICulture=en-US.
Per-user Configuration POST applied to all 9 existing users + wrapper
updated for future creations.
doc 20 covers the multi-layer pin (server / per-user / web SPA / Accept-
Language), the idempotent re-apply runner, drift-check curl one-liners,
known gaps, and a systemd-timer suggestion for weekly auto re-application.
bin/english-lockdown-runner.sh: idempotent runner that POSTs server-wide
UICulture / PreferredMetadataLanguage / MetadataCountryCode and per-user
UICulture / Audio+Subtitle prefs / PlayDefaultAudioTrack. Reads
JELLYFIN_API_TOKEN from env (set -u, refuses to run without it). One-line
summary per surface; exit 0 on full success, 1 on any failure.
doc 15 prefaced with a "Status as of 2026-05-08" section noting the
multi-agent lockdown sweep and cross-linking the audit baseline (doc 19,
sibling) and the new lockdown procedure (doc 20). Original body preserved
verbatim as historical context.
Owner saw "Abspielen" on the Play button — caused by every user having
Configuration.UICulture absent, so the web SPA falls back to browser
Accept-Language. No server-side flag exists to override this.
Adds docs/15-force-english.md with the per-user forcing mechanism,
limits (pre-auth splash bundle still uses navigator.language), and a
ready-to-execute bash script bin/force-english-all-users.sh that pins
UICulture=en-US on every user via POST /Users/{id}/Configuration.
Plan-only commit — no live config changed. Owner triggers when ready.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CSS selectors in CustomCss (a[href*=mypreferencesmenu], :has(...) wrappers)
weren't reliably hiding the entry — bundle renders it via MUI ListItemButton
+ React Router NavLink and the rendered DOM didn't match the wrapper rules.
Add nukeSettings() to the runtime shim: queries any
a[href*=mypreferencesmenu] / [to*=mypreferencesmenu], walks up to closest
li/.MuiListItem-root/[role=menuitem] and sets display:none. Wired into
start(), a new MutationObserver on document.body, and the existing 1s
setInterval. CustomCss rules left in place as belt-and-braces.
Doc: extend 10-spa-runtime-shim.md with the diagnosis, the bind-mount inode
gotcha (single-file binds + os.replace orphans the container's view), and
the nsenter-based recovery path.
The static <title>ARRFLIX</title> patch wasn't enough - Jellyfin's bundle
calls document.title=... on hydrate and per-route, so the tab kept showing
'Jellyfin'. Add a self-contained inline IIFE in <head> that:
- Replaces 'Jellyfin' with 'ARRFLIX' on the title (incl. ' - Jellyfin' suffix)
- Pins favicon hrefs to the existing data: URL already in the page
- Watches <head> via MutationObserver for SPA churn
- Has a 1s setInterval safety net for late-binding navigations
- One-shot unregisters the Jellyfin service worker so old clients reload fresh
bin/inject-shim.py is the source of truth - idempotent (replaces marker block).
docs/10-spa-runtime-shim.md covers root cause, deploy flow, SW eviction, and
how to extend the shim on Jellyfin upgrade.
- EnableUserPreferenceAccess=false on guest + 5 (hides Display, Home,
Playback, Subtitles pref pages — owner controls UX centrally).
- Wrapper bin/add-jellyfin-user.sh updated to bake this into all future
non-admin user creations.
- ROADMAP entries (added by sibling import agents):
- Imported: The Incredible Hulk (2008), TMDB 1724, 4 images
- Imported: Idiocracy (2006), TMDB 7512 (NOT 1542 = Office Space)
- Imported: American Dad! (2005) S01-S04, 58 eps, TMDB 1433
- WAN exposure docs added (doc 09, 256 lines): Gandi A record live,
no-guest middleware dropped, lockout=5 baked in. Owner still must
port-forward 80/443 on home router for actual public access.
Owner flipped libraries to PreferredMetadataLanguage=en, MetadataCountryCode=US.
New-user defaults must match. Wrapper now sets:
AudioLanguagePreference=eng (was pol)
SubtitleLanguagePreference=eng
SubtitleMode=Default (was Always — let player decide based on audio)
Jellyfin has no native global default for new-user DisplayPreferences;
home-layout defaults are baked into the web bundle. This wrapper layers
the s8n canonical prefs on top after user creation:
- Home sections: resume, resumeaudio, nextup, latestmedia, none x6
(drops the 'My Media' tile row — sidebar already exposes libraries)
- SubtitleMode=Always, SubtitleLanguagePreference=eng
- AudioLanguagePreference=pol
Use for every new user from now on; achieves the 'global default' the
admin wanted without patching the web bundle.
Already retroactively applied to s8n + guest.