Brand-new container, brand-new volumes, ZERO ARRFLIX customisation. Sister instance to prod (10.10.3, untouched) and dev (10.11.8 + scyfin). P1+P2 from the rebuild plan: Movies + TV Shows libraries added via API, library scan complete (4 movies / 12 series / 230 eps). Auto-scrape matched 10/12 series + 4/4 movies to canonical TMDB IDs without manual intervention; 3 unmatched are TMDB-absent indie content. No theme, no shim, no CustomCss, no plugins, no user import, no home- section seed — owner explicitly asked for a stock baseline.
186 lines
6.4 KiB
Markdown
186 lines
6.4 KiB
Markdown
# 30 — Stock Jellyfin rebuild on tv.s8n.ru (ground-up)
|
|
|
|
Date: 2026-05-11
|
|
Scope: brand new container, brand new volumes, zero ARRFLIX customisation.
|
|
Sister docs: 29 (the failed in-place dev upgrade that led here).
|
|
|
|
---
|
|
|
|
## 1. Decision
|
|
|
|
After running the dev migration (10.10.3 → 10.11.8 + scyfin) on the existing
|
|
`jellyfin-dev` container, the result still carried index.html shim, Cineplex
|
|
remnants, and accumulated configuration drift. Owner asked for a true clean
|
|
build instead.
|
|
|
|
Approach: new container, new domain, no shim, no CustomCss. Stock Jellyfin.
|
|
We layer ARRFLIX brand on top once the bare server is happy.
|
|
|
|
---
|
|
|
|
## 2. Deploy
|
|
|
|
```yaml
|
|
# /opt/docker/jellyfin-stock/docker-compose.yml
|
|
services:
|
|
jellyfin-stock:
|
|
image: jellyfin/jellyfin:10.11.8
|
|
container_name: jellyfin-stock
|
|
restart: unless-stopped
|
|
user: "1000:1000"
|
|
userns_mode: "host"
|
|
environment:
|
|
- TZ=Europe/London
|
|
- JELLYFIN_PublishedServerUrl=https://tv.s8n.ru
|
|
volumes:
|
|
- /home/docker/jellyfin-stock/config:/config
|
|
- /home/docker/jellyfin-stock/cache:/cache
|
|
- /home/user/media:/media:ro
|
|
networks: [proxy]
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.docker.network=proxy
|
|
- traefik.http.routers.jellyfin-stock.rule=Host(`tv.s8n.ru`)
|
|
- traefik.http.routers.jellyfin-stock.entrypoints=websecure
|
|
- traefik.http.routers.jellyfin-stock.tls=true
|
|
- traefik.http.routers.jellyfin-stock.tls.certresolver=letsencrypt
|
|
- traefik.http.services.jellyfin-stock.loadbalancer.server.port=8096
|
|
```
|
|
|
|
Volumes initialised empty. No bind-mount of index.html — the stock web UI
|
|
serves from the image as-is.
|
|
|
|
### DNS
|
|
|
|
```
|
|
Pi-hole local DNS: <nullstone-LAN-IP> tv.s8n.ru
|
|
onyx /etc/hosts: <nullstone-LAN-IP> tv.s8n.ru (appended to existing pin block)
|
|
Public DNS (Gandi): none — LAN-only by design
|
|
```
|
|
|
|
(LAN IP is the standard nullstone bind, see SYSTEM.md.)
|
|
|
|
`/opt/docker/pihole/etc-pihole/custom.list` is owned by root; we wrote via
|
|
privileged Alpine container + `--userns=host` to bypass the userns-remap.
|
|
Same trick used for the `/home/docker/jellyfin-stock/` dirs.
|
|
|
|
ServerId: `adbc441eb46e475c9610c3bd5258dc6e` (fresh, not migrated from prod).
|
|
|
|
---
|
|
|
|
## 3. Library scope (P1+P2)
|
|
|
|
User chose P1+P2 from `tv.s8n.ru` plan: libraries + canonical-ID lock only.
|
|
No user import, no watched-state transfer, no plugins, no theme.
|
|
|
|
### Libraries added via API
|
|
|
|
```bash
|
|
TOKEN=<admin token from Devices table after wizard>
|
|
|
|
curl -X POST -H "X-Emby-Token: $TOKEN" \
|
|
"https://tv.s8n.ru/Library/VirtualFolders?name=Movies&collectionType=movies&paths=%2Fmedia%2Fmovies&refreshLibrary=false" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"LibraryOptions":{"EnableInternetProviders":true,"PreferredMetadataLanguage":"en","MetadataCountryCode":"US","SubtitleDownloadLanguages":["eng"],"SaveSubtitlesWithMedia":true,"RequirePerfectSubtitleMatch":false,"EnabledMetadataFetchers":["TheMovieDb","The Open Movie Database"],"MetadataFetcherOrder":["TheMovieDb","The Open Movie Database"]}}'
|
|
|
|
curl -X POST -H "X-Emby-Token: $TOKEN" \
|
|
"https://tv.s8n.ru/Library/VirtualFolders?name=TV%20Shows&collectionType=tvshows&paths=%2Fmedia%2Ftv&refreshLibrary=false" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"LibraryOptions":{...same shape, fetchers=[TheMovieDb,TheTVDB]}}'
|
|
|
|
curl -X POST -H "X-Emby-Token: $TOKEN" "https://tv.s8n.ru/Library/Refresh"
|
|
```
|
|
|
|
### Scan result
|
|
|
|
```
|
|
MovieCount 4
|
|
SeriesCount 12
|
|
EpisodeCount 230
|
|
```
|
|
|
|
### Auto-scrape outcome
|
|
|
|
10 / 12 series + 4 / 4 movies matched canonical IDs without intervention.
|
|
Three unmatched, all expected:
|
|
|
|
```
|
|
The Big Lez Saga (2022) TMDB --- (TMDB has no entry; Australian indie)
|
|
The Donny & Clarence Show TMDB --- (IMDb tt32043762 only)
|
|
Star Wars: Maul - Shadow Lord [Before Upscale] no IDs (intentional dupe folder)
|
|
```
|
|
|
|
Matched IDs (sanity-checked against prod docs):
|
|
|
|
```
|
|
American Dad! TMDB 1433
|
|
Archer TMDB 10283
|
|
Futurama TMDB 615 TVDB 73871 IMDb tt0149460
|
|
The Mandalorian TMDB 82856
|
|
The Mike Nolan Show TMDB 67160
|
|
Obi-Wan Kenobi TMDB 92830
|
|
Rick and Morty TMDB 60625
|
|
Sassy the Sasquatch TMDB 321760
|
|
Star Wars: Maul TMDB 289219
|
|
Movies
|
|
The Dark Knight TMDB 155
|
|
Idiocracy TMDB 7512
|
|
The Incredible Hulk TMDB 1724
|
|
Lilo & Stitch TMDB 11544
|
|
```
|
|
|
|
No `POST /Items/{id}` lock calls needed — the auto-scrape was clean.
|
|
|
|
---
|
|
|
|
## 4. Passwords
|
|
|
|
Admin `s8n` + user `guest` created via first-run wizard with throwaway
|
|
passwords. Owner asked to use the same passwords as prod. Approach for that
|
|
(deferred — pending owner decision):
|
|
|
|
```sql
|
|
-- prod jellyfin.db Users.Password is $PBKDF2-SHA512$iterations=2100$<salt>$<hash>
|
|
-- Copy hash from prod to stock:
|
|
ATTACH '/path/to/prod-jellyfin.db' AS prod;
|
|
UPDATE Users
|
|
SET Password = (SELECT Password FROM prod.Users WHERE Username = Users.Username)
|
|
WHERE Username IN ('s8n', 'guest');
|
|
```
|
|
|
|
Run with container stopped. Verified the PBKDF2 hash includes the salt
|
|
inline so copying the column is enough — no separate salt column.
|
|
|
|
---
|
|
|
|
## 5. Explicitly NOT done
|
|
|
|
- No theme (no scyfin, no Cineplex, no ElegantFin).
|
|
- No `web-overrides/index.html` shim — stock Jellyfin chrome visible.
|
|
- No CustomCss in `branding.xml` (file is the 225-byte default).
|
|
- No plugins installed (no OpenSubtitles, no anything).
|
|
- No 13-user import — only `s8n` admin + `guest`.
|
|
- No home-section seed — stock defaults apply (smalllibrarytiles, resume,
|
|
resumeaudio, nextup, latestmedia). Owner will iterate from here.
|
|
- No backdrop pinning, no scrollbar themeing, no per-user prefs scripts.
|
|
|
|
---
|
|
|
|
## 6. State table
|
|
|
|
| Instance | Domain | Image | Theme | Brand | Status |
|
|
|---|---|---|---|---|---|
|
|
| `jellyfin` (prod) | arrflix.s8n.ru | 10.10.3 | Cineplex v1.0.6 + INC1-7 patches | ARRFLIX | Untouched, real users on it |
|
|
| `jellyfin-dev` | dev.arrflix.s8n.ru | 10.11.8 | scyfin OLED (broken brand-vs-shim mismatch) | ARRFLIX | Experimental — can be wiped |
|
|
| `jellyfin-stock` | tv.s8n.ru | 10.11.8 | — | stock Jellyfin | Fresh, ready to configure |
|
|
|
|
---
|
|
|
|
## 7. Open follow-ups (none owed before owner sign-off)
|
|
|
|
- Decide fate of `jellyfin-dev` (keep / wipe / repurpose).
|
|
- Owner explores stock UX → identifies what to brand vs leave alone.
|
|
- Eventually layer ARRFLIX skin (logo, accent, dark scrollbar) on top of
|
|
stock — incrementally, documenting each step.
|
|
- If migration to 10.11.8 on prod is later approved: docs/29 staged
|
|
10.10.3 → 10.10.7 → 10.11.8 path with snapshots is the playbook.
|