doc 26 INC2+INC3: pin backdrop, transparent sub-sections
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.
This commit is contained in:
parent
549c86efdf
commit
9b06bb48c6
3 changed files with 127 additions and 13 deletions
|
|
@ -40,15 +40,11 @@ s = open(p).read()
|
|||
patch = """
|
||||
|
||||
/* ARRFLIX 2026-05-09 — incident fixes (see docs/26-incident-2026-05-09-...).
|
||||
1. Cineplex theme hardcodes German "Abspielen" via ::after on .play_arrow.
|
||||
Override with English. The German text was in CSS, not Jellyfin locale —
|
||||
so all Traefik Accept-Language rewrites + force-english-all-users.sh
|
||||
chases never fixed it.
|
||||
2. The BLACK-PASS block paints #000 !important on .layout-desktop,
|
||||
.pageContainer, .padded-bottom-page etc — these wrap the backdrop layer
|
||||
(z-index:-1) and occlude it. Use :has() to scope only when an
|
||||
.itemDetailPage descendant exists, so backdrop renders only on detail
|
||||
pages. */
|
||||
INC1: Cineplex theme hardcodes German "Abspielen" via content: ::after.
|
||||
INC1: BLACK-PASS occludes backdrop; transparent-scope via :has().
|
||||
INC2: pin backdrop position:fixed so it persists across scroll.
|
||||
INC3: extend transparent-scope through detail-page sub-sections so
|
||||
section wrappers don't paint over the pinned backdrop. */
|
||||
.mainDetailButtons .material-icons.play_arrow::after {
|
||||
content: "Play" !important;
|
||||
}
|
||||
|
|
@ -64,6 +60,57 @@ patch = """
|
|||
background-color: transparent !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
.layout-desktop .backdropContainer,
|
||||
.layout-mobile .backdropContainer,
|
||||
.layout-tv .backdropContainer,
|
||||
.layout-desktop .backgroundContainer,
|
||||
.layout-mobile .backgroundContainer,
|
||||
.layout-tv .backgroundContainer {
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
z-index: 0 !important;
|
||||
}
|
||||
.layout-desktop .backgroundContainer.withBackdrop::after,
|
||||
.layout-mobile .backgroundContainer.withBackdrop::after,
|
||||
.layout-tv .backgroundContainer.withBackdrop::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(0,0,0,0.00) 0%,
|
||||
rgba(0,0,0,0.00) 35%,
|
||||
rgba(0,0,0,0.40) 70%,
|
||||
rgba(0,0,0,0.75) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
.itemDetailPage,
|
||||
.itemDetailPage > *,
|
||||
.detailPageContent,
|
||||
.detailPagePrimaryContainer,
|
||||
.detailPageWrapperContainer,
|
||||
.detailPageContent > *,
|
||||
.detailVerticalSection,
|
||||
.detailVerticalSection-extrabottompadding,
|
||||
.detailSection,
|
||||
.detailSectionContent,
|
||||
.itemsContainer,
|
||||
.scrollSlider,
|
||||
.scrollSliderContainer,
|
||||
.padded-bottom-page,
|
||||
.detailPagePrimaryContent,
|
||||
.sectionTitleContainer,
|
||||
.detailRibbon,
|
||||
.subtitleAudioContainer,
|
||||
.detailPageRoot {
|
||||
background-color: transparent !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
"""
|
||||
s = s.replace("</CustomCss>", patch + "</CustomCss>")
|
||||
open(p, "w").write(s)
|
||||
|
|
|
|||
|
|
@ -123,18 +123,24 @@ async def main():
|
|||
await page.goto(target, wait_until="networkidle", timeout=30000)
|
||||
await asyncio.sleep(4) # let SPA paint backdrop
|
||||
|
||||
# Probe key DOM elements
|
||||
# Probe key DOM elements (extended)
|
||||
probe = await page.evaluate("""() => {
|
||||
const result = {};
|
||||
const sel = ['.itemBackdrop', '.detailBackdrop', '.backdropContainer',
|
||||
'.backgroundContainer', '.layout-desktop',
|
||||
'body', '#reactRoot', '.itemDetailPage',
|
||||
'video', '.htmlvideoplayer', '.btnPlay', '.detailPagePrimaryContainer'];
|
||||
'video', '.htmlvideoplayer', '.btnPlay', '.detailPagePrimaryContainer',
|
||||
'.detailSection', '.detailVerticalSection', '.itemsContainer',
|
||||
'.padded-bottom-page', '.mainAnimatedPages', '.pageContainer',
|
||||
'.cardScalable', '.scrollSlider', '.sectionTitleContainer',
|
||||
'.detailPageContent', '.detailPageWrapperContainer'];
|
||||
for (const s of sel) {
|
||||
const el = document.querySelector(s);
|
||||
if (!el) { result[s] = '<absent>'; continue; }
|
||||
const els = document.querySelectorAll(s);
|
||||
if (els.length === 0) { result[s] = '<absent>'; continue; }
|
||||
const el = els[0];
|
||||
const cs = getComputedStyle(el);
|
||||
result[s] = {
|
||||
count: els.length,
|
||||
display: cs.display,
|
||||
opacity: cs.opacity,
|
||||
visibility: cs.visibility,
|
||||
|
|
@ -152,8 +158,14 @@ async def main():
|
|||
return result;
|
||||
}""")
|
||||
|
||||
# Two screenshots: top viewport + scrolled to mid-page (so fixed backdrop renders correctly)
|
||||
screenshot = os.path.join(OUT, f"{URL.replace('https://','').replace('.','_')}-detail.png")
|
||||
await page.screenshot(path=screenshot, full_page=False)
|
||||
# Scroll halfway down to verify pinned backdrop persists
|
||||
await page.evaluate("() => window.scrollTo(0, document.body.scrollHeight * 0.5)")
|
||||
await asyncio.sleep(1)
|
||||
scrolled = os.path.join(OUT, f"{URL.replace('https://','').replace('.','_')}-scrolled.png")
|
||||
await page.screenshot(path=scrolled, full_page=False)
|
||||
print(f"[+] screenshot: {screenshot}")
|
||||
|
||||
with open(os.path.join(OUT, "probe.json"), "w") as f:
|
||||
|
|
|
|||
|
|
@ -539,6 +539,61 @@ These are the dead-ends. Future operators (and future me) should skip:
|
|||
|
||||
---
|
||||
|
||||
## Iteration 2 — backdrop visible only on top viewport (2026-05-09 follow-up)
|
||||
|
||||
After INC1 (`:has()` transparent-scope) shipped and prod showed backdrop on
|
||||
detail-page top, owner reported "in the middle of the More from Season 1
|
||||
is black, it's hiding the artwork". Below-the-fold sections (Next Up, Seasons,
|
||||
More Like This) showed solid black instead of continuing the backdrop.
|
||||
|
||||
### Root cause (INC2)
|
||||
|
||||
`.backdropContainer` defaults to non-fixed positioning — it scrolls out of
|
||||
view. INC1 made wrappers transparent so backdrop showed through, but only
|
||||
where the backdrop EXISTED in the DOM viewport. Once user scrolls down,
|
||||
backdrop is above viewport, sections see body's `#000` bg.
|
||||
|
||||
### Fix INC2
|
||||
|
||||
Pin `.backdropContainer` + `.backgroundContainer` to `position: fixed; top:0;
|
||||
height:100vh; z-index:0`. Added `::after` vertical gradient (transparent at
|
||||
top → 75% black at bottom) so text remains readable as user scrolls into
|
||||
backdrop area.
|
||||
|
||||
### Root cause (INC3)
|
||||
|
||||
INC2 alone didn't fix it visually — section wrappers (`.detailVerticalSection`,
|
||||
`.scrollSliderContainer`, `.padded-bottom-page`, `.itemsContainer` etc) still
|
||||
painted opaque bg from BLACK-PASS + finity. Pinned backdrop sat behind, but
|
||||
sections occluded it section-by-section.
|
||||
|
||||
### Fix INC3
|
||||
|
||||
Extended transparent-scope to all detail-page sub-sections:
|
||||
`.itemDetailPage > *`, `.detailPageContent`, `.detailPagePrimaryContainer`,
|
||||
`.detailPageWrapperContainer`, `.detailVerticalSection*`, `.detailSection*`,
|
||||
`.itemsContainer`, `.scrollSlider*`, `.padded-bottom-page`,
|
||||
`.sectionTitleContainer`, `.detailRibbon`, `.subtitleAudioContainer`,
|
||||
`.detailPageRoot`.
|
||||
|
||||
### Verification (INC2 + INC3)
|
||||
|
||||
Updated `bin/headless-test.py` to take TWO viewport screenshots: top-of-page
|
||||
+ scrolled to 50% page height. With INC2/INC3 applied, scrolled screenshot
|
||||
shows R&M backdrop persisting behind "Seasons" + "More Like This" sections
|
||||
(previously: solid black).
|
||||
|
||||
### Lesson learned
|
||||
|
||||
When pinning a backdrop with `position:fixed`, transparency must extend
|
||||
RECURSIVELY through every wrapper ON TOP of the backdrop layer, not just the
|
||||
top-level page wrappers. Test with scrolled screenshot — full-page screenshot
|
||||
in playwright stretches viewport and hides `position:fixed` issues.
|
||||
|
||||
`bin/headless-test.py` now takes both top + scrolled. Use both to bisect.
|
||||
|
||||
---
|
||||
|
||||
## Open follow-ups (for separate sessions)
|
||||
|
||||
- **AV1+Opus playback** (Bug E): Chrome's AV1 DirectStream codec-tag mislabel
|
||||
|
|
|
|||
Loading…
Reference in a new issue