ARRFLIX/bin/apply-26-incident-fixes.sh
s8n 1ee698b592 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.
2026-05-09 02:03:39 +01:00

168 lines
5.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# apply-26-incident-fixes.sh
#
# Re-applies the three server-state fixes from docs/26 if branding.xml /
# encoding.xml drift back to broken state (e.g. after a Jellyfin restore).
#
# 1. CustomCss: Cineplex hardcoded "Abspielen" → "Play"
# 2. CustomCss: Backdrop transparent-scope using :has() (BLACK-PASS occluded backdrop layer)
# 3. encoding.xml: EnableThrottling=false + EnableSegmentDeletion=false (kills HLS 499)
#
# Usage: ssh user@nullstone "$(cat bin/apply-26-incident-fixes.sh)"
# Idempotent: re-running is safe.
set -euo pipefail
# 3+5. encoding.xml — disable throttling + segment deletion (HLS 499)
# AND disable software tonemapping (CPU-only nullstone
# cannot sustain real-time 4K HDR tonemap+x264, ffmpeg
# runs at ~0.5x → 18s wait time before video starts;
# R&M is fake-HDR per doc 21 anyway, so no visual loss)
for cfg in /home/docker/jellyfin/config/config/encoding.xml \
/home/docker/jellyfin-dev/config/config/encoding.xml; do
[ -f "$cfg" ] || continue
cp -n "$cfg" "$cfg.bak.pre-doc26" || true
sed -i \
-e 's|<EnableThrottling>true</EnableThrottling>|<EnableThrottling>false</EnableThrottling>|' \
-e 's|<EnableSegmentDeletion>true</EnableSegmentDeletion>|<EnableSegmentDeletion>false</EnableSegmentDeletion>|' \
-e 's|<EnableTonemapping>true|<EnableTonemapping>false|' \
-e 's|<EnableVppTonemapping>true|<EnableVppTonemapping>false|' \
"$cfg"
echo "[+] patched $cfg"
done
# 1+2. branding.xml CustomCss — Abspielen + backdrop transparent-scope
patch_branding() {
local cfg="$1"
[ -f "$cfg" ] || return 0
if grep -q "ARRFLIX 2026-05-09" "$cfg"; then
echo "[=] $cfg already has doc-26 patch"
return 0
fi
cp -n "$cfg" "$cfg.bak.pre-doc26" || true
python3 - <<PY
p = "$cfg"
s = open(p).read()
patch = """
/* ARRFLIX 2026-05-09 — incident fixes (see docs/26-incident-2026-05-09-...).
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.
INC4: override the 2026-05-08 .emby-scroller=#000 rule on detail page
(it was painting a black band behind every carousel — most visible
on admin-only "More from Season" / "More Like This"). */
.mainDetailButtons .material-icons.play_arrow::after {
content: "Play" !important;
}
.itemDetailPage,
.layout-desktop:has(.itemDetailPage),
.layout-mobile:has(.itemDetailPage),
.layout-tv:has(.itemDetailPage),
.mainAnimatedPages:has(.itemDetailPage),
.pageContainer:has(.itemDetailPage),
.padded-bottom-page:has(.itemDetailPage),
.libraryPage:has(.itemDetailPage),
.absolutePageTabContent:has(.itemDetailPage) {
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;
}
/* INC4: 2026-05-08 home-page "kill gray band" rule paints .emby-scroller
#000 unscoped — that's the OPAQUE wrapper around every carousel inside
.itemDetailPage. Override back to transparent on detail page only. */
.itemDetailPage .emby-scroller,
.itemDetailPage .emby-scroller-container,
.itemDetailPage .verticalSection,
.itemDetailPage .padded-top-focusscale,
.itemDetailPage .padded-bottom-focusscale,
.itemDetailPage .moreFromSeasonSection,
.itemDetailPage .moreFromArtistSection,
.itemDetailPage .scrollSliderContainer,
.itemDetailPage .scrollButtonContainer {
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)
PY
echo "[+] patched $cfg"
}
patch_branding /home/docker/jellyfin/config/config/branding.xml
patch_branding /home/docker/jellyfin-dev/config/config/branding.xml
# Restart so changes take effect
docker restart jellyfin jellyfin-dev 2>/dev/null || docker restart jellyfin
echo "[*] Done. Verify with bin/headless-test.py."