4-variant ARRFLIX login picker served at localhost:666 (variants 01 The Theater · 02 The Marquee · 03 The Cinema · 04 The Noir). Variant 01 was selected. Includes theater-fullsize.html (1920x1080 mockup), 470x170 landscape arrflix-logo (PNG + b64), poster-bg backdrop, and tv_theater_port.py CSS-block patcher reference for the later prod deploy. Persisted from /tmp to survive reboot; iteration continues in this dir under a 4-agent crew (Designer / Coder / Chromium / Inspector).
268 lines
9.6 KiB
Python
268 lines
9.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Port "The Theater" login design (variant 01 from picker R3) to
|
|
tv.s8n.ru jellyfin-stock.
|
|
|
|
Edits:
|
|
1. /opt/docker/jellyfin-stock/web-overrides/index.html (precss)
|
|
2. /home/docker/jellyfin-stock/config/config/branding.xml (CustomCss)
|
|
|
|
Theater spec:
|
|
- Backdrop: Cineplex poster-bg.jpg with vignette + linear shadow
|
|
- Top bar: 88px tall, transparent gradient fade, ARRFLIX logo
|
|
CENTERED (156px wide) via .skinHeader::before
|
|
- Hide .pageTitleWithLogo top-left (we paint centered instead)
|
|
- "Sign In" heading: keep stock h1.sectionTitle "Please sign in",
|
|
re-style large + centered (the visible text stays "Please sign in"
|
|
-- short of bundle JS rewrite that's the JF i18n string; matches
|
|
arrflix.s8n.ru behavior)
|
|
- Form card: .padded-left.padded-right.padded-bottom-page styled
|
|
as 540px wide dark glass card, padding, border, blur
|
|
- Inputs: underline-only, red focus
|
|
- Sign In: full-width red square button, 4px radius
|
|
- Remember Me: red square checkbox (already wired in earlier round)
|
|
- Disclaimer: small white-50% centered
|
|
"""
|
|
import re, sys
|
|
|
|
THEATER_PRECSS = """
|
|
/* === ARRFLIX 2026-05-12 -- THE THEATER login port ====================
|
|
Applied AFTER previous "ARRFLIX login-parity" + selectserver-extend
|
|
+ hide-visualform + polish-flash blocks. !important + late cascade
|
|
beats earlier rules. Sources:
|
|
picker variant 01 at /tmp/picker_assets/picker.html (.v1.*)
|
|
User pick 2026-05-12: "the theater sign in looks the best".
|
|
-------------------------------------------------------------------- */
|
|
|
|
/* Top bar: 88px tall transparent fade. ARRFLIX wordmark centered. */
|
|
.skinHeader,
|
|
.skinHeader.semiTransparent,
|
|
.skinHeader.focuscontainer-x,
|
|
.skinHeader.focuscontainer-x.skinHeader-withBackground,
|
|
.skinHeader.focuscontainer-x.skinHeader-withBackground.skinHeader-blurred {
|
|
height: 88px !important;
|
|
background: linear-gradient(180deg, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0) 100%) !important;
|
|
border: none !important;
|
|
contain: none !important;
|
|
overflow: visible !important;
|
|
}
|
|
/* Centered wordmark in top bar -- override the earlier
|
|
.skinHeader::before { bg: none } from selectserver-extend block. */
|
|
.skinHeader::before {
|
|
content: "" !important;
|
|
position: fixed !important;
|
|
top: 16px !important;
|
|
left: 50% !important;
|
|
transform: translateX(-50%) !important;
|
|
width: 156px !important;
|
|
height: 56px !important;
|
|
background-image: var(--arrflix-logo) !important;
|
|
background-repeat: no-repeat !important;
|
|
background-position: center !important;
|
|
background-size: contain !important;
|
|
z-index: 1001 !important;
|
|
pointer-events: none !important;
|
|
}
|
|
/* Kill the top-left .pageTitleWithLogo entirely -- centered
|
|
::before above replaces it. */
|
|
.pageTitleWithLogo,
|
|
.pageTitleWithDefaultLogo,
|
|
h3.pageTitle.pageTitleWithLogo,
|
|
h3.pageTitle.pageTitleWithDefaultLogo {
|
|
display: none !important;
|
|
}
|
|
|
|
/* #loginPage backdrop already handled by earlier #loginPage block
|
|
(poster-bg + vignette). Add a stronger vignette on top so the
|
|
card has more contrast. */
|
|
#loginPage::before {
|
|
content: "" !important;
|
|
position: absolute !important;
|
|
inset: 0 !important;
|
|
background:
|
|
radial-gradient(ellipse 70% 60% at center,
|
|
rgba(0,0,0,0) 0%,
|
|
rgba(0,0,0,0.55) 55%,
|
|
rgba(0,0,0,0.92) 100%),
|
|
linear-gradient(180deg, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0) 25%,
|
|
rgba(0,0,0,0) 75%, rgba(0,0,0,0.7) 100%) !important;
|
|
pointer-events: none !important;
|
|
z-index: 0 !important;
|
|
/* override the earlier blur+filter from cineplex.css #loginPage::before */
|
|
filter: none !important;
|
|
transform: none !important;
|
|
}
|
|
|
|
/* UN-hide the "Please sign in" heading (was hidden by hide-visualform
|
|
block). Re-style as Theater Sign In heading -- big, white, centered. */
|
|
#loginPage h1.sectionTitle,
|
|
#loginPage .formDialogHeader,
|
|
#loginPage .padded-left.padded-right.padded-bottom-page > h1.sectionTitle {
|
|
display: block !important;
|
|
font-family: "Bebas Neue", "Anton", Impact, "Haettenschweiler",
|
|
"Liberation Sans Narrow", "Arial Narrow Bold", sans-serif !important;
|
|
font-size: 48px !important;
|
|
color: #fff !important;
|
|
letter-spacing: 0.03em !important;
|
|
margin: 0 0 32px !important;
|
|
padding: 0 !important;
|
|
line-height: 1 !important;
|
|
text-align: center !important;
|
|
text-transform: none !important;
|
|
font-weight: 400 !important;
|
|
}
|
|
|
|
/* Form card: style the padded-left wrapper as a 540px-wide centered
|
|
dark-glass card. Override hide-visualform's flex layout to keep
|
|
center-vertical positioning. */
|
|
#loginPage .padded-left.padded-right.padded-bottom-page {
|
|
max-width: 540px !important;
|
|
width: 540px !important;
|
|
margin: 0 auto !important;
|
|
padding: 56px 56px 44px !important;
|
|
background: rgba(0, 0, 0, 0.78) !important;
|
|
border: 1px solid rgba(255,255,255,0.06) !important;
|
|
border-radius: 0 !important;
|
|
backdrop-filter: blur(8px) !important;
|
|
-webkit-backdrop-filter: blur(8px) !important;
|
|
min-height: 0 !important;
|
|
align-items: stretch !important;
|
|
box-shadow: 0 30px 80px rgba(0,0,0,0.6) !important;
|
|
}
|
|
/* Vertical center the card on the page (account for 88px top bar). */
|
|
#loginPage {
|
|
display: flex !important;
|
|
flex-direction: column !important;
|
|
align-items: center !important;
|
|
justify-content: center !important;
|
|
padding-top: 88px !important;
|
|
}
|
|
|
|
/* Form-internal: manualLoginForm + visualLoginForm full width inside card. */
|
|
#loginPage .manualLoginForm,
|
|
#loginPage form.manualLoginForm {
|
|
width: 100% !important;
|
|
max-width: 100% !important;
|
|
margin: 0 !important;
|
|
}
|
|
|
|
/* Inputs: underline-only, transparent bg, red focus. */
|
|
#loginPage .inputContainer { margin-bottom: 24px !important; }
|
|
#loginPage .inputContainer label,
|
|
#loginPage .inputLabelFocused,
|
|
#loginPage label.inputLabel {
|
|
font-family: "SF Mono", "Monaco", "Cascadia Code", "JetBrains Mono",
|
|
"Roboto Mono", "DejaVu Sans Mono", "Liberation Mono",
|
|
Consolas, monospace !important;
|
|
font-size: 11px !important;
|
|
color: rgba(255,255,255,0.55) !important;
|
|
text-transform: uppercase !important;
|
|
letter-spacing: 0.22em !important;
|
|
margin-bottom: 10px !important;
|
|
font-weight: 600 !important;
|
|
display: block !important;
|
|
}
|
|
#loginPage .emby-input {
|
|
width: 100% !important;
|
|
background: transparent !important;
|
|
border: none !important;
|
|
border-bottom: 1px solid rgba(255,255,255,0.18) !important;
|
|
border-radius: 0 !important;
|
|
padding: 12px 0 14px !important;
|
|
font-size: 18px !important;
|
|
color: #fff !important;
|
|
outline: none !important;
|
|
box-shadow: none !important;
|
|
}
|
|
#loginPage .emby-input:focus,
|
|
#loginPage .inputContainer.focused .emby-input {
|
|
border-bottom-color: #E50914 !important;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
/* Remember Me row centered. (Checkbox red already from earlier polish.) */
|
|
#loginPage .checkboxContainer,
|
|
#loginPage label.emby-checkbox-label {
|
|
display: flex !important;
|
|
align-items: center !important;
|
|
justify-content: center !important;
|
|
margin: 28px 0 30px !important;
|
|
padding: 0 !important;
|
|
}
|
|
|
|
/* Sign In button: full-width red, 4px radius, square label. */
|
|
#loginPage .raised.button-submit,
|
|
#loginPage button.raised.button-submit,
|
|
#loginPage .raised.block.emby-button[type="submit"] {
|
|
width: 100% !important;
|
|
background: #E50914 !important;
|
|
color: #fff !important;
|
|
font-size: 17px !important;
|
|
font-weight: 700 !important;
|
|
padding: 18px !important;
|
|
border: none !important;
|
|
border-radius: 4px !important;
|
|
letter-spacing: 0.04em !important;
|
|
text-transform: none !important;
|
|
margin: 0 0 0 !important;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
/* Disclaimer: centered, light grey, small. */
|
|
#loginPage .loginDisclaimer,
|
|
#loginPage .disclaimerContainer,
|
|
#loginPage .loginDisclaimerContainer {
|
|
margin-top: 28px !important;
|
|
font-size: 12px !important;
|
|
color: rgba(255,255,255,0.5) !important;
|
|
text-align: center !important;
|
|
letter-spacing: 0.04em !important;
|
|
width: 100% !important;
|
|
display: block !important;
|
|
}
|
|
"""
|
|
|
|
# Branding mirror: same selectors + rules, copied. Doesn't include the
|
|
# var(--arrflix-logo) reference issue since branding.xml already
|
|
# defines --arrflix-logo at L64.
|
|
THEATER_BRANDING = THEATER_PRECSS # identical CSS body
|
|
|
|
|
|
def patch_precss(path, marker, block):
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
s = f.read()
|
|
if marker in s:
|
|
print(f" {path}: already patched")
|
|
return
|
|
m = re.search(r'(<style id="arrflix-precss">.*?)(</style>)', s, flags=re.DOTALL)
|
|
if not m:
|
|
sys.exit(f"ERROR: arrflix-precss block not found in {path}")
|
|
s = s[:m.end(1)] + block + s[m.end(1):]
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
f.write(s)
|
|
print(f" {path}: patched (+{len(block)})")
|
|
|
|
|
|
def patch_branding(path, marker, block):
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
s = f.read()
|
|
if marker in s:
|
|
print(f" {path}: already patched")
|
|
return
|
|
CLOSE = "</CustomCss>"
|
|
if CLOSE not in s:
|
|
sys.exit(f"ERROR: </CustomCss> not found in {path}")
|
|
s = s.replace(CLOSE, block + CLOSE, 1)
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
f.write(s)
|
|
print(f" {path}: patched (+{len(block)})")
|
|
|
|
|
|
MARKER = "THE THEATER login port"
|
|
|
|
patch_precss(
|
|
"/opt/docker/jellyfin-stock/web-overrides/index.html",
|
|
MARKER, THEATER_PRECSS)
|
|
|
|
patch_branding(
|
|
"/home/docker/jellyfin-stock/config/config/branding.xml",
|
|
MARKER, THEATER_BRANDING)
|