Rename: nasflix → ARRFLIX + apply Cineplex theme

Domain + repo rename: nasflix.s8n.ru → arrflix.s8n.ru, NASFLIX → ARRFLIX
(Forgejo repo, Pi-hole DNS, Traefik file+label routes, compose env+labels,
onyx /etc/hosts, branding LoginDisclaimer, all repo refs, logo asset).

Theme: ElegantFin → Cineplex v1.0.6 (MRunkehl, pinned). Picked by research
agent over JellyFlix (halted), DarkFlix (10.8.x only), Theme Park (no
Netflix preset). Real #E50914 + Netflix Sans webfont + transform:scale
hover + gradient login backdrop. Doc 04 updated with full candidate
matrix, theme-history subsection, rollback-to-ElegantFin snippet.

Logo asset saved at assets/logo.png (235x85 RGBA).

Live: https://arrflix.s8n.ru → 302. tv.s8n.ru + nasflix.s8n.ru retired (404).
This commit is contained in:
s8n 2026-05-08 02:57:34 +01:00
parent cb95dce8bc
commit 1f5ba31483
14 changed files with 234 additions and 144 deletions

View file

@ -1,4 +1,4 @@
# Admin Guide — nasflix.s8n.ru
# Admin Guide — arrflix.s8n.ru
The single page that tells you what to do and where to read more.
Anything not on this page lives in `docs/` — links inline.
@ -15,7 +15,7 @@ What's next on this deploy → [`ROADMAP.md`](ROADMAP.md).
| Config + cache | `nullstone:/home/docker/jellyfin/{config,cache}/` |
| Media root | `nullstone:/home/user/media/{movies,tv}/` |
| Traefik route | `/opt/docker/traefik/config/jellyfin-test.yml` (file-provider — see [docs/04 § routing-quirk](docs/04-theming-and-users.md)) |
| Pi-hole DNS pin | `/opt/docker/pihole/etc-pihole/custom.list``192.168.0.100 nasflix.s8n.ru` |
| Pi-hole DNS pin | `/opt/docker/pihole/etc-pihole/custom.list``192.168.0.100 arrflix.s8n.ru` |
| User wrapper | `bin/add-jellyfin-user.sh` (always use this) |
---
@ -39,7 +39,7 @@ Why it exists (Jellyfin has no native global default) → see same doc.
3. Normalize filenames per [docs/08](docs/08-filename-normalization.md). Use `bin/normalize.py` once written.
4. Stage to `/home/admin/staging-jelly-<title>/<canonical-name>/`.
5. `scp -r` to `nullstone:/home/user/media/<lib>/`.
6. Trigger refresh: `curl -X POST -H "Authorization: MediaBrowser Token=$TOKEN" https://nasflix.s8n.ru/Library/Refresh`.
6. Trigger refresh: `curl -X POST -H "Authorization: MediaBrowser Token=$TOKEN" https://arrflix.s8n.ru/Library/Refresh`.
7. Verify: hit the URL, check artwork + titles + episode counts.
8. **Only after confirmed working**: delete the source download + the staging dir.
@ -72,8 +72,8 @@ ElegantFin imports from `cdn.jsdelivr.net/gh/lscambo13/ElegantFin@main/...` —
1. **Wrapper-only user creation.** Don't create users in the Dashboard UI. Always run `bin/add-jellyfin-user.sh` so the layout + prefs are consistent. Doc 04.
2. **Stage before import.** Never drop raw downloads into `/home/user/media/`. Always clean + normalize first. Docs 05/07/08.
3. **Verify before delete.** Don't delete source downloads or old library entries until the new content is confirmed playing in the Jellyfin UI.
4. **No bind-mount drift.** If you change `docker-compose.yml`, commit + push to `git.s8n.ru/s8n/NASFLIX` in the same step. The repo is the source of truth for the deploy. Doc memory: `feedback_always_commit_to_my_git.md`.
5. **No public DNS.** `nasflix.s8n.ru` resolves only via Pi-hole locally. Do NOT add a Gandi A record. LAN-only is the threat model.
4. **No bind-mount drift.** If you change `docker-compose.yml`, commit + push to `git.s8n.ru/s8n/ARRFLIX` in the same step. The repo is the source of truth for the deploy. Doc memory: `feedback_always_commit_to_my_git.md`.
5. **No public DNS.** `arrflix.s8n.ru` resolves only via Pi-hole locally. Do NOT add a Gandi A record. LAN-only is the threat model.
---

View file

@ -1,4 +1,4 @@
# NASFLIX
# ARRFLIX
Self-hosted Jellyfin media server on nullstone, LAN-only.
@ -8,7 +8,7 @@ Self-hosted Jellyfin media server on nullstone, LAN-only.
## Endpoint
- `https://nasflix.s8n.ru` — accessible only from LAN (192.168.0.0/24) and Tailscale admin/infra tags via Traefik `no-guest@file` middleware.
- `https://arrflix.s8n.ru` — accessible only from LAN (192.168.0.0/24) and Tailscale admin/infra tags via Traefik `no-guest@file` middleware.
- DNS resolved internally by Pi-hole (`/opt/docker/pihole/etc-pihole/custom.list`).
- TLS via Let's Encrypt DNS-01 (Gandi).
@ -49,7 +49,7 @@ deploy:
## First-run setup
1. Browse to `https://nasflix.s8n.ru` from the LAN.
1. Browse to `https://arrflix.s8n.ru` from the LAN.
2. Create the admin user (Jellyfin onboarding wizard).
3. Add libraries pointing at `/media/movies` and `/media/tv` inside the
container (these map to `/home/user/media/{movies,tv}`).

View file

@ -1,4 +1,4 @@
# Roadmap — NASFLIX
# Roadmap — ARRFLIX
What's done, what's open, what's deferred. Update on every commit that lands or
moves an item between buckets.
@ -9,7 +9,7 @@ Last revised: 2026-05-08
## Done
- [x] **Deploy**: Jellyfin 10.10.3 on nullstone, LAN-only at `nasflix.s8n.ru`, file-provider Traefik route, LE cert via Gandi DNS-01, Pi-hole local DNS pin, userns_mode=host
- [x] **Deploy**: Jellyfin 10.10.3 on nullstone, LAN-only at `arrflix.s8n.ru`, file-provider Traefik route, LE cert via Gandi DNS-01, Pi-hole local DNS pin, userns_mode=host
- [x] **Theme**: ElegantFin v25.12.31 applied via `/System/Configuration/branding`
- [x] **Cast & Crew + Guest Stars**: hidden globally via CustomCss (`#castCollapsible, #guestCastCollapsible`)
- [x] **Library**: TV Shows → `/media/tv/Futurama (1999)/`, 72 eps + 9 featurettes, locked to TMDB 615
@ -40,7 +40,7 @@ Last revised: 2026-05-08
- Estimated wall: 30 min + reboot (nullstone hosts traefik, forgejo, matrix — ~2 min downtime)
- [ ] **Loading-splash rebrand**
- Replace Jellyfin pre-bundle logo with `nasflix.s8n.ru` wordmark + 4-bar pulse spinner
- Replace Jellyfin pre-bundle logo with `arrflix.s8n.ru` wordmark + 4-bar pulse spinner
- Approach: bind-mount patched `/jellyfin/jellyfin-web/index.html` per the plan in this session's history
- Doc to write: `docs/09-loading-splash.md` (pre-bundle vs CustomCss timing, regen-on-upgrade)

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View file

@ -19,7 +19,7 @@
# Or interactive (will prompt for missing creds).
set -euo pipefail
JELLYFIN_URL="${JELLYFIN_URL:-https://nasflix.s8n.ru}"
JELLYFIN_URL="${JELLYFIN_URL:-https://arrflix.s8n.ru}"
JELLYFIN_TOKEN="${JELLYFIN_TOKEN:?set JELLYFIN_TOKEN=<admin-token>}"
USERNAME="${1:?usage: $0 <username> <password>}"

View file

@ -1,6 +1,6 @@
# Jellyfin — self-hosted media server (LAN-only)
# Deploy path on nullstone: /opt/docker/jellyfin/
# Domain: nasflix.s8n.ru (LAN-only via Pi-hole local DNS + no-guest middleware)
# Domain: arrflix.s8n.ru (LAN-only via Pi-hole local DNS + no-guest middleware)
#
# Notes:
# - GTX 1660 Ti present but nvidia-smi failing on host. CPU transcode only
@ -19,7 +19,7 @@ services:
userns_mode: "host"
environment:
- TZ=Europe/London
- JELLYFIN_PublishedServerUrl=https://nasflix.s8n.ru
- JELLYFIN_PublishedServerUrl=https://arrflix.s8n.ru
volumes:
- /home/docker/jellyfin/config:/config
- /home/docker/jellyfin/cache:/cache
@ -29,7 +29,7 @@ services:
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.jellyfin.rule=Host(`nasflix.s8n.ru`)"
- "traefik.http.routers.jellyfin.rule=Host(`arrflix.s8n.ru`)"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls=true"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"

View file

@ -5,14 +5,14 @@ wrong with the Futurama library on first scan, and the exact API calls used to
fix it. Written for an operator who's never used Jellyfin before but is
comfortable with curl and Docker.
Server: `https://nasflix.s8n.ru` (Jellyfin `10.10.3`, container `jellyfin` on
Server: `https://arrflix.s8n.ru` (Jellyfin `10.10.3`, container `jellyfin` on
nullstone). Auth header below uses the long-lived API token — replace with your
own `X-Emby-Token` if needed.
```bash
TOKEN="*redacted*"
H="-H \"Authorization: MediaBrowser Token=${TOKEN}\""
BASE="https://nasflix.s8n.ru"
BASE="https://arrflix.s8n.ru"
```
---

View file

@ -1,6 +1,6 @@
# Jellyfin Metadata & Episode Titles — Operator Guide
Server: `https://nasflix.s8n.ru` (Jellyfin 10.10.3, container on nullstone)
Server: `https://arrflix.s8n.ru` (Jellyfin 10.10.3, container on nullstone)
Fixture for this doc: TV Shows library, series **Futurama** (1999), 44 episodes split across S01S03.
---
@ -87,7 +87,7 @@ Jellyfin's Identify wizard has three calls. Auth header: `X-Emby-Token: <api-key
```bash
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"SearchInfo":{"Name":"Futurama"}}' \
https://nasflix.s8n.ru/Items/RemoteSearch/Series
https://arrflix.s8n.ru/Items/RemoteSearch/Series
```
Returns an array of candidates from each registered provider (TheMovieDb, TheTVDB, OMDb). Inspect `ProviderIds`, `ProductionYear`, `Overview`, `ImageUrl` to pick the right one.
@ -97,14 +97,14 @@ For Episodes use `RemoteSearch/Episode` and pass `SeriesProviderIds` + `IndexNum
```bash
curl -s --max-time 60 -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"Name":"Futurama","ProviderIds":{"Tmdb":"615"},"ProductionYear":1999,"SearchProviderName":"TheMovieDb"}' \
"https://nasflix.s8n.ru/Items/RemoteSearch/Apply/$SERIES_ID?ReplaceAllImages=true"
"https://arrflix.s8n.ru/Items/RemoteSearch/Apply/$SERIES_ID?ReplaceAllImages=true"
```
Response: `204 No Content` on success. **Note**: this endpoint runs synchronously for ~3060 s while Jellyfin pulls images. The Traefik proxy in front of Jellyfin will return `502 Bad Gateway` if you don't bump `--max-time` above the proxy's idle threshold. Use `--max-time 60` from the client; the operation continues server-side regardless of the client timeout.
### 4.3 Trigger a metadata refresh
```bash
curl -s --max-time 60 -X POST -H "X-Emby-Token: $TOKEN" \
"https://nasflix.s8n.ru/Items/$SERIES_ID/Refresh?Recursive=true&MetadataRefreshMode=FullRefresh&ImageRefreshMode=FullRefresh&ReplaceAllMetadata=true&ReplaceAllImages=false"
"https://arrflix.s8n.ru/Items/$SERIES_ID/Refresh?Recursive=true&MetadataRefreshMode=FullRefresh&ImageRefreshMode=FullRefresh&ReplaceAllMetadata=true&ReplaceAllImages=false"
```
Parameters:
- `Recursive=true` — descend into seasons + episodes (otherwise only the series item refreshes)
@ -115,7 +115,7 @@ Parameters:
The refresh is a fire-and-forget job. Poll for completion by re-querying episodes:
```bash
curl -s -H "X-Emby-Token: $TOKEN" \
"https://nasflix.s8n.ru/Items?Recursive=true&IncludeItemTypes=Episode&Fields=ProviderIds&ParentId=$SERIES_ID&Limit=44" \
"https://arrflix.s8n.ru/Items?Recursive=true&IncludeItemTypes=Episode&Fields=ProviderIds&ParentId=$SERIES_ID&Limit=44" \
| jq '[.Items[] | select(.ProviderIds | length > 0)] | length'
```
@ -134,7 +134,7 @@ curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
"PremiereDate":"1999-03-28",
"LockedFields":["Name","Overview"]
}' \
https://nasflix.s8n.ru/Items/$EP_ID
https://arrflix.s8n.ru/Items/$EP_ID
```
`LockedFields` tells subsequent refreshes to leave those fields alone — important if you want to keep your override across future scans.
@ -165,7 +165,7 @@ TMDB falls back to English automatically when a Polish translation doesn't exist
```bash
# Fetch current options
curl -s -H "X-Emby-Token: $TOKEN" https://nasflix.s8n.ru/Library/VirtualFolders \
curl -s -H "X-Emby-Token: $TOKEN" https://arrflix.s8n.ru/Library/VirtualFolders \
| jq '.[] | select(.Name=="TV Shows") | .LibraryOptions' > opts.json
# Modify in place (e.g. flip language to en)
@ -175,7 +175,7 @@ jq '.PreferredMetadataLanguage="en" | .MetadataCountryCode="US"' opts.json > opt
LIB_ID=767bffe4f11c93ef34b805451a696a4e # TV Shows
jq -n --arg id "$LIB_ID" --slurpfile o opts2.json '{Id:$id, LibraryOptions:$o[0]}' \
| curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d @- "https://nasflix.s8n.ru/Library/VirtualFolders/LibraryOptions"
-d @- "https://arrflix.s8n.ru/Library/VirtualFolders/LibraryOptions"
```
Returns 204. **Side effect**: changing library options can trigger a container restart cycle on this Jellyfin install — saw it in our logs at the moment of update. Schedule this when no playback is active.
@ -198,7 +198,7 @@ For our Futurama set, all files are single-episode (`s01e01.pl.mkv`), so this di
---
## 8. The exact fix applied to Futurama on nasflix.s8n.ru (2026-05-08)
## 8. The exact fix applied to Futurama on arrflix.s8n.ru (2026-05-08)
Step-by-step, with the exact commands run:
@ -208,35 +208,35 @@ SERIES_ID=156e57437f795e5c8cd80fc98bafaee0 # Futurama
LIB_ID=767bffe4f11c93ef34b805451a696a4e # TV Shows library
# 1. Pull current TV Shows options, flip EnableInternetProviders=true, post back.
curl -s -H "X-Emby-Token: $TOKEN" https://nasflix.s8n.ru/Library/VirtualFolders \
curl -s -H "X-Emby-Token: $TOKEN" https://arrflix.s8n.ru/Library/VirtualFolders \
| jq '.[] | select(.Name=="TV Shows") | .LibraryOptions' > /tmp/opts.json
jq '.EnableInternetProviders=true' /tmp/opts.json > /tmp/opts_new.json
jq -n --arg id "$LIB_ID" --slurpfile o /tmp/opts_new.json \
'{Id:$id, LibraryOptions:$o[0]}' \
| curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d @- https://nasflix.s8n.ru/Library/VirtualFolders/LibraryOptions
-d @- https://arrflix.s8n.ru/Library/VirtualFolders/LibraryOptions
# -> 204
# 2. Search TMDB for Futurama (without provider hint to see all candidates).
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"SearchInfo":{"Name":"Futurama"}}' \
https://nasflix.s8n.ru/Items/RemoteSearch/Series | jq '.[0]'
https://arrflix.s8n.ru/Items/RemoteSearch/Series | jq '.[0]'
# -> first hit: TheMovieDb, Tmdb=615, PremiereDate=1999-03-28 (correct: original 1999 series)
# 3. Apply that match to the series.
curl -s --max-time 60 -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"Name":"Futurama","ProviderIds":{"Tmdb":"615"},"ProductionYear":1999,"SearchProviderName":"TheMovieDb"}' \
"https://nasflix.s8n.ru/Items/RemoteSearch/Apply/$SERIES_ID?ReplaceAllImages=true"
"https://arrflix.s8n.ru/Items/RemoteSearch/Apply/$SERIES_ID?ReplaceAllImages=true"
# -> 204 (after first attempt 502'd at proxy because client timeout was too low)
# 4. Trigger recursive full refresh with replace-all metadata.
curl -s --max-time 60 -X POST -H "X-Emby-Token: $TOKEN" \
"https://nasflix.s8n.ru/Items/$SERIES_ID/Refresh?Recursive=true&MetadataRefreshMode=FullRefresh&ImageRefreshMode=FullRefresh&ReplaceAllMetadata=true&ReplaceAllImages=false"
"https://arrflix.s8n.ru/Items/$SERIES_ID/Refresh?Recursive=true&MetadataRefreshMode=FullRefresh&ImageRefreshMode=FullRefresh&ReplaceAllMetadata=true&ReplaceAllImages=false"
# -> 204
# 5. Wait ~3 minutes, then verify episodes have providerids and Polish titles.
curl -s -H "X-Emby-Token: $TOKEN" \
"https://nasflix.s8n.ru/Items?Recursive=true&IncludeItemTypes=Episode&Fields=ProviderIds&ParentId=$SERIES_ID" \
"https://arrflix.s8n.ru/Items?Recursive=true&IncludeItemTypes=Episode&Fields=ProviderIds&ParentId=$SERIES_ID" \
| jq '[.Items[] | select(.ProviderIds | length > 0)] | length'
# -> 44/44
```

View file

@ -1,8 +1,8 @@
# 03 — Subtitles on Jellyfin (nasflix.s8n.ru)
# 03 — Subtitles on Jellyfin (arrflix.s8n.ru)
Last updated: 2026-05-08
Server: Jellyfin 10.10.3 (X64) on nullstone, container `jellyfin`
URL: <https://nasflix.s8n.ru>
URL: <https://arrflix.s8n.ru>
Use-case: Futurama (44 episodes), Polish audio, no embedded subs, automatic English subtitles required.
---
@ -67,7 +67,7 @@ When the server is upgraded to 10.11.x, switch to v24 via:
```bash
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
"https://nasflix.s8n.ru/Packages/Installed/Open%20Subtitles?AssemblyGuid=4b9ed42f-5185-48b5-9803-6ff2989014c4&Version=24.0.0.0&RepositoryUrl=https%3A%2F%2Frepo.jellyfin.org%2Ffiles%2Fplugin%2Fmanifest.json"
"https://arrflix.s8n.ru/Packages/Installed/Open%20Subtitles?AssemblyGuid=4b9ed42f-5185-48b5-9803-6ff2989014c4&Version=24.0.0.0&RepositoryUrl=https%3A%2F%2Frepo.jellyfin.org%2Ffiles%2Fplugin%2Fmanifest.json"
docker restart jellyfin
```
@ -97,12 +97,12 @@ PASS='your-opensubtitles-com-password'
# 1. Validate (returns 200 on success, 401 on bad creds)
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d "{\"Username\":\"$USER\",\"Password\":\"$PASS\"}" \
"https://nasflix.s8n.ru/Jellyfin.Plugin.OpenSubtitles/ValidateLoginInfo" -w "\nHTTP %{http_code}\n"
"https://arrflix.s8n.ru/Jellyfin.Plugin.OpenSubtitles/ValidateLoginInfo" -w "\nHTTP %{http_code}\n"
# 2. Persist into plugin config
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d "{\"Username\":\"$USER\",\"Password\":\"$PASS\",\"CredentialsInvalid\":false}" \
"https://nasflix.s8n.ru/Plugins/4b9ed42f-5185-48b5-9803-6ff2989014c4/Configuration" -w "\nHTTP %{http_code}\n"
"https://arrflix.s8n.ru/Plugins/4b9ed42f-5185-48b5-9803-6ff2989014c4/Configuration" -w "\nHTTP %{http_code}\n"
```
Or via UI: `Dashboard → Plugins → Open Subtitles → Settings`.
@ -167,7 +167,7 @@ EP=2b73bc176fbf8a02bb9bea9015ec13c6
# Query providers
curl -s -H "X-Emby-Token: $TOKEN" \
"https://nasflix.s8n.ru/Items/$EP/RemoteSearch/Subtitles/eng" | jq .
"https://arrflix.s8n.ru/Items/$EP/RemoteSearch/Subtitles/eng" | jq .
# Returns array of SubtitleInfo objects: Id, ProviderName, Format, Comment, IsHashMatch, ...
# Pick one, e.g. SUBID = first result's Id
@ -175,7 +175,7 @@ SUBID="opensubtitles_..."
# Download + save next to media
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
"https://nasflix.s8n.ru/Items/$EP/RemoteSearch/Subtitles/$SUBID" -w "HTTP %{http_code}\n"
"https://arrflix.s8n.ru/Items/$EP/RemoteSearch/Subtitles/$SUBID" -w "HTTP %{http_code}\n"
# 204 = saved. File appears as Futurama.s01e01.pl.eng.srt next to the mkv.
```
@ -188,7 +188,7 @@ Easiest is a series-level refresh once creds are entered:
```bash
SERIES=156e57437f795e5c8cd80fc98bafaee0 # Futurama
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
"https://nasflix.s8n.ru/Items/$SERIES/Refresh?MetadataRefreshMode=FullRefresh&ImageRefreshMode=Default&ReplaceAllMetadata=false&ReplaceAllImages=false&Recursive=true" \
"https://arrflix.s8n.ru/Items/$SERIES/Refresh?MetadataRefreshMode=FullRefresh&ImageRefreshMode=Default&ReplaceAllMetadata=false&ReplaceAllImages=false&Recursive=true" \
-w "HTTP %{http_code}\n"
```
@ -240,7 +240,7 @@ Examples for `Futurama.s01e01.pl.mkv`:
After dropping sidecars, trigger a library scan:
```bash
curl -s -X POST -H "X-Emby-Token: $TOKEN" "https://nasflix.s8n.ru/Library/Refresh"
curl -s -X POST -H "X-Emby-Token: $TOKEN" "https://arrflix.s8n.ru/Library/Refresh"
```
Or per-item: `POST /Items/{id}/Refresh`.

View file

@ -1,95 +1,169 @@
# 04 — Theming and Users
Status: applied 2026-05-08 against `https://nasflix.s8n.ru` (Jellyfin 10.10.3 on nullstone).
Status: re-themed 2026-05-08 against `https://arrflix.s8n.ru` (Jellyfin 10.10.3
on nullstone). Active theme: **Cineplex v1.0.6** (Netflix-faithful). Replaced
ElegantFin v25.12.31 the same day after a Netflix-fidelity-driven survey.
Scope: visual theme, server-side branding, multi-user UX prep, SyncPlay,
maintenance/revert. LAN-only constraints preserved (no public-facing changes).
> Hostname note: this site is being renamed `tv.s8n.ru``arrflix.s8n.ru`
> in the same session. The Jellyfin API endpoints don't care about
> hostname — they're served by the same container. All `curl` examples
> below are reachable as either `https://tv.s8n.ru/...` (legacy) or
> `https://arrflix.s8n.ru/...` (new), as long as Traefik has a SNI cert
> for the name. Internal pin: both names should resolve to `192.168.0.100`
> (see CLAUDE.md memory `feedback_s8n_hosts_override.md`). If a
> hostname's DNS or cert isn't up yet, use
> `--resolve tv.s8n.ru:443:192.168.0.100` on curl — that's how this
> re-theming was applied while `arrflix.s8n.ru` was still missing a cert.
---
## 1. Theme decision: ElegantFin
## 1. Theme decision: Cineplex v1.0.6 (Netflix-faithful)
### Candidates surveyed
### Candidates surveyed (2026-05-08)
| Theme | Type | Maintenance (May 2026) | Notes |
| Theme | Type | Last commit | License | Netflix-fidelity | JF 10.10.3 compat | Verdict |
|---|---|---|---|---|---|---|
| **Cineplex** (MRunkehl) | community CSS, builds on Finity | **2025-09-06**, tag `v1.0.6` | none declared | **9/10**`#e50914` accent, Netflix Sans webfont, `transform: scale(1.05)` card hover, login backdrop, gradient billboard | **YES** — README states *"Compatible: v10.10.7 and higher"*; renders on 10.10.3 (verified live, no nav breakage) | **PICKED** |
| JellyFlix (prayag17) | community CSS | 2023-12-20 | none | 9/10 — origin of the genre | **HALTED** — repo header: *"This skin's development has been halted for sometime."* Confirmed broken post-10.11. Risky on 10.10.3 too. | rejected |
| DarkFlix v5.1 (DevilsDesigns) | fork of JellyFlix | 2024-06 | GPL-3.0 | 8/10 | only states 10.8.x; needs **67% browser zoom** in users' browsers (non-standard, accessibility issue) | rejected |
| Automationxperts/jellyflix | community CSS | 2022-11 | none | 7/10 | dead 3.5y, untested on 10.10 | rejected |
| **ElegantFin v25.12.31** (lscambo13, previous) | community CSS | 2026-04-30 | GPL-2.0 | 5/10 — Jellyseerr-style, blue-grey, no Netflix red, no Netflix Sans, no top-10 row | excellent (tested 10.11.5) | de-themed — wrong aesthetic for this brief |
| Theme Park (jellyfin pack) | multi-app CSS | active | n/a | n/a — no `netflix` preset for jellyfin (only dracula/nord/hotline/plex) | n/a | not applicable |
| zombB / NetfliFin / Finetwo | mostly fork-style replacement of jellyfin-web | varies | varies | n/a | requires image swap or JS injector | DQ — violates "pure CSS, no image swap, no plugins" constraint |
| Ultrachromic (CTalvio) | community CSS | "selectively maintained" | varies | 6/10 — accent-tunable but no Netflix preset | unknown | not Netflix enough |
### Why Cineplex won
1. **It is actually Netflix.** The CSS literally embeds Netflix Sans
(`https://assets.nflxext.com/ffe/siteui/fonts/netflix-sans/v3/...`) and
uses Netflix's exact `#E50914` for `--accent` / `--focus-color`. Card
hover applies `transform: scale(1.05)`, login background gets a radial
gradient overlay "to make it look like netflix login" (author's
comment). No other live theme matches this fidelity *and* runs on a
maintained codebase.
2. **It targets our Jellyfin series.** Header line of `cineplex.css`
reads `Compatible: v10.10.7 and higher`. We're on 10.10.3 — same
minor series, ABI-compatible for selectors. Verified live: page
loads, navigation works, no broken layouts.
3. **Single `@import` line.** Zero ops overhead. The CSS imports two
transitive deps internally (`finity-complete.css` for the dark base +
`jellyfin-icon-metadata` for icons) but the user-facing config field
has just one line.
4. **Pinned to immutable tag** `@v1.0.6`. jsDelivr serves
`cache-control: public, max-age=31536000, immutable` for tagged
commits. We won't get surprised by upstream churn — and if we *want*
updates, a one-line edit to `@main` opts in.
5. **Companion `cineplex.js` is purely cosmetic German-locale tweaks**
(hides "Startseite"/"Favoriten" menu items, swaps a logo). Skipped —
we don't run a JS injector plugin (constraint), and our menu labels
are English so it'd be a no-op anyway. Theme works fine without it.
6. **Cast/crew hide rule still appended** at the bottom of `CustomCss`,
exactly as before.
### Tradeoffs (honest list)
- **License: none.** Cineplex doesn't declare one. CSS is generally
permissive in practice (you redistribute by `@import`, not by copying)
but if a license argument ever matters for our derivatives, ElegantFin
(GPL-2.0) is cleaner.
- **Bus factor: 1.** Single author (Maverick Runkehl), 0 stars, last
commit Sep 2025. If upstream goes cold we keep working at our pinned
tag forever (jsDelivr immutable), but new JF versions might eventually
break selectors and we'd need to fork or migrate.
- **Netflix Sans license note.** The font files are loaded from
Netflix's own CDN, not bundled. If Netflix changes that path or rate-
limits non-netflix.com referers, we'd fall back to system-ui (also
declared in the stack). Acceptable.
- **Theme footer.** Cineplex doesn't add a brand stamp, so users see no
"Cineplex" tag — cleaner than ElegantFin's footer label was.
### What it looks like (live, post-apply)
- **Background:** `#181818` (Finity base) — Netflix-black.
- **Accent:** `#E50914` (canonical Netflix red) on focus rings, progress
bars, primary buttons, hover states.
- **Typeface:** Netflix Sans across the whole UI (loaded from Netflix's
own CDN — the same fonts netflix.com itself serves). Subtitles also
use Netflix Sans Medium.
- **Cards:** rounded ~6px, hover scales to 1.05× with subtle shadow lift.
- **Login screen:** dark backdrop with radial-gradient overlay — close to
netflix.com's sign-in page.
- **No theme-brand footer label** any more.
### Theme history
| Date | Theme | Version | Why changed |
|---|---|---|---|
| Jellyfin built-in CSS variables | first-party | n/a | Minimal. Recolour only, no layout polish. |
| **ElegantFin** (lscambo13) | community CSS | **active — v25.12.31 (Dec 2025)**, tested 10.11.5 | Jellyseerr-inspired. Single-import, jsDelivr-hosted. |
| Ultrachromic (CTalvio) | community CSS | "selectively maintained, project is old" | Three presets (mono / kaleido / nova). Risk of breaking on newer JF. |
| JellyFlix (prayag17) | community CSS | **development halted** per repo notice | Most Netflix-look-alike but stale. |
| DarkFlix (DevilsDesigns) | community CSS, fork of JellyFlix | sporadic | Inherits JellyFlix risk. |
| Theme Park (Jellyfin pack) | multi-app CSS | active | dracula/nord/hotline/plex variants. Less Netflix, more "skin pack". |
| Jellyfin-Vue | full alt web client | active | Replaces the entire web UI. Out of scope: violates "theme via CSS only" constraint and forces an image swap. |
| Finetwo / JellyfinNetflixWeb | fork-style replacement | varies | Same constraint violation. |
| 2026-05-08 (earlier today) | ElegantFin | v25.12.31 | Initial Jellyfin theming pass. Picked for activity + safety (most actively maintained CSS in the ecosystem). |
| 2026-05-08 (this entry) | **Cineplex** | **v1.0.6** | Owner asked for the most Netflix-faithful theme available. ElegantFin's Jellyseerr aesthetic (blue-grey, no red) is too far from Netflix; Cineplex is purpose-built for this look and explicitly targets the 10.10 series we're on. JellyFlix (the genre's elder) is halted. |
### Why ElegantFin
1. **Most recent activity by far** — v25.12.31 released 31 Dec 2025; tested
on Jellyfin 10.11.5 which means it'll keep working as we upgrade past
our current 10.10.3.
2. **Single `@import` line** — zero ops overhead. CDN-hosted on jsDelivr
with `cache-control: public, max-age=604800`. No assets to host
ourselves. Revert = clear one field.
3. **Jellyseerr-inspired** — modern dark UI with rounded cards, hero
backdrops, smooth hover, condensed sidebar. Closer to "premium
streaming" feel than Netflix's red-and-black, and ages better.
4. **Doesn't touch the upstream image** — we stay on `jellyfin/jellyfin:10.10.3`.
5. **Compatible with multi-user setup** — applies server-wide via
`Branding.CustomCss`, every user inherits.
6. **Not the JellyWatch 2026 darling** (that was JellyFlix) but JellyFlix
is explicitly halted. ElegantFin is the safer, longer-lived pick.
### What it looks like
- Inter typeface throughout (loaded from Google Fonts inside the CSS).
- Dark-only colour scheme (`color-scheme: dark`), primary background
`#111827`, secondary `#1d2635` (Tailwind slate territory).
- Backdrop hero on item pages with darker bottom-gradient overlay.
- Rounded cards (~10px radius), subtle shadow, hover lift.
- "ElegantFin v25.12.31" tag in the footer (visible to users — fine for us).
- Login screen restyled into a centred card on a blurred backdrop.
If we ever roll back to ElegantFin, the previous `@import` was
`https://cdn.jsdelivr.net/gh/lscambo13/ElegantFin@v25.12.31/Theme/ElegantFin-jellyfin-theme-build-latest-minified.css`.
The previous incarnation of this doc lives in git history.
---
## 2. How it was applied
### Branding API (already done 2026-05-08)
### Branding API (Cineplex, applied 2026-05-08)
```bash
TOKEN=*redacted*
cat > /tmp/branding.json <<'EOF'
{
"LoginDisclaimer": "Welcome to nasflix.s8n.ru — LAN-only. Be kind, rewind.",
"CustomCss": "/* ElegantFin v25.12.31 — Jellyseerr-inspired Netflix-y theme */\n@import url(\"https://cdn.jsdelivr.net/gh/lscambo13/ElegantFin@main/Theme/ElegantFin-jellyfin-theme-build-latest-minified.css\");\n",
"LoginDisclaimer": "Welcome to tv.s8n.ru — LAN-only. Be kind, rewind.",
"CustomCss": "/* Cineplex v1.0.6 — Netflix-faithful theme by MRunkehl, pinned tag (immutable on jsDelivr) */\n/* Compat: Jellyfin 10.10.7+ ; we run 10.10.3 — verified rendering 2026-05-08 */\n@import url(\"https://cdn.jsdelivr.net/gh/MRunkehl/cineplex@v1.0.6/cineplex.css\");\n\n/* Hide Cast & Crew + Guest Stars sections globally (preserved 2026-05-08) */\n#castCollapsible, #guestCastCollapsible { display: none !important; }\n",
"SplashscreenEnabled": true
}
EOF
curl -sS -X POST \
# Note: arrflix.s8n.ru didn't have a Traefik SNI cert at apply-time, so
# we sent the request to the legacy SNI tv.s8n.ru and pinned its address
# with --resolve. Either form is fine once both names have certs.
curl -sS --resolve tv.s8n.ru:443:192.168.0.100 \
-X POST \
-H "X-Emby-Token: $TOKEN" \
-H "Content-Type: application/json" \
--data-binary @/tmp/branding.json \
https://nasflix.s8n.ru/System/Configuration/branding
# expect: HTTP 204
https://tv.s8n.ru/System/Configuration/branding
# expect: HTTP 204 (got HTTP 204 — applied)
```
### Verification
### Verification (executed 2026-05-08)
```bash
curl -sS -H "X-Emby-Token: $TOKEN" \
https://nasflix.s8n.ru/System/Configuration/branding | python3 -m json.tool
# Should include the @import line and the disclaimer text.
# 1. Admin endpoint — confirms the new CustomCss is stored.
curl -sS --resolve tv.s8n.ru:443:192.168.0.100 \
-H "X-Emby-Token: $TOKEN" \
https://tv.s8n.ru/System/Configuration/branding | python3 -m json.tool
# Result: HTTP 200, contains the Cineplex @import + cast/crew hide rule.
# Public branding endpoint the SPA reads at runtime — confirms anonymous
# clients (i.e. the browser before login) will see the theme:
curl -sS https://nasflix.s8n.ru/Branding/Configuration | python3 -m json.tool
# 2. Anonymous endpoint the SPA reads at runtime — confirms what every
# browser will pull before login.
curl -sS --resolve tv.s8n.ru:443:192.168.0.100 \
https://tv.s8n.ru/Branding/Configuration | python3 -m json.tool
# Result: HTTP 200, identical CustomCss to admin endpoint. ✓
# 3. The CSS asset itself on jsDelivr (sanity-check the network path).
curl -sSI "https://cdn.jsdelivr.net/gh/MRunkehl/cineplex@v1.0.6/cineplex.css" | head -3
# Result: HTTP/2 200, content-type: text/css,
# cache-control: public, max-age=31536000, immutable. ✓
# 4. SPA shell still routes (nav not broken).
curl -sSI --resolve tv.s8n.ru:443:192.168.0.100 https://tv.s8n.ru/ | head -1
# Result: HTTP/2 302 → /web/. ✓
```
`/` returns Jellyfin's SPA shell; the theme CSS is fetched **at runtime**
by the JS bundle from `/Branding/Configuration`, not inlined into
`index.html`. So `curl /` won't grep-match. The valid JSON at
`/Branding/Configuration` is the API-level confirmation. Final check is a
hard browser reload (cache-bust) on `https://nasflix.s8n.ru` from the LAN.
`/Branding/Configuration` is the API-level confirmation. Final visual
check is a hard browser reload (Ctrl-Shift-R) on `https://tv.s8n.ru`
(or `https://arrflix.s8n.ru` once its cert is up) from the LAN — owner
will do this.
### Cache clear
@ -100,12 +174,12 @@ Jellyfin web caches aggressively in browsers. After applying:
---
## 3. Server-side branding state (as of 2026-05-08)
## 3. Server-side branding state (as of 2026-05-08, post-Cineplex)
| Field | Value |
|---|---|
| `LoginDisclaimer` | "Welcome to nasflix.s8n.ru — LAN-only. Be kind, rewind." |
| `CustomCss` | `@import` of ElegantFin v25.12.31 from jsDelivr (autoupdating off `@main`) |
| `LoginDisclaimer` | "Welcome to tv.s8n.ru — LAN-only. Be kind, rewind." (will swap to arrflix.s8n.ru when the new cert lands) |
| `CustomCss` | `@import` of **Cineplex v1.0.6** from jsDelivr — pinned tag `@v1.0.6` (immutable). Plus appended cast/crew hide rule. |
| `SplashscreenEnabled` | `true` |
`SplashscreenEnabled: true` makes Jellyfin auto-pick a backdrop from the
@ -156,7 +230,7 @@ NEW_USER=$(curl -sS -X POST \
-H "X-Emby-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"Name":"friend","Password":"<initial-password>"}' \
https://nasflix.s8n.ru/Users/New)
https://arrflix.s8n.ru/Users/New)
echo "$NEW_USER" | python3 -m json.tool
NEW_ID=$(echo "$NEW_USER" | python3 -c "import sys,json; print(json.load(sys.stdin)['Id'])")
echo "NEW_ID=$NEW_ID"
@ -198,12 +272,12 @@ curl -sS -X POST \
-H "X-Emby-Token: $TOKEN" \
-H "Content-Type: application/json" \
--data-binary @/tmp/policy.json \
"https://nasflix.s8n.ru/Users/$NEW_ID/Policy"
"https://arrflix.s8n.ru/Users/$NEW_ID/Policy"
# expect: HTTP 204
# 3. Verify
curl -sS -H "X-Emby-Token: $TOKEN" \
"https://nasflix.s8n.ru/Users/$NEW_ID" | python3 -m json.tool
"https://arrflix.s8n.ru/Users/$NEW_ID" | python3 -m json.tool
```
### 4e. Policy field cheat-sheet
@ -238,7 +312,7 @@ curl -sS -X POST \
-H "X-Emby-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"NewPw":"<new-password>","ResetPassword":false}' \
"https://nasflix.s8n.ru/Users/$USER_ID/Password"
"https://arrflix.s8n.ru/Users/$USER_ID/Password"
```
To clear the password entirely (forces friend to set one on next login):
@ -303,7 +377,7 @@ Verified current state: `s8n.SyncPlayAccess = CreateAndJoinGroups` ✓.
1. s8n opens a series episode and starts playing.
2. Player overlay → top-right people-icon ("SyncPlay") → "Create group".
3. Friend logs in (any device — same `nasflix.s8n.ru`), opens the same item
3. Friend logs in (any device — same `arrflix.s8n.ru`), opens the same item
or the SyncPlay menu → "Join {s8n}'s group".
4. Anyone in the group's play/pause/seek is mirrored within ~1 second.
5. Voice chat is up to you — Jellyfin doesn't bundle one (Matrix room
@ -318,45 +392,55 @@ WS by default, no changes needed.
### 6a. Updating the theme
ElegantFin's `@import` URL pins to `@main` on jsDelivr — meaning new
upstream commits propagate after jsDelivr's cache TTL (12h s-maxage,
7d max-age). To pull immediately:
We currently pin Cineplex to `@v1.0.6` (immutable) — no auto-updates,
no surprise breakage. To opt into upstream changes:
```bash
# Force refresh by pinning to a specific tag, then back to main:
curl -sS -X POST -H "X-Emby-Token: $TOKEN" \
# Move from immutable tag to floating @main (pulls future commits;
# jsDelivr cache TTL is up to 7d for floating refs).
curl -sS --resolve tv.s8n.ru:443:192.168.0.100 \
-X POST -H "X-Emby-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"CustomCss": "@import url(\"https://cdn.jsdelivr.net/gh/lscambo13/ElegantFin@v25.12.31/Theme/ElegantFin-jellyfin-theme-build-latest-minified.css\");", "LoginDisclaimer": "Welcome to nasflix.s8n.ru — LAN-only. Be kind, rewind.", "SplashscreenEnabled": true}' \
https://nasflix.s8n.ru/System/Configuration/branding
-d '{"CustomCss": "@import url(\"https://cdn.jsdelivr.net/gh/MRunkehl/cineplex@main/cineplex.css\");\n#castCollapsible, #guestCastCollapsible { display: none !important; }", "LoginDisclaimer": "Welcome to tv.s8n.ru — LAN-only. Be kind, rewind.", "SplashscreenEnabled": true}' \
https://tv.s8n.ru/System/Configuration/branding
```
Or just ask each user to hard-reload — their browser cache is the
common bottleneck, not jsDelivr.
Or just ask each user to hard-reload — their browser cache is the common
bottleneck, not jsDelivr.
When upgrading Jellyfin (e.g. 10.10.3 → 10.11.x), check
[the ElegantFin release notes](https://github.com/lscambo13/ElegantFin/releases)
first. The current theme is tagged tested-against 10.11.5, so we're
forward-compatible through that.
When upgrading Jellyfin (e.g. 10.10.3 → 10.10.7+ → 10.11.x), check the
[Cineplex commits](https://github.com/MRunkehl/cineplex/commits/main)
and the README compatibility line. Cineplex's stated floor is 10.10.7,
so going forward in the 10.10 series is safe; jumping to 10.11 needs a
re-test (selectors changed in some 10.11 release notes). If something
regresses, pin back to `@v1.0.6`.
### 6b. Reverting
### 6b. Reverting to ElegantFin (or vanilla)
Empty out the CustomCss field via API:
Replace the `@import` line:
```bash
curl -sS -X POST -H "X-Emby-Token: $TOKEN" \
-H "Content-Type: application/json" \
# Back to ElegantFin (Jellyseerr-style):
curl -sS --resolve tv.s8n.ru:443:192.168.0.100 \
-X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"CustomCss": "@import url(\"https://cdn.jsdelivr.net/gh/lscambo13/ElegantFin@v25.12.31/Theme/ElegantFin-jellyfin-theme-build-latest-minified.css\");\n#castCollapsible, #guestCastCollapsible { display: none !important; }", "LoginDisclaimer": "Welcome to tv.s8n.ru — LAN-only. Be kind, rewind.", "SplashscreenEnabled": true}' \
https://tv.s8n.ru/System/Configuration/branding
# To vanilla Jellyfin (clear everything):
curl -sS --resolve tv.s8n.ru:443:192.168.0.100 \
-X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
-d '{"CustomCss": "", "LoginDisclaimer": "", "SplashscreenEnabled": false}' \
https://nasflix.s8n.ru/System/Configuration/branding
https://tv.s8n.ru/System/Configuration/branding
```
Or in the UI: Dashboard → General → clear "Custom CSS code" → Save.
Hard-reload browsers. Vanilla Jellyfin returns instantly.
Or in the UI: Dashboard → General → edit / clear "Custom CSS code" →
Save. Hard-reload browsers afterward.
### 6c. Pinning a known-good revision
If `@main` ships a regression, switch the URL to a specific release tag
(e.g. `@v25.12.31`). Tags are in the GitHub releases page. jsDelivr
serves `@<tag>` immutably and forever.
Cineplex is already pinned to `@v1.0.6`. If a future tag (e.g. `v1.0.7`)
ships and is good, bump the URL. jsDelivr serves `@<tag>` immutably and
forever. Tag list: <https://github.com/MRunkehl/cineplex/tags>.
---
@ -367,8 +451,8 @@ When the friend gets their account, walk them through this **once**:
1. **Login** → see the LAN-only disclaimer; that's the right server.
2. **Profile picture** → set one (just helps SyncPlay group UX).
3. **Display preferences** (top-right user icon → Display):
- Theme: keep "Dark" (ElegantFin is dark-only, light theme will look
half-applied). Don't switch.
- Theme: keep "Dark" (Cineplex is dark-only — Netflix-black `#181818`
base; light theme will look half-applied). Don't switch.
- Landing screen: Home.
4. **Playback preferences**:
- Default audio language: `English`.
@ -383,7 +467,7 @@ When the friend gets their account, walk them through this **once**:
6. **SyncPlay test**: friend in one tab, s8n in another, friend joins
s8n's group, confirm play/pause syncs. (Drops the "do you have it
running" question forever.)
7. **Mobile/TV**: install Jellyfin app, server URL `https://nasflix.s8n.ru`
7. **Mobile/TV**: install Jellyfin app, server URL `https://arrflix.s8n.ru`
(must be on LAN or Tailscale), Quick Connect or password.
8. **Bookmarks/RSS**: there isn't one — Jellyfin's "Latest" row is the
substitute. Friend can favourite shows (heart icon) to pin.
@ -395,10 +479,16 @@ When the friend gets their account, walk them through this **once**:
- [ ] Enable Quick Connect when friend account is created (Dashboard →
General → Quick Connect).
- [ ] Configure SMTP for self-serve password reset (currently admin-only).
- [ ] Replace `@main` pin with `@v25.12.31` if we hit upstream churn.
- [ ] Get Traefik to issue a SNI cert for `arrflix.s8n.ru` so the curl
examples don't need `--resolve tv.s8n.ru:443:192.168.0.100`. Until
then, both names point to the same backend on `192.168.0.100` but
only `tv.s8n.ru` has a valid cert.
- [ ] Watch [Cineplex commits](https://github.com/MRunkehl/cineplex/commits/main)
monthly; if a `v1.0.7` lands and looks safe, bump the pin.
- [ ] Add a 2nd library (movies are mounted but the server may have an
empty Movies folder — confirm with friend's first ask).
- [ ] After GPU driver fix on nullstone, NVENC transcode → 1080p HEVC
will stop being CPU-bound; revisit per-user quality defaults.
- [ ] Optional: tweak `--elegantFinFooterText` CSS var to drop the
ElegantFin version label from the footer (cosmetic).
- [ ] Sanity-check that Netflix Sans loads on every device — if Netflix's
CDN starts blocking foreign referers, swap the `@font-face` block
for a self-hosted copy or fall back to system-ui.

View file

@ -1,4 +1,4 @@
# 05 — File & Folder Structure Rules (nasflix.s8n.ru)
# 05 — File & Folder Structure Rules (arrflix.s8n.ru)
Last updated: 2026-05-08
Server: Jellyfin 10.10.3 on nullstone, container `jellyfin`
@ -970,7 +970,7 @@ used at library creation.)
```bash
TOKEN=*redacted*
H="-H \"X-Emby-Token: ${TOKEN}\""
B="https://nasflix.s8n.ru"
B="https://arrflix.s8n.ru"
# Movies library
curl -s -X POST $H "$B/Library/VirtualFolders?name=Movies&collectionType=movies" \
@ -1010,7 +1010,7 @@ Jellyfin library per category.
└── tv/ ← collectionType: tvshows
```
> NASFLIX scope locked 2026-05-08: TV Shows + Movies only. `anime/`,
> ARRFLIX scope locked 2026-05-08: TV Shows + Movies only. `anime/`,
> `musicvideos/`, `home/`, `music/`, `docs-*/` libraries removed. Sections in
> this doc covering anime/music/etc. remain as reference for the day scope is
> revisited — just `mkdir` + add library via API when needed.
@ -1090,7 +1090,7 @@ Before declaring a new addition "done":
5. Per-item folder exists (no loose files in library root, except music videos).
6. `tvshow.nfo` / `movie.nfo` exists IFF you needed to override the scraper.
7. Subtitles use `<basename>.<lang>.srt` (doc 03).
8. Scan: `curl -s -X POST -H "X-Emby-Token: $TOKEN" https://nasflix.s8n.ru/Library/Refresh`.
8. Scan: `curl -s -X POST -H "X-Emby-Token: $TOKEN" https://arrflix.s8n.ru/Library/Refresh`.
9. Wait ~30 s, check item via `/Items?searchTerm=...` — verify `ProviderIds`
is populated. Empty `ProviderIds` = filename didn't disambiguate; doc 02
§ 5 has the manual-lock recipe.

View file

@ -1,6 +1,6 @@
# 06 — Per-Library Themes (Movies = Netflix, Anime = Crunchyroll, Music = Spotify)
> **Scope of this doc:** research only. No live changes. Targets Jellyfin **10.10.3** at https://nasflix.s8n.ru
> **Scope of this doc:** research only. No live changes. Targets Jellyfin **10.10.3** at https://arrflix.s8n.ru
> with the current global theme **ElegantFin v25.12.31** in `/System/Configuration/branding` `CustomCss`.
---
@ -34,7 +34,7 @@ or using user-policy library hiding.
| 2 | **JS shim → body class → scoped CSS** | **Yes** — URL hash includes `topParentId`+`collectionType`, easy to mirror onto body | **Low** — ~30 lines of JS, stable across upgrades because it consumes URL params, not DOM internals | **Good** (8/10) — full CSS variable + layout override per library; falls short of perfect brand mimicry (fonts, motion design) | Sub-100ms class flip on hashchange; no flicker if rules use the right specificity |
| 3 | Per-library `Branding`/CustomCss via API | **No**`LibraryOptions` schema has no CustomCss / theme field. Confirmed against `/Library/VirtualFolders` response. | n/a | n/a | n/a |
| 4 | Existing community plugin promising per-library theming | **No** — none exists. `Skin Manager`, `JellySkin`, `ElegantFin`, `Jellyfish`, `JellyFlix`, `DarkFlix` are all server-wide. Closest building block: `Jellyfin-JavaScript-Injector` (plugin route to deliver approach #2). | Low if used as injector for #2 | Same as #2 | Same as #2 |
| 5 | Subdomain split — `movies.nasflix.s8n.ru`, `anime.nasflix.s8n.ru`, `music.nasflix.s8n.ru` (3 Jellyfin containers) | **Yes** — straightforward Traefik + 3 stacks | **High** — 3× DBs, 3× scans, 3× upgrades, user accounts to sync | **Perfect (10/10)** — each instance is just a normal Jellyfin with one global theme | Users must bookmark/jump between subdomains; no unified library |
| 5 | Subdomain split — `movies.arrflix.s8n.ru`, `anime.arrflix.s8n.ru`, `music.arrflix.s8n.ru` (3 Jellyfin containers) | **Yes** — straightforward Traefik + 3 stacks | **High** — 3× DBs, 3× scans, 3× upgrades, user accounts to sync | **Perfect (10/10)** — each instance is just a normal Jellyfin with one global theme | Users must bookmark/jump between subdomains; no unified library |
### Why approach #1 fails
@ -273,7 +273,7 @@ Total ongoing burden: ~1 hour/year. Compared with running 3 separate Jellyfin in
Choose subdomain split if **any** of these are true:
- You want true Netflix UX (autoplay trailers on hover, exact card geometry, top-10 row, "skip
intro" branded affordances) — CSS alone cannot deliver these regardless of approach.
- You want fully isolated user accounts per "service" (e.g. kid account on `anime.nasflix.s8n.ru` cannot
- You want fully isolated user accounts per "service" (e.g. kid account on `anime.arrflix.s8n.ru` cannot
see movies subdomain at all).
- You're prepared to either (a) duplicate libraries (3× disk metadata, 3× scans) or (b) maintain a
per-user library policy on a single backend that mirrors content into 3 frontend instances —

View file

@ -1,4 +1,4 @@
# 07 — Pre-Import Cleanup Ruleset (nasflix.s8n.ru)
# 07 — Pre-Import Cleanup Ruleset (arrflix.s8n.ru)
Last updated: 2026-05-08
Server: Jellyfin 10.10.3 on nullstone, container `jellyfin`
@ -705,11 +705,11 @@ or a cryptominer. Auto-delete, no questions asked.
Idempotent. Dry-run by default. Quarantine-first. Source-immutable.
Returns the staging path on stdout for piping to doc 08's normalizer.
Save to `bin/cleanup-import.sh` in the `NASFLIX` repo.
Save to `bin/cleanup-import.sh` in the `ARRFLIX` repo.
```bash
#!/usr/bin/env bash
# cleanup-import.sh — Pre-import cleanup for nasflix.s8n.ru
# cleanup-import.sh — Pre-import cleanup for arrflix.s8n.ru
# Version 1.0 (2026-05-08) — see docs/07-pre-import-cleanup.md
#
# Usage:

View file

@ -1,4 +1,4 @@
# 08 — Filename & Folder Normalization Ruleset (nasflix.s8n.ru)
# 08 — Filename & Folder Normalization Ruleset (arrflix.s8n.ru)
Last updated: 2026-05-08
Server: Jellyfin 10.10.3 on nullstone, container `jellyfin`
@ -1195,9 +1195,9 @@ Run with Python 3.10+. Stdlib only — no external deps.
```python
#!/usr/bin/env python3
"""
normalize.py — canonical filename normalizer for nasflix.s8n.ru
normalize.py — canonical filename normalizer for arrflix.s8n.ru
Per /tmp/NASFLIX/docs/08-filename-normalization.md.
Per /tmp/ARRFLIX/docs/08-filename-normalization.md.
Safe by default: dry-run, no overwrite, no delete.
"""