doc 26: case CLOSED — final state + 8 forbidden patterns

All 8 owner-reported symptoms resolved across 5 iterations (INC1-5):

INC1 — index.html drift revert + :has() transparent-scope + Cineplex
       Abspielen override + encoding.xml HLS 499 fix
INC2 — pin .backdropContainer position:fixed (persistent backdrop)
INC3 — extend transparent-scope through detail-page sub-sections
INC4 — .emby-scroller transparent (kill black band behind carousels) +
       EnableTonemapping=false + 20Mbps RemoteClientBitrateLimit cap +
       headless-test-v2.py (admin+guest+click-play+bg-sweep)
INC5 — AV1 source re-encode (MNS S1E2/E4/E5 to H.264/AAC) +
       enableHlsFmp4=false localStorage shim +
       ::-webkit-scrollbar styled to ARRFLIX palette

Verification: headless playwright on Chrome + Firefox UA confirms MNS
S1E4 plays 1920x1080 readyState=4 currentTime advancing. Owner
double-confirmed solved.

Doc 26 final state section + 18-item forbidden-pattern checklist added
for future operators.
This commit is contained in:
s8n 2026-05-09 02:03:39 +01:00
parent 733a6df96c
commit 1ee698b592
2 changed files with 206 additions and 1 deletions

View file

@ -135,6 +135,23 @@ patch = """
background-color: transparent !important;
background: transparent !important;
}
/* INC5: kill grey scrollbar groove at page bottom (Chrome native scrollbar
default = grey track; appears as ~15px strip at viewport bottom). Style
all scrollbars to ARRFLIX palette. */
*::-webkit-scrollbar {
background: #000000 !important;
width: 10px;
height: 10px;
}
*::-webkit-scrollbar-track { background: #000000 !important; }
*::-webkit-scrollbar-thumb {
background: #2a2a2a !important;
border-radius: 5px;
}
*::-webkit-scrollbar-thumb:hover { background: #3a3a3a !important; }
*::-webkit-scrollbar-corner { background: #000000 !important; }
* { scrollbar-color: #2a2a2a #000000; }
html, body { scrollbar-color: #2a2a2a #000000; }
"""
s = s.replace("</CustomCss>", patch + "</CustomCss>")
open(p, "w").write(s)

View file

@ -2,7 +2,8 @@
> Session log. Live document — updated as fix proceeds. Goal: future-me + other operators can read this and skip every dead-end I already walked.
Status as of doc creation: **ONGOING** — partial fix applied, more under investigation.
Status: **CLOSED 2026-05-09** — owner double-confirmed all symptoms resolved.
See "## Final state" at bottom for the consolidated outcome.
---
@ -1302,3 +1303,190 @@ that the device profile doesn't DirectPlay).
**Repo commit:** `web-overrides/index.html` updated under git so the
repo state matches the deployed file (no drift).
### INC5 MNS playback verify (post-fix end-to-end test)
Closes the loop on Iteration-3 INC5 fixes (AV1 source re-encode +
fMP4-HLS-disable shim) by exercising the actual user flow on prod:
log in -> click Play on MNS S1E4 -> observe `<video>` state.
**Item under test.** itemId `9312799ca24979bd05aad9733ce7ee14` --
`The Mike Nolan Show (2016) - S01E04 - Ding Dong Delli.mkv`.
**Pre-test ffprobe re-confirmed file is now H.264/AAC** (re-encode
landed):
```
codec_name=h264 profile=High width=1920 height=1080 pix_fmt=yuv420p
audio codec_name=aac
```
i.e. AV1+Opus is gone from the file itself; this verification is now
testing whether the re-encoded asset DirectPlays cleanly, not whether
AV1+Opus DirectStream still misbehaves.
**Verification method.** `/tmp/mns-s1e4-verify.py` (focused playwright
probe). Logs in as admin `s8n / 2001dude`, navigates to the MNS S1E4
detail page, clicks `.btnPlay`, snapshots `<video>` state at 5/10/20/30
s post-click. Captures network requests filtered to `/Videos/`,
`/master.m3u8`, `/PlaybackInfo`, `.m4s`, `.ts`, `/Audio`, `/hls/`. Runs
once with Chrome UA (default playwright Chromium 148), once with Firefox
UA (`Mozilla/5.0 (X11; Linux x86_64; rv:130.0) Gecko/20100101
Firefox/130.0`). Temp ApiKey row `arrflix-mns-verify-2026-05-09`
inserted to drive `/Users/.../Items` lookup, deleted post-test (verified
`count=0`).
**Result -- Chrome UA:** PLAYING.
- `videoWidth=1920`, `videoHeight=1080` at every snapshot.
- `currentTime` advanced 58.56 -> 63.55 -> 73.54 -> 83.54 (resumed from
the user's 0:54.326 stop point recorded at 01:53:19).
- `readyState=4` (HAVE_ENOUGH_DATA), `paused=false`, `error=null`.
- 1 buffered range present.
**Result -- Firefox UA:** PLAYING.
- `videoWidth=1920`, `videoHeight=1080` at every snapshot.
- `currentTime` advanced 78.79 -> 83.78 -> 93.76 -> 103.77.
- `readyState=4`, `paused=false`, `error=null`.
**What changed since pre-fix.** Pre-fix symptom was `videoWidth=0` +
audio at t<=0:54 (frames decoded as silent black). Post-fix:
`videoWidth=1920`, no `<video>.error`, `currentTime` advances normally,
`readyState=4`. Decisive network-log evidence:
```
GET /Items/9312799c.../PlaybackInfo -> 200
GET /Videos/9312799c.../stream.mkv?Static=true&mediaSourceId=... -> 206
```
`Static=true` means the browser is **DirectPlaying** the H.264/AAC MKV
(byte-range request, no transcode pipeline involved). No `master.m3u8`,
no `.m4s`, no `.ts` segment requests -- the `enableHlsFmp4=false` shim
isn't even exercised on this asset because the H.264 source needs no
HLS at all. The fMP4-disable fix sits dormant as defence-in-depth for
the next non-DirectPlay codec.
**Server-side transcode logs.**
```
$ ssh user@192.168.0.100 'docker logs --since 2m jellyfin 2>&1 | \
grep -iE "ffmpeg|libx264|libfdk_aac|StartTranscode|Ding Dong"'
01:55:32 [INF] [10] ...MediaEncoder: Starting .../ffprobe ... Ding Dong Delli.mkv
```
Only `ffprobe` (codec-discovery for PlaybackInfo response) -- **no
`ffmpeg` transcode** kicked off for the MNS session. ffmpeg activity in
the window was unrelated content (Dark Knight HEVC movie + R&M S01E01
HEVC pilot, both 4K HDR). Server has the right idea: H.264/AAC -> hand
the file to the browser and stay out of the loop. Confirms both fixes
work as designed:
- AV1 re-encode -> source codec is now DirectPlayable, transcode
skipped entirely.
- fMP4-HLS-disable -> shim sets `localStorage.enableHlsFmp4=false`
before SPA boot, would force `.ts` segments if a transcode WERE
triggered (visible to the user via `localStorage.getItem('enableHlsFmp4')
=== 'false'` in DevTools console, repo-deployed file md5 matched).
**Failures captured (5x `net::ERR_ABORTED`).** All on
`/Sessions/Capabilities/Full` and `/Sessions/Playing[/Progress]`. These
are POSTs that the SPA fires through a `sendBeacon`-style path and
playwright's content_script aborts on page navigation; they do not
indicate playback impairment. Console messages: 120 (Chrome) / 118
(Firefox) -- routine SPA chatter, no playback-fatal entries.
**Recommended next step.**
1. **Owner re-test in their daily-driver browser.** Hard-reload
(Ctrl+Shift+R) on the MNS S1E4 detail page, hit Play, choose
"Restart" (not "Resume") so the player starts from t=0 and exercises
the full first-play codepath. The headless test resumed from t=54.3s
so it skipped the initial keyframe seek the user originally tripped
over. If still black: capture `chrome://media-internals` mid-failure
and grep the decoder line for the codec tag.
2. **Sweep the rest of the library for AV1.** This was 3 episodes of
one show; if Sonarr/Radarr are auto-grabbing AV1 releases the
problem will recur. Open task carried from the Iteration-3 follow-ups
list.
3. **Pin MNS S1E4 in `bin/headless-test-v2.py`.** Currently the AV1
episode is auto-discovered via `find_av1_episode()`; with the source
re-encoded that lookup will pick a different episode (or none) on
future runs. Hardcode `MNS_S1E4_ID = "9312799ca24979bd05aad9733ce7ee14"`
so this regression test sticks specifically to the file that broke.
4. **Cleanup confirmed.** Temp ApiKey
`arrflix-mns-verify-2026-05-09` deleted from
`/home/docker/jellyfin/config/data/jellyfin.db` (count=0).
**Artifacts.**
- `/tmp/mns-s1e4-verify.py` -- test script
- `/tmp/mns-verify/video-snapshots-{chrome,firefox}.json` -- all 4
timestamped `<video>` snapshots per UA
- `/tmp/mns-verify/{netlog,console,failures}-{chrome,firefox}.json` --
network + console + requestfailed dumps
- `/tmp/mns-verify/02-play-{chrome,firefox}-{5s,10s,20s,30s}.png` --
per-checkpoint screenshots (8 files)
---
## Final state (case closed 2026-05-09)
| Symptom (owner-reported, in order) | Iteration | Final status |
|---|---|---|
| 1. Browser arrflix broken, videos don't play | INC1 (index.html drift revert) | ✅ resolved |
| 2. Can't see preview of TV/movie | INC1 (`:has()` transparent-scope) | ✅ resolved |
| 3. Page Unresponsive Chrome dialog | INC1 (DOM-walker MutationObserver removed) | ✅ resolved |
| 4. "Abspielen" German Play button | INC1 (Cineplex CSS `content:` override) | ✅ resolved |
| 5. All show backdrop art replaced by black | INC1 → INC3 (`:has()` + sub-section transparent) | ✅ resolved |
| 6. Black band hiding "More from Season N" | INC4 (`.emby-scroller` transparent) | ✅ resolved |
| 7. Video plays as black screen on click | INC4 (tonemap=false + 20Mbps cap) + INC5 (AV1 re-encode + fMP4=false shim) | ✅ resolved (Chrome + Firefox UA verified) |
| 8. Grey strip at very bottom of page on scroll | INC5 (ARRFLIX-themed `::-webkit-scrollbar`) | ✅ resolved |
### Verification matrix (headless playwright)
- **Dark Knight (HEVC 4K HDR)**: `readyState=3`, playing 1918×800 (1080p transcode)
- **Mike Nolan Show S1E4 Ding Dong Delli (was AV1+Opus, now H.264/AAC)**: `readyState=4`, playing 1920×1080, DirectPlay (no transcode needed)
- **Rick and Morty S1E1 Pilot (4K HDR HEVC)**: still slow first-frame on cold seek; pre-transcode batch tracked as follow-up
### Files changed (this incident)
Repo `git.s8n.ru/s8n/ARRFLIX`:
- `docs/26-incident-2026-05-09-page-unresponsive-and-playback.md` (this doc)
- `bin/headless-test.py` (v1 — added click + dual-screenshot)
- `bin/headless-test-v2.py` (v2 — multi-user, click Play, bg sweep, diff vs golden)
- `bin/apply-26-incident-fixes.sh` (idempotent re-apply of all INC patches)
- `web-overrides/index.html` (INC5 fMP4=false localStorage shim)
- `.gitignore` (`__pycache__/`)
Server-side state on `nullstone:/home/docker/jellyfin/config/config/`:
- `branding.xml` — INC1+INC2+INC3+INC4+INC5 CustomCss patches
- `encoding.xml``EnableThrottling=false`, `EnableSegmentDeletion=false`, `EnableTonemapping=false`, `EnableVppTonemapping=false`
- 12 user `Policy.RemoteClientBitrateLimit=20000000` (20 Mbps cap)
- MNS S1E2/E4/E5 source files re-encoded AV1+Opus → H.264/AAC; originals at `/tmp/*-av1-original-*.mkv.bak`
### Forbidden/learned patterns added in this incident
(in addition to original "Do-NOT-repeat checklist")
11. **Don't trust headless `full_page=True` screenshot for `position:fixed`
elements.** Stretches viewport, hides fixed-positioning regressions.
Use viewport-sized screenshots at multiple scroll positions.
12. **Don't test playback only as `guest` user.** Admin sees more sections
(carousels: "More from Season X", "More Like This"). Admin-only sections
can hide their own bg regressions. Always run as both.
13. **Don't declare playback "fixed" without clicking Play in headless.**
Server-log `PlaybackStart` is necessary but not sufficient — browser
side might receive bytes and still render black (codec mislabel).
14. **Don't apply `Clear-Site-Data` on every visit.** Wipes cookies → forces
re-login → race with playback init. Use it ONCE for stuck SW state and
immediately remove the middleware.
15. **Don't ban a unscoped `background: #000` rule from prior commits without
auditing every selector it covers.** The 2026-05-08 home-page
`.emby-scroller=#000` was sensible for home-page Recently Added rows but
catastrophic on detail pages with a pinned backdrop. Scope every
background rule to its target page-class.
16. **Don't assume `text-walker MutationObserver` is fast.** O(N×M) on poster
grids. Always debounce + scope to specific attribute filters.
17. **Don't forget Chrome's native scrollbar default = grey.** Style with
`::-webkit-scrollbar*` on dark themes.
18. **Don't fight an upstream codec mislabel bug — re-encode the source.**
Faster than profile editing for tiny files; aligns with "best quality"
promise anyway.
### Next sessions
- Library-wide AV1 sweep + Sonarr/Radarr custom format penalty so future grabs don't re-trigger #15646.
- 4K HDR pre-transcode batch (R&M masters → 1080p H.264 SDR) OR 10.11.8 migration with GPU driver fixed.
- v2 test allowlist: filter off-viewport elements (negative coords) to drop false-positive regressions on `#reactRoot` y=-490 and collapsed `.mainDrawer` x=-320.
- Promote `/tmp/*-av1-original-*.mkv.bak` to a real archive directory.