Rename: tv.s8n.ru → nasflix.s8n.ru, jellyfin-stack → NASFLIX
- Domain: tv.s8n.ru retired (404). nasflix.s8n.ru live (302 → /web). Pi-hole local DNS updated. Traefik file-provider router rule + docker-label router rule both flipped. Jellyfin PublishedServerUrl env updated. Cert re-issued via Gandi DNS-01. Onyx /etc/hosts pin moved. - Repo: forgejo PATCH /api/v1/repos rename. Local clone remote URL updated. All in-tree refs to tv.s8n.ru and jellyfin-stack swept (sed). - Scope: TV Shows + Movies only. anime/, musicvideos/, home/, music/, docs-*/ libraries removed from canonical layout. Sections kept as reference for re-introduction. - Branding LoginDisclaimer text updated to nasflix.s8n.ru.
This commit is contained in:
parent
ab16861314
commit
cb95dce8bc
13 changed files with 77 additions and 92 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
# Admin Guide — tv.s8n.ru
|
# Admin Guide — nasflix.s8n.ru
|
||||||
|
|
||||||
The single page that tells you what to do and where to read more.
|
The single page that tells you what to do and where to read more.
|
||||||
Anything not on this page lives in `docs/` — links inline.
|
Anything not on this page lives in `docs/` — links inline.
|
||||||
|
|
@ -13,9 +13,9 @@ What's next on this deploy → [`ROADMAP.md`](ROADMAP.md).
|
||||||
|---|---|
|
|---|---|
|
||||||
| Compose file | `docker-compose.yml` (this repo) → deployed at `nullstone:/opt/docker/jellyfin/` |
|
| Compose file | `docker-compose.yml` (this repo) → deployed at `nullstone:/opt/docker/jellyfin/` |
|
||||||
| Config + cache | `nullstone:/home/docker/jellyfin/{config,cache}/` |
|
| Config + cache | `nullstone:/home/docker/jellyfin/{config,cache}/` |
|
||||||
| Media root | `nullstone:/home/user/media/{movies,tv,anime,musicvideos}/` |
|
| 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)) |
|
| 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 tv.s8n.ru` |
|
| Pi-hole DNS pin | `/opt/docker/pihole/etc-pihole/custom.list` → `192.168.0.100 nasflix.s8n.ru` |
|
||||||
| User wrapper | `bin/add-jellyfin-user.sh` (always use this) |
|
| 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.
|
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>/`.
|
4. Stage to `/home/admin/staging-jelly-<title>/<canonical-name>/`.
|
||||||
5. `scp -r` to `nullstone:/home/user/media/<lib>/`.
|
5. `scp -r` to `nullstone:/home/user/media/<lib>/`.
|
||||||
6. Trigger refresh: `curl -X POST -H "Authorization: MediaBrowser Token=$TOKEN" https://tv.s8n.ru/Library/Refresh`.
|
6. Trigger refresh: `curl -X POST -H "Authorization: MediaBrowser Token=$TOKEN" https://nasflix.s8n.ru/Library/Refresh`.
|
||||||
7. Verify: hit the URL, check artwork + titles + episode counts.
|
7. Verify: hit the URL, check artwork + titles + episode counts.
|
||||||
8. **Only after confirmed working**: delete the source download + the staging dir.
|
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.
|
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.
|
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.
|
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/jellyfin-stack` in the same step. The repo is the source of truth for the deploy. Doc memory: `feedback_always_commit_to_my_git.md`.
|
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.** `tv.s8n.ru` resolves only via Pi-hole locally. Do NOT add a Gandi A record. LAN-only is the threat model.
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# jellyfin-stack
|
# NASFLIX
|
||||||
|
|
||||||
Self-hosted Jellyfin media server on nullstone, LAN-only.
|
Self-hosted Jellyfin media server on nullstone, LAN-only.
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@ Self-hosted Jellyfin media server on nullstone, LAN-only.
|
||||||
|
|
||||||
## Endpoint
|
## Endpoint
|
||||||
|
|
||||||
- `https://tv.s8n.ru` — accessible only from LAN (192.168.0.0/24) and Tailscale admin/infra tags via Traefik `no-guest@file` middleware.
|
- `https://nasflix.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`).
|
- DNS resolved internally by Pi-hole (`/opt/docker/pihole/etc-pihole/custom.list`).
|
||||||
- TLS via Let's Encrypt DNS-01 (Gandi).
|
- TLS via Let's Encrypt DNS-01 (Gandi).
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ deploy:
|
||||||
|
|
||||||
## First-run setup
|
## First-run setup
|
||||||
|
|
||||||
1. Browse to `https://tv.s8n.ru` from the LAN.
|
1. Browse to `https://nasflix.s8n.ru` from the LAN.
|
||||||
2. Create the admin user (Jellyfin onboarding wizard).
|
2. Create the admin user (Jellyfin onboarding wizard).
|
||||||
3. Add libraries pointing at `/media/movies` and `/media/tv` inside the
|
3. Add libraries pointing at `/media/movies` and `/media/tv` inside the
|
||||||
container (these map to `/home/user/media/{movies,tv}`).
|
container (these map to `/home/user/media/{movies,tv}`).
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Roadmap — jellyfin-stack
|
# Roadmap — NASFLIX
|
||||||
|
|
||||||
What's done, what's open, what's deferred. Update on every commit that lands or
|
What's done, what's open, what's deferred. Update on every commit that lands or
|
||||||
moves an item between buckets.
|
moves an item between buckets.
|
||||||
|
|
@ -9,7 +9,7 @@ Last revised: 2026-05-08
|
||||||
|
|
||||||
## Done
|
## Done
|
||||||
|
|
||||||
- [x] **Deploy**: Jellyfin 10.10.3 on nullstone, LAN-only at `tv.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 `nasflix.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] **Theme**: ElegantFin v25.12.31 applied via `/System/Configuration/branding`
|
||||||
- [x] **Cast & Crew + Guest Stars**: hidden globally via CustomCss (`#castCollapsible, #guestCastCollapsible`)
|
- [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
|
- [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)
|
- Estimated wall: 30 min + reboot (nullstone hosts traefik, forgejo, matrix — ~2 min downtime)
|
||||||
|
|
||||||
- [ ] **Loading-splash rebrand**
|
- [ ] **Loading-splash rebrand**
|
||||||
- Replace Jellyfin pre-bundle logo with `tv.s8n.ru` wordmark + 4-bar pulse spinner
|
- Replace Jellyfin pre-bundle logo with `nasflix.s8n.ru` wordmark + 4-bar pulse spinner
|
||||||
- Approach: bind-mount patched `/jellyfin/jellyfin-web/index.html` per the plan in this session's history
|
- 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)
|
- Doc to write: `docs/09-loading-splash.md` (pre-bundle vs CustomCss timing, regen-on-upgrade)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
# Or interactive (will prompt for missing creds).
|
# Or interactive (will prompt for missing creds).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
JELLYFIN_URL="${JELLYFIN_URL:-https://tv.s8n.ru}"
|
JELLYFIN_URL="${JELLYFIN_URL:-https://nasflix.s8n.ru}"
|
||||||
JELLYFIN_TOKEN="${JELLYFIN_TOKEN:?set JELLYFIN_TOKEN=<admin-token>}"
|
JELLYFIN_TOKEN="${JELLYFIN_TOKEN:?set JELLYFIN_TOKEN=<admin-token>}"
|
||||||
|
|
||||||
USERNAME="${1:?usage: $0 <username> <password>}"
|
USERNAME="${1:?usage: $0 <username> <password>}"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Jellyfin — self-hosted media server (LAN-only)
|
# Jellyfin — self-hosted media server (LAN-only)
|
||||||
# Deploy path on nullstone: /opt/docker/jellyfin/
|
# Deploy path on nullstone: /opt/docker/jellyfin/
|
||||||
# Domain: tv.s8n.ru (LAN-only via Pi-hole local DNS + no-guest middleware)
|
# Domain: nasflix.s8n.ru (LAN-only via Pi-hole local DNS + no-guest middleware)
|
||||||
#
|
#
|
||||||
# Notes:
|
# Notes:
|
||||||
# - GTX 1660 Ti present but nvidia-smi failing on host. CPU transcode only
|
# - GTX 1660 Ti present but nvidia-smi failing on host. CPU transcode only
|
||||||
|
|
@ -19,7 +19,7 @@ services:
|
||||||
userns_mode: "host"
|
userns_mode: "host"
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/London
|
- TZ=Europe/London
|
||||||
- JELLYFIN_PublishedServerUrl=https://tv.s8n.ru
|
- JELLYFIN_PublishedServerUrl=https://nasflix.s8n.ru
|
||||||
volumes:
|
volumes:
|
||||||
- /home/docker/jellyfin/config:/config
|
- /home/docker/jellyfin/config:/config
|
||||||
- /home/docker/jellyfin/cache:/cache
|
- /home/docker/jellyfin/cache:/cache
|
||||||
|
|
@ -29,7 +29,7 @@ services:
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=proxy"
|
- "traefik.docker.network=proxy"
|
||||||
- "traefik.http.routers.jellyfin.rule=Host(`tv.s8n.ru`)"
|
- "traefik.http.routers.jellyfin.rule=Host(`nasflix.s8n.ru`)"
|
||||||
- "traefik.http.routers.jellyfin.entrypoints=websecure"
|
- "traefik.http.routers.jellyfin.entrypoints=websecure"
|
||||||
- "traefik.http.routers.jellyfin.tls=true"
|
- "traefik.http.routers.jellyfin.tls=true"
|
||||||
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
|
||||||
|
|
|
||||||
|
|
@ -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
|
fix it. Written for an operator who's never used Jellyfin before but is
|
||||||
comfortable with curl and Docker.
|
comfortable with curl and Docker.
|
||||||
|
|
||||||
Server: `https://tv.s8n.ru` (Jellyfin `10.10.3`, container `jellyfin` on
|
Server: `https://nasflix.s8n.ru` (Jellyfin `10.10.3`, container `jellyfin` on
|
||||||
nullstone). Auth header below uses the long-lived API token — replace with your
|
nullstone). Auth header below uses the long-lived API token — replace with your
|
||||||
own `X-Emby-Token` if needed.
|
own `X-Emby-Token` if needed.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
TOKEN="*redacted*"
|
TOKEN="*redacted*"
|
||||||
H="-H \"Authorization: MediaBrowser Token=${TOKEN}\""
|
H="-H \"Authorization: MediaBrowser Token=${TOKEN}\""
|
||||||
BASE="https://tv.s8n.ru"
|
BASE="https://nasflix.s8n.ru"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Jellyfin Metadata & Episode Titles — Operator Guide
|
# Jellyfin Metadata & Episode Titles — Operator Guide
|
||||||
|
|
||||||
Server: `https://tv.s8n.ru` (Jellyfin 10.10.3, container on nullstone)
|
Server: `https://nasflix.s8n.ru` (Jellyfin 10.10.3, container on nullstone)
|
||||||
Fixture for this doc: TV Shows library, series **Futurama** (1999), 44 episodes split across S01–S03.
|
Fixture for this doc: TV Shows library, series **Futurama** (1999), 44 episodes split across S01–S03.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -87,7 +87,7 @@ Jellyfin's Identify wizard has three calls. Auth header: `X-Emby-Token: <api-key
|
||||||
```bash
|
```bash
|
||||||
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||||
-d '{"SearchInfo":{"Name":"Futurama"}}' \
|
-d '{"SearchInfo":{"Name":"Futurama"}}' \
|
||||||
https://tv.s8n.ru/Items/RemoteSearch/Series
|
https://nasflix.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.
|
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
|
```bash
|
||||||
curl -s --max-time 60 -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
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"}' \
|
-d '{"Name":"Futurama","ProviderIds":{"Tmdb":"615"},"ProductionYear":1999,"SearchProviderName":"TheMovieDb"}' \
|
||||||
"https://tv.s8n.ru/Items/RemoteSearch/Apply/$SERIES_ID?ReplaceAllImages=true"
|
"https://nasflix.s8n.ru/Items/RemoteSearch/Apply/$SERIES_ID?ReplaceAllImages=true"
|
||||||
```
|
```
|
||||||
Response: `204 No Content` on success. **Note**: this endpoint runs synchronously for ~30–60 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.
|
Response: `204 No Content` on success. **Note**: this endpoint runs synchronously for ~30–60 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
|
### 4.3 Trigger a metadata refresh
|
||||||
```bash
|
```bash
|
||||||
curl -s --max-time 60 -X POST -H "X-Emby-Token: $TOKEN" \
|
curl -s --max-time 60 -X POST -H "X-Emby-Token: $TOKEN" \
|
||||||
"https://tv.s8n.ru/Items/$SERIES_ID/Refresh?Recursive=true&MetadataRefreshMode=FullRefresh&ImageRefreshMode=FullRefresh&ReplaceAllMetadata=true&ReplaceAllImages=false"
|
"https://nasflix.s8n.ru/Items/$SERIES_ID/Refresh?Recursive=true&MetadataRefreshMode=FullRefresh&ImageRefreshMode=FullRefresh&ReplaceAllMetadata=true&ReplaceAllImages=false"
|
||||||
```
|
```
|
||||||
Parameters:
|
Parameters:
|
||||||
- `Recursive=true` — descend into seasons + episodes (otherwise only the series item refreshes)
|
- `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:
|
The refresh is a fire-and-forget job. Poll for completion by re-querying episodes:
|
||||||
```bash
|
```bash
|
||||||
curl -s -H "X-Emby-Token: $TOKEN" \
|
curl -s -H "X-Emby-Token: $TOKEN" \
|
||||||
"https://tv.s8n.ru/Items?Recursive=true&IncludeItemTypes=Episode&Fields=ProviderIds&ParentId=$SERIES_ID&Limit=44" \
|
"https://nasflix.s8n.ru/Items?Recursive=true&IncludeItemTypes=Episode&Fields=ProviderIds&ParentId=$SERIES_ID&Limit=44" \
|
||||||
| jq '[.Items[] | select(.ProviderIds | length > 0)] | length'
|
| 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",
|
"PremiereDate":"1999-03-28",
|
||||||
"LockedFields":["Name","Overview"]
|
"LockedFields":["Name","Overview"]
|
||||||
}' \
|
}' \
|
||||||
https://tv.s8n.ru/Items/$EP_ID
|
https://nasflix.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.
|
`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
|
```bash
|
||||||
# Fetch current options
|
# Fetch current options
|
||||||
curl -s -H "X-Emby-Token: $TOKEN" https://tv.s8n.ru/Library/VirtualFolders \
|
curl -s -H "X-Emby-Token: $TOKEN" https://nasflix.s8n.ru/Library/VirtualFolders \
|
||||||
| jq '.[] | select(.Name=="TV Shows") | .LibraryOptions' > opts.json
|
| jq '.[] | select(.Name=="TV Shows") | .LibraryOptions' > opts.json
|
||||||
|
|
||||||
# Modify in place (e.g. flip language to en)
|
# 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
|
LIB_ID=767bffe4f11c93ef34b805451a696a4e # TV Shows
|
||||||
jq -n --arg id "$LIB_ID" --slurpfile o opts2.json '{Id:$id, LibraryOptions:$o[0]}' \
|
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" \
|
| curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||||
-d @- "https://tv.s8n.ru/Library/VirtualFolders/LibraryOptions"
|
-d @- "https://nasflix.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.
|
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 tv.s8n.ru (2026-05-08)
|
## 8. The exact fix applied to Futurama on nasflix.s8n.ru (2026-05-08)
|
||||||
|
|
||||||
Step-by-step, with the exact commands run:
|
Step-by-step, with the exact commands run:
|
||||||
|
|
||||||
|
|
@ -208,35 +208,35 @@ SERIES_ID=156e57437f795e5c8cd80fc98bafaee0 # Futurama
|
||||||
LIB_ID=767bffe4f11c93ef34b805451a696a4e # TV Shows library
|
LIB_ID=767bffe4f11c93ef34b805451a696a4e # TV Shows library
|
||||||
|
|
||||||
# 1. Pull current TV Shows options, flip EnableInternetProviders=true, post back.
|
# 1. Pull current TV Shows options, flip EnableInternetProviders=true, post back.
|
||||||
curl -s -H "X-Emby-Token: $TOKEN" https://tv.s8n.ru/Library/VirtualFolders \
|
curl -s -H "X-Emby-Token: $TOKEN" https://nasflix.s8n.ru/Library/VirtualFolders \
|
||||||
| jq '.[] | select(.Name=="TV Shows") | .LibraryOptions' > /tmp/opts.json
|
| jq '.[] | select(.Name=="TV Shows") | .LibraryOptions' > /tmp/opts.json
|
||||||
jq '.EnableInternetProviders=true' /tmp/opts.json > /tmp/opts_new.json
|
jq '.EnableInternetProviders=true' /tmp/opts.json > /tmp/opts_new.json
|
||||||
jq -n --arg id "$LIB_ID" --slurpfile o /tmp/opts_new.json \
|
jq -n --arg id "$LIB_ID" --slurpfile o /tmp/opts_new.json \
|
||||||
'{Id:$id, LibraryOptions:$o[0]}' \
|
'{Id:$id, LibraryOptions:$o[0]}' \
|
||||||
| curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
| curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||||
-d @- https://tv.s8n.ru/Library/VirtualFolders/LibraryOptions
|
-d @- https://nasflix.s8n.ru/Library/VirtualFolders/LibraryOptions
|
||||||
# -> 204
|
# -> 204
|
||||||
|
|
||||||
# 2. Search TMDB for Futurama (without provider hint to see all candidates).
|
# 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" \
|
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||||
-d '{"SearchInfo":{"Name":"Futurama"}}' \
|
-d '{"SearchInfo":{"Name":"Futurama"}}' \
|
||||||
https://tv.s8n.ru/Items/RemoteSearch/Series | jq '.[0]'
|
https://nasflix.s8n.ru/Items/RemoteSearch/Series | jq '.[0]'
|
||||||
# -> first hit: TheMovieDb, Tmdb=615, PremiereDate=1999-03-28 (correct: original 1999 series)
|
# -> first hit: TheMovieDb, Tmdb=615, PremiereDate=1999-03-28 (correct: original 1999 series)
|
||||||
|
|
||||||
# 3. Apply that match to the 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" \
|
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"}' \
|
-d '{"Name":"Futurama","ProviderIds":{"Tmdb":"615"},"ProductionYear":1999,"SearchProviderName":"TheMovieDb"}' \
|
||||||
"https://tv.s8n.ru/Items/RemoteSearch/Apply/$SERIES_ID?ReplaceAllImages=true"
|
"https://nasflix.s8n.ru/Items/RemoteSearch/Apply/$SERIES_ID?ReplaceAllImages=true"
|
||||||
# -> 204 (after first attempt 502'd at proxy because client timeout was too low)
|
# -> 204 (after first attempt 502'd at proxy because client timeout was too low)
|
||||||
|
|
||||||
# 4. Trigger recursive full refresh with replace-all metadata.
|
# 4. Trigger recursive full refresh with replace-all metadata.
|
||||||
curl -s --max-time 60 -X POST -H "X-Emby-Token: $TOKEN" \
|
curl -s --max-time 60 -X POST -H "X-Emby-Token: $TOKEN" \
|
||||||
"https://tv.s8n.ru/Items/$SERIES_ID/Refresh?Recursive=true&MetadataRefreshMode=FullRefresh&ImageRefreshMode=FullRefresh&ReplaceAllMetadata=true&ReplaceAllImages=false"
|
"https://nasflix.s8n.ru/Items/$SERIES_ID/Refresh?Recursive=true&MetadataRefreshMode=FullRefresh&ImageRefreshMode=FullRefresh&ReplaceAllMetadata=true&ReplaceAllImages=false"
|
||||||
# -> 204
|
# -> 204
|
||||||
|
|
||||||
# 5. Wait ~3 minutes, then verify episodes have providerids and Polish titles.
|
# 5. Wait ~3 minutes, then verify episodes have providerids and Polish titles.
|
||||||
curl -s -H "X-Emby-Token: $TOKEN" \
|
curl -s -H "X-Emby-Token: $TOKEN" \
|
||||||
"https://tv.s8n.ru/Items?Recursive=true&IncludeItemTypes=Episode&Fields=ProviderIds&ParentId=$SERIES_ID" \
|
"https://nasflix.s8n.ru/Items?Recursive=true&IncludeItemTypes=Episode&Fields=ProviderIds&ParentId=$SERIES_ID" \
|
||||||
| jq '[.Items[] | select(.ProviderIds | length > 0)] | length'
|
| jq '[.Items[] | select(.ProviderIds | length > 0)] | length'
|
||||||
# -> 44/44
|
# -> 44/44
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# 03 — Subtitles on Jellyfin (tv.s8n.ru)
|
# 03 — Subtitles on Jellyfin (nasflix.s8n.ru)
|
||||||
|
|
||||||
Last updated: 2026-05-08
|
Last updated: 2026-05-08
|
||||||
Server: Jellyfin 10.10.3 (X64) on nullstone, container `jellyfin`
|
Server: Jellyfin 10.10.3 (X64) on nullstone, container `jellyfin`
|
||||||
URL: <https://tv.s8n.ru>
|
URL: <https://nasflix.s8n.ru>
|
||||||
Use-case: Futurama (44 episodes), Polish audio, no embedded subs, automatic English subtitles required.
|
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
|
```bash
|
||||||
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
|
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
|
||||||
"https://tv.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://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"
|
||||||
docker restart jellyfin
|
docker restart jellyfin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -97,12 +97,12 @@ PASS='your-opensubtitles-com-password'
|
||||||
# 1. Validate (returns 200 on success, 401 on bad creds)
|
# 1. Validate (returns 200 on success, 401 on bad creds)
|
||||||
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||||
-d "{\"Username\":\"$USER\",\"Password\":\"$PASS\"}" \
|
-d "{\"Username\":\"$USER\",\"Password\":\"$PASS\"}" \
|
||||||
"https://tv.s8n.ru/Jellyfin.Plugin.OpenSubtitles/ValidateLoginInfo" -w "\nHTTP %{http_code}\n"
|
"https://nasflix.s8n.ru/Jellyfin.Plugin.OpenSubtitles/ValidateLoginInfo" -w "\nHTTP %{http_code}\n"
|
||||||
|
|
||||||
# 2. Persist into plugin config
|
# 2. Persist into plugin config
|
||||||
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||||
-d "{\"Username\":\"$USER\",\"Password\":\"$PASS\",\"CredentialsInvalid\":false}" \
|
-d "{\"Username\":\"$USER\",\"Password\":\"$PASS\",\"CredentialsInvalid\":false}" \
|
||||||
"https://tv.s8n.ru/Plugins/4b9ed42f-5185-48b5-9803-6ff2989014c4/Configuration" -w "\nHTTP %{http_code}\n"
|
"https://nasflix.s8n.ru/Plugins/4b9ed42f-5185-48b5-9803-6ff2989014c4/Configuration" -w "\nHTTP %{http_code}\n"
|
||||||
```
|
```
|
||||||
|
|
||||||
Or via UI: `Dashboard → Plugins → Open Subtitles → Settings`.
|
Or via UI: `Dashboard → Plugins → Open Subtitles → Settings`.
|
||||||
|
|
@ -167,7 +167,7 @@ EP=2b73bc176fbf8a02bb9bea9015ec13c6
|
||||||
|
|
||||||
# Query providers
|
# Query providers
|
||||||
curl -s -H "X-Emby-Token: $TOKEN" \
|
curl -s -H "X-Emby-Token: $TOKEN" \
|
||||||
"https://tv.s8n.ru/Items/$EP/RemoteSearch/Subtitles/eng" | jq .
|
"https://nasflix.s8n.ru/Items/$EP/RemoteSearch/Subtitles/eng" | jq .
|
||||||
# Returns array of SubtitleInfo objects: Id, ProviderName, Format, Comment, IsHashMatch, ...
|
# Returns array of SubtitleInfo objects: Id, ProviderName, Format, Comment, IsHashMatch, ...
|
||||||
|
|
||||||
# Pick one, e.g. SUBID = first result's Id
|
# Pick one, e.g. SUBID = first result's Id
|
||||||
|
|
@ -175,7 +175,7 @@ SUBID="opensubtitles_..."
|
||||||
|
|
||||||
# Download + save next to media
|
# Download + save next to media
|
||||||
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
|
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
|
||||||
"https://tv.s8n.ru/Items/$EP/RemoteSearch/Subtitles/$SUBID" -w "HTTP %{http_code}\n"
|
"https://nasflix.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.
|
# 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
|
```bash
|
||||||
SERIES=156e57437f795e5c8cd80fc98bafaee0 # Futurama
|
SERIES=156e57437f795e5c8cd80fc98bafaee0 # Futurama
|
||||||
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
|
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
|
||||||
"https://tv.s8n.ru/Items/$SERIES/Refresh?MetadataRefreshMode=FullRefresh&ImageRefreshMode=Default&ReplaceAllMetadata=false&ReplaceAllImages=false&Recursive=true" \
|
"https://nasflix.s8n.ru/Items/$SERIES/Refresh?MetadataRefreshMode=FullRefresh&ImageRefreshMode=Default&ReplaceAllMetadata=false&ReplaceAllImages=false&Recursive=true" \
|
||||||
-w "HTTP %{http_code}\n"
|
-w "HTTP %{http_code}\n"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -240,7 +240,7 @@ Examples for `Futurama.s01e01.pl.mkv`:
|
||||||
After dropping sidecars, trigger a library scan:
|
After dropping sidecars, trigger a library scan:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -s -X POST -H "X-Emby-Token: $TOKEN" "https://tv.s8n.ru/Library/Refresh"
|
curl -s -X POST -H "X-Emby-Token: $TOKEN" "https://nasflix.s8n.ru/Library/Refresh"
|
||||||
```
|
```
|
||||||
|
|
||||||
Or per-item: `POST /Items/{id}/Refresh`.
|
Or per-item: `POST /Items/{id}/Refresh`.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# 04 — Theming and Users
|
# 04 — Theming and Users
|
||||||
|
|
||||||
Status: applied 2026-05-08 against `https://tv.s8n.ru` (Jellyfin 10.10.3 on nullstone).
|
Status: applied 2026-05-08 against `https://nasflix.s8n.ru` (Jellyfin 10.10.3 on nullstone).
|
||||||
Scope: visual theme, server-side branding, multi-user UX prep, SyncPlay,
|
Scope: visual theme, server-side branding, multi-user UX prep, SyncPlay,
|
||||||
maintenance/revert. LAN-only constraints preserved (no public-facing changes).
|
maintenance/revert. LAN-only constraints preserved (no public-facing changes).
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ TOKEN=*redacted*
|
||||||
|
|
||||||
cat > /tmp/branding.json <<'EOF'
|
cat > /tmp/branding.json <<'EOF'
|
||||||
{
|
{
|
||||||
"LoginDisclaimer": "Welcome to tv.s8n.ru — LAN-only. Be kind, rewind.",
|
"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",
|
"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",
|
||||||
"SplashscreenEnabled": true
|
"SplashscreenEnabled": true
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +69,7 @@ curl -sS -X POST \
|
||||||
-H "X-Emby-Token: $TOKEN" \
|
-H "X-Emby-Token: $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
--data-binary @/tmp/branding.json \
|
--data-binary @/tmp/branding.json \
|
||||||
https://tv.s8n.ru/System/Configuration/branding
|
https://nasflix.s8n.ru/System/Configuration/branding
|
||||||
# expect: HTTP 204
|
# expect: HTTP 204
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -77,19 +77,19 @@ curl -sS -X POST \
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -sS -H "X-Emby-Token: $TOKEN" \
|
curl -sS -H "X-Emby-Token: $TOKEN" \
|
||||||
https://tv.s8n.ru/System/Configuration/branding | python3 -m json.tool
|
https://nasflix.s8n.ru/System/Configuration/branding | python3 -m json.tool
|
||||||
# Should include the @import line and the disclaimer text.
|
# Should include the @import line and the disclaimer text.
|
||||||
|
|
||||||
# Public branding endpoint the SPA reads at runtime — confirms anonymous
|
# Public branding endpoint the SPA reads at runtime — confirms anonymous
|
||||||
# clients (i.e. the browser before login) will see the theme:
|
# clients (i.e. the browser before login) will see the theme:
|
||||||
curl -sS https://tv.s8n.ru/Branding/Configuration | python3 -m json.tool
|
curl -sS https://nasflix.s8n.ru/Branding/Configuration | python3 -m json.tool
|
||||||
```
|
```
|
||||||
|
|
||||||
`/` returns Jellyfin's SPA shell; the theme CSS is fetched **at runtime**
|
`/` returns Jellyfin's SPA shell; the theme CSS is fetched **at runtime**
|
||||||
by the JS bundle from `/Branding/Configuration`, not inlined into
|
by the JS bundle from `/Branding/Configuration`, not inlined into
|
||||||
`index.html`. So `curl /` won't grep-match. The valid JSON at
|
`index.html`. So `curl /` won't grep-match. The valid JSON at
|
||||||
`/Branding/Configuration` is the API-level confirmation. Final check is a
|
`/Branding/Configuration` is the API-level confirmation. Final check is a
|
||||||
hard browser reload (cache-bust) on `https://tv.s8n.ru` from the LAN.
|
hard browser reload (cache-bust) on `https://nasflix.s8n.ru` from the LAN.
|
||||||
|
|
||||||
### Cache clear
|
### Cache clear
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ Jellyfin web caches aggressively in browsers. After applying:
|
||||||
|
|
||||||
| Field | Value |
|
| Field | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `LoginDisclaimer` | "Welcome to tv.s8n.ru — LAN-only. Be kind, rewind." |
|
| `LoginDisclaimer` | "Welcome to nasflix.s8n.ru — LAN-only. Be kind, rewind." |
|
||||||
| `CustomCss` | `@import` of ElegantFin v25.12.31 from jsDelivr (autoupdating off `@main`) |
|
| `CustomCss` | `@import` of ElegantFin v25.12.31 from jsDelivr (autoupdating off `@main`) |
|
||||||
| `SplashscreenEnabled` | `true` |
|
| `SplashscreenEnabled` | `true` |
|
||||||
|
|
||||||
|
|
@ -156,7 +156,7 @@ NEW_USER=$(curl -sS -X POST \
|
||||||
-H "X-Emby-Token: $TOKEN" \
|
-H "X-Emby-Token: $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"Name":"friend","Password":"<initial-password>"}' \
|
-d '{"Name":"friend","Password":"<initial-password>"}' \
|
||||||
https://tv.s8n.ru/Users/New)
|
https://nasflix.s8n.ru/Users/New)
|
||||||
echo "$NEW_USER" | python3 -m json.tool
|
echo "$NEW_USER" | python3 -m json.tool
|
||||||
NEW_ID=$(echo "$NEW_USER" | python3 -c "import sys,json; print(json.load(sys.stdin)['Id'])")
|
NEW_ID=$(echo "$NEW_USER" | python3 -c "import sys,json; print(json.load(sys.stdin)['Id'])")
|
||||||
echo "NEW_ID=$NEW_ID"
|
echo "NEW_ID=$NEW_ID"
|
||||||
|
|
@ -198,12 +198,12 @@ curl -sS -X POST \
|
||||||
-H "X-Emby-Token: $TOKEN" \
|
-H "X-Emby-Token: $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
--data-binary @/tmp/policy.json \
|
--data-binary @/tmp/policy.json \
|
||||||
"https://tv.s8n.ru/Users/$NEW_ID/Policy"
|
"https://nasflix.s8n.ru/Users/$NEW_ID/Policy"
|
||||||
# expect: HTTP 204
|
# expect: HTTP 204
|
||||||
|
|
||||||
# 3. Verify
|
# 3. Verify
|
||||||
curl -sS -H "X-Emby-Token: $TOKEN" \
|
curl -sS -H "X-Emby-Token: $TOKEN" \
|
||||||
"https://tv.s8n.ru/Users/$NEW_ID" | python3 -m json.tool
|
"https://nasflix.s8n.ru/Users/$NEW_ID" | python3 -m json.tool
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4e. Policy field cheat-sheet
|
### 4e. Policy field cheat-sheet
|
||||||
|
|
@ -238,7 +238,7 @@ curl -sS -X POST \
|
||||||
-H "X-Emby-Token: $TOKEN" \
|
-H "X-Emby-Token: $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"NewPw":"<new-password>","ResetPassword":false}' \
|
-d '{"NewPw":"<new-password>","ResetPassword":false}' \
|
||||||
"https://tv.s8n.ru/Users/$USER_ID/Password"
|
"https://nasflix.s8n.ru/Users/$USER_ID/Password"
|
||||||
```
|
```
|
||||||
|
|
||||||
To clear the password entirely (forces friend to set one on next login):
|
To clear the password entirely (forces friend to set one on next login):
|
||||||
|
|
@ -303,7 +303,7 @@ Verified current state: `s8n.SyncPlayAccess = CreateAndJoinGroups` ✓.
|
||||||
|
|
||||||
1. s8n opens a series episode and starts playing.
|
1. s8n opens a series episode and starts playing.
|
||||||
2. Player overlay → top-right people-icon ("SyncPlay") → "Create group".
|
2. Player overlay → top-right people-icon ("SyncPlay") → "Create group".
|
||||||
3. Friend logs in (any device — same `tv.s8n.ru`), opens the same item
|
3. Friend logs in (any device — same `nasflix.s8n.ru`), opens the same item
|
||||||
or the SyncPlay menu → "Join {s8n}'s group".
|
or the SyncPlay menu → "Join {s8n}'s group".
|
||||||
4. Anyone in the group's play/pause/seek is mirrored within ~1 second.
|
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
|
5. Voice chat is up to you — Jellyfin doesn't bundle one (Matrix room
|
||||||
|
|
@ -326,8 +326,8 @@ upstream commits propagate after jsDelivr's cache TTL (12h s-maxage,
|
||||||
# Force refresh by pinning to a specific tag, then back to main:
|
# Force refresh by pinning to a specific tag, then back to main:
|
||||||
curl -sS -X POST -H "X-Emby-Token: $TOKEN" \
|
curl -sS -X POST -H "X-Emby-Token: $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-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 tv.s8n.ru — LAN-only. Be kind, rewind.", "SplashscreenEnabled": true}' \
|
-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://tv.s8n.ru/System/Configuration/branding
|
https://nasflix.s8n.ru/System/Configuration/branding
|
||||||
```
|
```
|
||||||
|
|
||||||
Or just ask each user to hard-reload — their browser cache is the
|
Or just ask each user to hard-reload — their browser cache is the
|
||||||
|
|
@ -346,7 +346,7 @@ Empty out the CustomCss field via API:
|
||||||
curl -sS -X POST -H "X-Emby-Token: $TOKEN" \
|
curl -sS -X POST -H "X-Emby-Token: $TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"CustomCss": "", "LoginDisclaimer": "", "SplashscreenEnabled": false}' \
|
-d '{"CustomCss": "", "LoginDisclaimer": "", "SplashscreenEnabled": false}' \
|
||||||
https://tv.s8n.ru/System/Configuration/branding
|
https://nasflix.s8n.ru/System/Configuration/branding
|
||||||
```
|
```
|
||||||
|
|
||||||
Or in the UI: Dashboard → General → clear "Custom CSS code" → Save.
|
Or in the UI: Dashboard → General → clear "Custom CSS code" → Save.
|
||||||
|
|
@ -383,7 +383,7 @@ When the friend gets their account, walk them through this **once**:
|
||||||
6. **SyncPlay test**: friend in one tab, s8n in another, friend joins
|
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
|
s8n's group, confirm play/pause syncs. (Drops the "do you have it
|
||||||
running" question forever.)
|
running" question forever.)
|
||||||
7. **Mobile/TV**: install Jellyfin app, server URL `https://tv.s8n.ru`
|
7. **Mobile/TV**: install Jellyfin app, server URL `https://nasflix.s8n.ru`
|
||||||
(must be on LAN or Tailscale), Quick Connect or password.
|
(must be on LAN or Tailscale), Quick Connect or password.
|
||||||
8. **Bookmarks/RSS**: there isn't one — Jellyfin's "Latest" row is the
|
8. **Bookmarks/RSS**: there isn't one — Jellyfin's "Latest" row is the
|
||||||
substitute. Friend can favourite shows (heart icon) to pin.
|
substitute. Friend can favourite shows (heart icon) to pin.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# 05 — File & Folder Structure Rules (tv.s8n.ru)
|
# 05 — File & Folder Structure Rules (nasflix.s8n.ru)
|
||||||
|
|
||||||
Last updated: 2026-05-08
|
Last updated: 2026-05-08
|
||||||
Server: Jellyfin 10.10.3 on nullstone, container `jellyfin`
|
Server: Jellyfin 10.10.3 on nullstone, container `jellyfin`
|
||||||
|
|
@ -970,7 +970,7 @@ used at library creation.)
|
||||||
```bash
|
```bash
|
||||||
TOKEN=*redacted*
|
TOKEN=*redacted*
|
||||||
H="-H \"X-Emby-Token: ${TOKEN}\""
|
H="-H \"X-Emby-Token: ${TOKEN}\""
|
||||||
B="https://tv.s8n.ru"
|
B="https://nasflix.s8n.ru"
|
||||||
|
|
||||||
# Movies library
|
# Movies library
|
||||||
curl -s -X POST $H "$B/Library/VirtualFolders?name=Movies&collectionType=movies" \
|
curl -s -X POST $H "$B/Library/VirtualFolders?name=Movies&collectionType=movies" \
|
||||||
|
|
@ -1007,17 +1007,13 @@ Jellyfin library per category.
|
||||||
```
|
```
|
||||||
/home/user/media/
|
/home/user/media/
|
||||||
├── movies/ ← collectionType: movies
|
├── movies/ ← collectionType: movies
|
||||||
├── tv/ ← collectionType: tvshows
|
└── tv/ ← collectionType: tvshows
|
||||||
├── anime/ ← collectionType: tvshows (separate library)
|
|
||||||
├── musicvideos/ ← collectionType: musicvideos
|
|
||||||
├── docs-movies/ ← collectionType: movies (future, optional)
|
|
||||||
└── docs-tv/ ← collectionType: tvshows (future, optional)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> `home/` and `music/` libraries removed from the canonical layout per owner
|
> NASFLIX scope locked 2026-05-08: TV Shows + Movies only. `anime/`,
|
||||||
> decision 2026-05-08. Sections 6 (Music) and the Home-videos category remain
|
> `musicvideos/`, `home/`, `music/`, `docs-*/` libraries removed. Sections in
|
||||||
> in this doc as reference for re-introduction later — just `mkdir` + add
|
> this doc covering anime/music/etc. remain as reference for the day scope is
|
||||||
> library via API when needed.
|
> revisited — just `mkdir` + add library via API when needed.
|
||||||
|
|
||||||
### 13.2 Why Architecture A (not B or C)
|
### 13.2 Why Architecture A (not B or C)
|
||||||
|
|
||||||
|
|
@ -1045,8 +1041,6 @@ or client-facing change needed at migration time.
|
||||||
**A wins because:**
|
**A wins because:**
|
||||||
|
|
||||||
- One library per `collectionType` is the simplest correct mapping.
|
- One library per `collectionType` is the simplest correct mapping.
|
||||||
- Anime as its own library lets us set provider order and language
|
|
||||||
preference independently from `tv/`.
|
|
||||||
|
|
||||||
### 13.3 Concrete mkdir commands
|
### 13.3 Concrete mkdir commands
|
||||||
|
|
||||||
|
|
@ -1056,9 +1050,7 @@ by `user:user`):
|
||||||
```bash
|
```bash
|
||||||
ssh user@192.168.0.100 'mkdir -p \
|
ssh user@192.168.0.100 'mkdir -p \
|
||||||
/home/user/media/movies \
|
/home/user/media/movies \
|
||||||
/home/user/media/tv \
|
/home/user/media/tv'
|
||||||
/home/user/media/anime \
|
|
||||||
/home/user/media/musicvideos'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Verify:
|
Verify:
|
||||||
|
|
@ -1076,20 +1068,13 @@ under `/media/<name>`:
|
||||||
volumes:
|
volumes:
|
||||||
- /home/user/media/movies:/media/movies:ro
|
- /home/user/media/movies:/media/movies:ro
|
||||||
- /home/user/media/tv:/media/tv:ro
|
- /home/user/media/tv:/media/tv:ro
|
||||||
- /home/user/media/anime:/media/anime:ro
|
|
||||||
- /home/user/media/musicvideos:/media/musicvideos:ro
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The current compose only mounts `movies` and `tv`; extend it before adding
|
|
||||||
the new libraries via API.
|
|
||||||
|
|
||||||
### 13.5 Initial state after applying
|
### 13.5 Initial state after applying
|
||||||
|
|
||||||
```
|
```
|
||||||
Movies library → /media/movies (empty, ready)
|
Movies library → /media/movies (empty, ready)
|
||||||
TV Shows library → /media/tv (Futurama 1999, S01-S03, 44 eps)
|
TV Shows library → /media/tv (Futurama 1999, S01-S04, 72 eps + 9 featurettes)
|
||||||
Anime library → /media/anime (empty, ready)
|
|
||||||
Music Videos lib → /media/musicvideos (empty, ready)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -1105,7 +1090,7 @@ Before declaring a new addition "done":
|
||||||
5. Per-item folder exists (no loose files in library root, except music videos).
|
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.
|
6. `tvshow.nfo` / `movie.nfo` exists IFF you needed to override the scraper.
|
||||||
7. Subtitles use `<basename>.<lang>.srt` (doc 03).
|
7. Subtitles use `<basename>.<lang>.srt` (doc 03).
|
||||||
8. Scan: `curl -s -X POST -H "X-Emby-Token: $TOKEN" https://tv.s8n.ru/Library/Refresh`.
|
8. Scan: `curl -s -X POST -H "X-Emby-Token: $TOKEN" https://nasflix.s8n.ru/Library/Refresh`.
|
||||||
9. Wait ~30 s, check item via `/Items?searchTerm=...` — verify `ProviderIds`
|
9. Wait ~30 s, check item via `/Items?searchTerm=...` — verify `ProviderIds`
|
||||||
is populated. Empty `ProviderIds` = filename didn't disambiguate; doc 02
|
is populated. Empty `ProviderIds` = filename didn't disambiguate; doc 02
|
||||||
§ 5 has the manual-lock recipe.
|
§ 5 has the manual-lock recipe.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# 06 — Per-Library Themes (Movies = Netflix, Anime = Crunchyroll, Music = Spotify)
|
# 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://tv.s8n.ru
|
> **Scope of this doc:** research only. No live changes. Targets Jellyfin **10.10.3** at https://nasflix.s8n.ru
|
||||||
> with the current global theme **ElegantFin v25.12.31** in `/System/Configuration/branding` `CustomCss`.
|
> 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 |
|
| 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 |
|
| 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 |
|
| 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.tv.s8n.ru`, `anime.tv.s8n.ru`, `music.tv.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.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 |
|
||||||
|
|
||||||
### Why approach #1 fails
|
### 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:
|
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
|
- 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.
|
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.tv.s8n.ru` cannot
|
- You want fully isolated user accounts per "service" (e.g. kid account on `anime.nasflix.s8n.ru` cannot
|
||||||
see movies subdomain at all).
|
see movies subdomain at all).
|
||||||
- You're prepared to either (a) duplicate libraries (3× disk metadata, 3× scans) or (b) maintain a
|
- 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 —
|
per-user library policy on a single backend that mirrors content into 3 frontend instances —
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# 07 — Pre-Import Cleanup Ruleset (tv.s8n.ru)
|
# 07 — Pre-Import Cleanup Ruleset (nasflix.s8n.ru)
|
||||||
|
|
||||||
Last updated: 2026-05-08
|
Last updated: 2026-05-08
|
||||||
Server: Jellyfin 10.10.3 on nullstone, container `jellyfin`
|
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.
|
Idempotent. Dry-run by default. Quarantine-first. Source-immutable.
|
||||||
Returns the staging path on stdout for piping to doc 08's normalizer.
|
Returns the staging path on stdout for piping to doc 08's normalizer.
|
||||||
|
|
||||||
Save to `bin/cleanup-import.sh` in the `jellyfin-stack` repo.
|
Save to `bin/cleanup-import.sh` in the `NASFLIX` repo.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# cleanup-import.sh — Pre-import cleanup for tv.s8n.ru
|
# cleanup-import.sh — Pre-import cleanup for nasflix.s8n.ru
|
||||||
# Version 1.0 (2026-05-08) — see docs/07-pre-import-cleanup.md
|
# Version 1.0 (2026-05-08) — see docs/07-pre-import-cleanup.md
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# 08 — Filename & Folder Normalization Ruleset (tv.s8n.ru)
|
# 08 — Filename & Folder Normalization Ruleset (nasflix.s8n.ru)
|
||||||
|
|
||||||
Last updated: 2026-05-08
|
Last updated: 2026-05-08
|
||||||
Server: Jellyfin 10.10.3 on nullstone, container `jellyfin`
|
Server: Jellyfin 10.10.3 on nullstone, container `jellyfin`
|
||||||
|
|
@ -1195,9 +1195,9 @@ Run with Python 3.10+. Stdlib only — no external deps.
|
||||||
```python
|
```python
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
normalize.py — canonical filename normalizer for tv.s8n.ru
|
normalize.py — canonical filename normalizer for nasflix.s8n.ru
|
||||||
|
|
||||||
Per /tmp/jellyfin-stack/docs/08-filename-normalization.md.
|
Per /tmp/NASFLIX/docs/08-filename-normalization.md.
|
||||||
Safe by default: dry-run, no overwrite, no delete.
|
Safe by default: dry-run, no overwrite, no delete.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue