ARRFLIX/docs/05-file-structure-rules.md
s8n 1f5ba31483 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).
2026-05-08 02:57:34 +01:00

1139 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 05 — File & Folder Structure Rules (arrflix.s8n.ru)
Last updated: 2026-05-08
Server: Jellyfin 10.10.3 on nullstone, container `jellyfin`
Library root inside container: `/media`
Library root on host: `/home/user/media`
This document is the authoritative ruleset for laying media out on disk so
Jellyfin scrapes it correctly the first time, every time. Cross-linked to:
- [`01-artwork-and-images.md`](01-artwork-and-images.md) — image scrapers and on-disk override files
- [`02-metadata-and-titles.md`](02-metadata-and-titles.md) — filename parsing, `RemoteSearch/Apply`, the lock-the-series flow
- [`03-subtitles.md`](03-subtitles.md) — sidecar `.srt` / `.ass` naming and the OpenSubtitles plugin
- [`04-theming-and-users.md`](04-theming-and-users.md) — multi-user policies and library access
Sources of truth (check these BEFORE this doc — they update):
- <https://jellyfin.org/docs/general/server/media/movies/>
- <https://jellyfin.org/docs/general/server/media/shows/>
- <https://jellyfin.org/docs/general/server/media/music/>
- <https://jellyfin.org/docs/general/server/media/books/>
- <https://jellyfin.org/docs/general/server/libraries/>
- <https://jellyfin.org/docs/general/server/metadata/nfo/>
- Source: `Emby.Naming.dll` ships in the container at `/jellyfin/Emby.Naming.dll`. Rules below match the Emby.Naming regex chain referenced by the docs.
- `CollectionType.cs` (master): `unknown, movies, tvshows, music, musicvideos, trailers, homevideos, boxsets, books, photos, livetv, playlists, folders` (enum int values 012).
---
## 0. Top-level rules (apply to everything)
These are non-negotiable. Most "Jellyfin won't match my file" tickets are
caused by violating one of these:
1. **One library = one `CollectionType`.** Never mix Movies and TV in the same
library. Mixed libraries technically exist (`mixed`) but lose half the
scrapers and most edge-case parsing — do not use.
2. **One folder per item.** A movie lives in its own folder. A series lives in
its own folder. A music album lives in its own folder. Loose files in the
library root will scrape, but extras / NFO / artwork sidecars cannot attach
to a loose file.
3. **Forbidden filename characters:** `< > : " / \ | ? *`
These are illegal on Windows and Jellyfin's parser refuses to canonicalise
them. Use `--` for `:`, drop quotes entirely.
4. **No accents/non-ASCII in folder names** unless you are sure the underlying
filesystem (`ext4` here) and SMB/NFS clients all support UTF-8. We're on
`ext4` + LAN-only HTTP, so accents are safe — but avoid them where the
ASCII title is well-known (e.g. `Amelie (2001)` not `Amélie (2001)`).
5. **Always include the year** for movies/series whose title is not unique:
`The Office (2005)` vs `The Office (2001)` (UK), `It (2017)` vs `It (1990)`.
Year goes in parentheses immediately after the title with a single space.
6. **Year is parsed only when in `( )`**`Movie 2005.mkv` does NOT bind 2005
as the year, it becomes part of the title. `Movie (2005).mkv` does.
7. **Provider-ID overrides win over filename guessing.** If a title is
ambiguous or the scraper repeatedly picks the wrong show, embed the ID:
`Series Name (2023) [tmdbid-12345]/`. Doc 02 covers the `RemoteSearch/Apply`
path for fixing this after-the-fact via API.
8. **`SeriesName/Season XX/SeriesName SXXEYY.ext` is the canonical TV layout.**
Anything flatter or deeper has corner cases. Stick to it.
9. **Dots, dashes, underscores, and spaces are interchangeable** between tokens
for the parser. `Futurama.s01e01.pl.mkv` parses identically to
`Futurama - S01E01 - pl.mkv`. Pick one and be consistent inside a library.
10. **Refresh after a rename.** Renaming a file on disk does NOT auto-refresh
the existing Jellyfin item — it creates a new "missing" record. Either
rename BEFORE first scan, or `POST /Library/Refresh` after.
---
## 1. Movies
### 1.1 Folder structure
```
/media/movies/
├── Blade Runner (1982)/
│ └── Blade Runner (1982).mkv
├── Blade Runner 2049 (2017)/
│ ├── Blade Runner 2049 (2017) - 2160p.mkv
│ ├── Blade Runner 2049 (2017) - 1080p.mkv
│ └── Blade Runner 2049 (2017) - Theatrical.mkv
├── Dune (1984)/
│ └── Dune (1984) [imdbid-tt0087182].mkv
├── Dune (2021)/
│ └── Dune (2021).mkv
└── Lord of the Rings - Fellowship (2001)/
├── Lord of the Rings - Fellowship (2001) - cd1.mkv
└── Lord of the Rings - Fellowship (2001) - cd2.mkv
```
One folder per movie. Folder name = movie name.
### 1.2 Filename pattern
- **Pattern:** `^<Title> \((?<year>\d{4})\)( \[(imdbid|tmdbid|tvdbid)-[^\]]+\])?( - <Label>)?\.<ext>$`
- **Title** is whatever you put — but it must **byte-for-byte match** the
parent folder name when using multi-version naming.
- **Year** in `(YYYY)` is technically optional but **required for this deploy**.
- **Provider-ID block** `[imdbid-ttNNNNNNN]` / `[tmdbid-NNNN]` / `[tvdbid-NNNN]`
is optional; use it when the title is ambiguous or scraper picks wrong.
- **Label** for multi-version movies: ` - <free text>` or ` - [<free text>]`.
Resolution labels ending in `p` or `i` (`2160p`, `1080p`, `720i`) sort
descending by resolution; everything else sorts alphabetically.
#### Examples that WORK
```
Blade Runner (1982).mkv
Blade Runner (1982) [imdbid-tt0083658].mkv
Blade Runner 2049 (2017) - 2160p.mkv
Blade Runner 2049 (2017) - Directors Cut.mkv
Blade Runner 2049 (2017) - [Extended].mkv
Lord of the Rings - Fellowship (2001) - cd1.mkv
Lord of the Rings - Fellowship (2001) - part 1.mkv
Lord of the Rings - Fellowship (2001).part.1.mkv
```
#### Examples that BREAK
```
Blade Runner.1982.mkv ← year not in parens; title becomes "Blade Runner 1982"
Blade.Runner.(1982).mkv ← parses but folder/file mismatch will void multi-version
BR (1982).mkv ← title too cryptic, scraper guesses wrong
Blade Runner (1982) - Directors Cut.mkv ← in folder "BladeRunner1982" → mismatch
LOTR: Fellowship (2001).mkv ← `:` is illegal
Movies/Blade Runner (1982).mkv ← no per-movie folder; extras/NFO can't attach
```
### 1.3 Multi-disc / multi-part rips
Use part separators `cd|dvd|part|pt|disc|disk` followed by a number, optionally
preceded by space/`.`/`-`/`_`:
```
Lord of the Rings - Fellowship (2001) - cd1.mkv
Lord of the Rings - Fellowship (2001) - cd2.mkv
```
**Limitation (from upstream docs, verbatim):** "This does not work with
multiple versions or merging." → if you have a 2-disc 2160p AND a 2-disc 1080p
of the same movie, you must remux into single files; the parser cannot encode
both axes.
### 1.4 Foreign-language audio dubs
Jellyfin matches on the original title (English/release-language) regardless
of audio. Polish-dubbed `Futurama` would be:
```
/media/movies/Futurama - Bender's Big Score (2007)/
└── Futurama - Bender's Big Score (2007).mkv ← Polish audio inside
```
Do NOT put `.pl` in the filename — the audio language tag is a track-level
attribute (read from the mkv stream), not a filename token. If you must
distinguish two language rips of the same film, use the multi-version pattern:
```
Movie (2020) - PL Dub.mkv
Movie (2020) - Original.mkv
```
### 1.5 Year disambiguation
When two films share a title, year alone is what the scraper uses. If both
are 1980, fall back to provider IDs:
```
/media/movies/Bad Movie (1980) [imdbid-tt0080000]/
/media/movies/Bad Movie (1980) [imdbid-tt0080001]/
```
### 1.6 Scrapers
- **Primary:** TheMovieDb (TMDb) — bundled, on by default.
- **Fallback / cross-reference:** OMDb (IMDb-backed; ships with Jellyfin core).
- **Image-only:** TheMovieDb covers most posters/backdrops. Add the
**Fanart.tv** plugin if you need clearart, disc, logo overrides — see
`01-artwork-and-images.md` § 4.
- **Trailers:** TheMovieDb attaches YouTube trailer links automatically; the
**AniList** / **TheTVDB** plugins do not apply here.
### 1.7 Edge cases
- **`VIDEO_TS` / `BDMV` rips** are supported but **lose multi-version, multi-part,
and external subtitles**. Avoid for new rips; remux to mkv.
- **Pre-release / unofficial cuts** (Snyder Cut, Final Cut Pro, etc.) → use the
multi-version label, not a separate folder.
- **Movies that became series** (e.g. Fargo) — the original film goes in
`/media/movies/`, the show in `/media/tv/`. Provider IDs prevent cross-match.
- **Anime films that are part of a TV show** (One Piece: Stampede) — see § 3.6.
---
## 2. TV shows
### 2.1 Folder structure (canonical)
```
/media/tv/
├── Futurama (1999)/
│ ├── Season 00/ ← specials live here
│ │ └── Futurama (1999) S00E01 - Christmas Special.mkv
│ ├── Season 01/
│ │ ├── Futurama (1999) S01E01.mkv
│ │ ├── Futurama (1999) S01E02.mkv
│ │ └── Futurama (1999) S01E03-E04.mkv ← multi-episode file
│ ├── Season 02/
│ └── tvshow.nfo ← optional, doc § 11
└── The Office (2005)/
└── Season 01/
└── The Office (2005) S01E01.mkv
```
**Per-season folders are mandatory** for this deploy. Flat (no season folders)
parses but loses the per-season-poster override path (§ 10) and breaks for
shows >2 seasons.
### 2.2 Filename pattern
- **Pattern:** `^.*?[Ss](?<season>\d{1,2})[Ee](?<episode>\d{1,3})(-[Ee]?(?<end>\d{1,3}))?(\s.*)?\.<ext>$`
- Season + episode tokens recognised by the parser (case-insensitive, dots
and dashes equivalent to spaces — verified in doc 02 § 2):
| Pattern | Example | Result |
|---|---|---|
| `S##E##` | `Futurama S01E01.mkv` | s1e1 |
| `s##e##` | `Futurama.s01e01.pl.mkv` | s1e1 (current Futurama) |
| `Season ## Episode ##` | `Futurama Season 1 Episode 1.mkv` | s1e1 |
| `##x##` | `Futurama 1x01.mkv` | s1e1 |
| `S##E##-E##` | `Futurama S01E01-E02.mkv` | one file, eps 1+2 |
| `S##E## - S##E##` | `Futurama S01E01 - S01E02.mkv` | multi range |
Series name comes from the **parent folder** (preferred) or the filename
prefix before the `S##E##` token. If both are present, folder wins.
#### Examples that WORK
```
Futurama (1999)/Season 01/Futurama (1999) S01E01.mkv
Futurama (1999)/Season 01/Futurama.s01e01.pl.mkv
Futurama (1999)/Season 01/Futurama 1x01 Space Pilot 3000.mkv
Futurama (1999)/Season 01/Futurama S01E01-E02.mkv
Futurama (1999)/Season 00/Futurama (1999) S00E01 - Bender Big Score.mkv
```
#### Examples that BREAK
```
Futurama/Futurama-Pilot.mkv ← no S##E## token, ungrabbable as episode
Futurama/Season1/... ← "Season1" — needs space: "Season 1" or "Season 01"
Futurama/Specials/... ← "Specials" doesn't match; use "Season 00"
Futurama/Season 01/01.mkv ← parser sees no season+episode token, only "01"
Futurama/S01/Futurama_S01E01.mkv ← top-level folder is "S01", series name = "S01"
Futurama (1999) S01E01.mkv ← in /media/tv/ root; no series folder
```
### 2.3 Specials (Season 0)
- Folder: `Season 00` (zero-padded). `Specials/`, `Season 0/`, `Season Specials/`
do **not** match the parser.
- Filename: `Series (year) S00E01 - Title.mkv``S00` is required; without
it the file falls into "no season" and is ignored.
- For specials that should appear inside a regular season (e.g. between S03E04
and S03E05), use NFO `<airsbefore_season>` / `<airsafter_season>` /
`<airsbefore_episode>` tags AND enable "Display specials within their
series" in library settings.
### 2.4 Multi-episode files
Two formats accepted:
```
Futurama (1999) S01E01-E02.mkv ← preferred
Futurama (1999) S01E01 - S01E02.mkv ← also accepted
```
Both tag the file as "stacked" — Jellyfin shows it as one entry on the
episode list and plays the entire file when either episode is clicked.
### 2.5 Date-based / daily shows
The official docs do not define a date-based pattern as of 2026-05. The
practical workaround for daily shows (talk shows, news) is to fake them
into seasonal numbering by year:
```
The Daily Show/
├── Season 2024/
│ ├── The Daily Show S2024E001 - 2024-01-02.mkv
│ └── The Daily Show S2024E002 - 2024-01-03.mkv
```
Episode number = day-of-year (001366). Ugly but parser-clean. If the
metadata provider (TVDB) supports the date-based show, NFO sidecars can
override the episode title to the actual airdate.
### 2.6 Scrapers
- **Primary:** TheTVDB.
- **Secondary:** TheMovieDb (TMDb has good TV coverage too).
- **Order matters:** Library options → Metadata Fetchers → drag the order.
For Futurama on this deploy we used TMDB primary because TVDB had stale
episode-still URLs.
- **Image:** TVDB ships posters and episode stills; TMDB has higher-resolution
backdrops; Fanart.tv has clearart + clearlogo.
### 2.7 Edge cases
- **Series whose name STARTS with a year** (e.g. "1923") — wrap in folder
`1923 (2022)/` so the parser doesn't confuse the series-name year with
the disambiguation year.
- **Shows that re-run/reboot** (`Doctor Who`, `Battlestar Galactica`) — keep
reboots in separate folders, year disambiguation is mandatory:
`Doctor Who (1963)/` and `Doctor Who (2005)/`.
- **Mini-series / limited series** — treat as TV, single season is fine
(`Chernobyl (2019)/Season 01/...`).
- **Episode title inside filename is ignored** once the series is identified;
TMDB/TVDB title overwrites it (see doc 02 § 4).
- **`(year)` is required only at the series level**, not on every episode.
Including it on every episode is harmless but verbose.
---
## 3. Anime
Anime is the area with the highest "scraper picks the wrong thing" risk
because TVDB / TMDB / AniDB / AniList disagree on how to slice multi-cours
shows into seasons. Two distinct strategies — pick **one per show**, never
mix.
### 3.1 Strategy A — TVDB seasonal numbering (default for this deploy)
Use this when:
- Show has ≤ 100 episodes.
- TVDB's season split matches the official Blu-ray / streaming split.
- You're not running Shoko.
#### Folder structure
```
/media/anime/
├── Cowboy Bebop (1998)/
│ └── Season 01/
│ ├── Cowboy Bebop (1998) S01E01.mkv
│ └── Cowboy Bebop (1998) S01E02.mkv
└── Mushishi (2005)/
├── Season 01/
└── Season 02/ ← Mushishi Zoku Shou maps to S02 on TVDB
```
#### Filename pattern
Identical to TV shows § 2.2. Include `(year)` of first broadcast.
#### Edge case — episodes >99 in a season
`S01E100` works for the parser. `S01E001` (3-digit) also works — see [Issue
#17 in jellyfin-plugin-anime](https://github.com/jellyfin-archive/jellyfin-plugin-anime/issues/17).
But **absolute numbering across multiple seasons** (where the show has 1099
episodes spanning many "seasons" on disk) breaks the seasonal model. Use
Strategy B.
### 3.2 Strategy B — Absolute numbering with Shoko Server
Use this when:
- Show has > 100 episodes (One Piece, Naruto, Detective Conan).
- You want AniDB matching, MAL/AniList sync, exact tag accuracy.
- You don't mind running an extra container.
Shoko hashes files by content (ED2K) and identifies them regardless of
filename. With Shoko + the Jellyfin Shoko plugin, **filenames don't matter**.
```
/media/anime-shoko/
└── (any layout you like; Shoko walks the tree and hashes everything)
```
This deploy does **not currently run Shoko**. If/when added, it lives at
`/opt/docker/shoko/` and exposes `/media/anime-shoko/` to Jellyfin via the
plugin, separate from `/media/anime/`.
### 3.3 Strategy C — Absolute numbering with naive Jellyfin (avoid)
Naming files `One Piece - 1099.mkv` with no season folders works for the
**very first 99 episodes** then breaks: the parser sees ep 100+ as "ep 1
of S00 (Specials)" and shuffles. Documented in upstream issue #17. Don't.
### 3.4 Sub vs Dub
Two acceptable patterns:
**Pattern 1 — separate libraries** (recommended, clean):
```
/media/anime/Death Note (2006)/Season 01/Death Note (2006) S01E01.mkv ← original Japanese w/ subs
/media/anime-dub/Death Note (2006)/Season 01/Death Note (2006) S01E01.mkv ← English dub
```
Two libraries; user picks which to browse.
**Pattern 2 — multi-version filenames** (single library, may confuse scraper):
```
/media/anime/Death Note (2006)/Season 01/
├── Death Note (2006) S01E01.mkv ← default (sub)
└── Death Note (2006) S01E01 - Dub.mkv ← extra version, label "Dub"
```
Beware: multi-version on TV episodes (vs movies) is partial in Jellyfin
10.10 — the dub version may not be selectable from all clients. Pattern 1
is safer.
### 3.5 OVAs / OADs / specials
OVAs go in `Season 00` of the parent series, with descriptive titles:
```
Mushishi (2005)/
├── Season 00/
│ ├── Mushishi (2005) S00E01 - Hihamukage OVA.mkv
│ └── Mushishi (2005) S00E02 - Bell of Stillness OVA.mkv
└── Season 01/
```
### 3.6 Anime films that are "part of" a show
Two camps:
- **Canon to plot, watch-order matters** (e.g. *Code Geass: Lelouch of the
Re;surrection*) → put in `Season 00` of the show as a special.
- **Standalone film**, parallel universe (most One Piece movies, Pokémon
films) → put in `/media/movies/` with year. Provider IDs prevent
cross-matching with the parent series.
### 3.7 Japanese vs English titles
Folder name uses the title that matches your **primary metadata provider's
preferred display language**. On this deploy, library `MetadataLanguage` is
`pl` for Futurama; if you add an Anime library set it to `en` or `ja-JP`.
Practical rule: use **the romaji or English title that the show's English
Wikipedia article uses as its primary heading**. That's what TVDB/TMDB
search will resolve. Set provider ID to lock if both titles match
something:
```
/media/anime/Steins;Gate (2011) [tvdbid-244061]/
```
(Note `;` is illegal on Windows but allowed on `ext4`. Avoid for portability.)
### 3.8 Scrapers
- **Primary (sub library):** TheTVDB (anime detection enabled).
- **Optional plugin:** **AniDB** + **AniList** plugins — install via
Dashboard → Plugins → Catalog. Enable per-library. AniDB has better
episode-level metadata for older shows; AniList has better
current-airing data.
- **With Shoko:** Shoko replaces all of the above; AniDB IDs are canonical.
### 3.9 Library type
For this deploy, the Anime library uses `CollectionType: tvshows` (NOT a
separate "anime" type — Jellyfin doesn't have one). Set
`PreferredMetadataLanguage` and the metadata-provider order at library
creation. See § 12 for the API call.
---
## 4. Stand-up comedy specials
### 4.1 Folder structure
Treat as **movies** (one folder per special). Each comedian's specials are
peers — do not nest by performer.
```
/media/movies/
├── Bo Burnham - Inside (2021)/
│ └── Bo Burnham - Inside (2021).mkv
├── Bo Burnham - Make Happy (2016)/
│ └── Bo Burnham - Make Happy (2016).mkv
└── Norm Macdonald - Nothing Special (2022)/
└── Norm Macdonald - Nothing Special (2022).mkv
```
### 4.2 Filename pattern
Same as Movies (§ 1.2). Convention: `<Performer> - <Special Title> (year)`.
#### Examples that WORK
```
Bo Burnham - Inside (2021).mkv
Hannah Gadsby - Nanette (2018) [imdbid-tt8465676].mkv
```
#### Examples that BREAK
```
Bo Burnham/Inside (2021).mkv ← no per-movie folder
Inside (2021).mkv ← title too generic; TMDB picks horror film "Inside"
Bo Burnham: Inside (2021).mkv ← `:` is illegal
```
### 4.3 Scrapers
- **Primary:** TheMovieDb (TMDb has stand-up special listings under "Movies").
- TVDB has a Stand-Up category but the Jellyfin TVDB integration treats
everything in a movies library as a movie — leave it.
### 4.4 Edge cases
- **Specials that aren't on TMDB** (small comedians, festival recordings) →
write a `movie.nfo` (§ 11) and let it stand alone. Jellyfin won't fetch
remote data without an ID.
- **Optional separate library:** if you want stand-up out of the main movies
grid, create a second library with `CollectionType: movies` rooted at
`/media/standup/` — same scrapers, just a different shelf.
- **Tagging:** add `<tag>Stand-up</tag>` to the NFO or use a Jellyfin
Collection (BoxSet) called "Stand-up Specials" to group them.
---
## 5. Concerts / music videos
### 5.1 Folder structure
```
/media/musicvideos/
├── Daft Punk/
│ ├── Get Lucky/
│ │ └── Daft Punk - Get Lucky.mp4
│ └── Around the World/
│ └── Daft Punk - Around the World.mp4
└── Pink Floyd/
└── Pulse Concert (1995)/
├── Pink Floyd - Pulse (1995).mkv
└── poster.jpg
```
The library can nest as deep as you like — verbatim from upstream:
"The folders and video files can be named however you want, since no
metadata fetching is performed."
### 5.2 Filename pattern
- **Pattern:** anything. Free-form. The display name is the literal filename
minus extension.
- **Convention for this deploy:** `<Artist> - <Track Title>.<ext>` (or
`<Artist> - <Concert Name> (year).<ext>` for full concerts).
#### Examples that WORK
Anything not containing `< > : " / \ | ? *`.
#### Examples that BREAK (parser-wise — none, but UX-wise)
```
01.mp4 ← display name "01", useless
videoplayback (1).mp4 ← yt-dlp default; rename before scan
```
### 5.3 Scrapers
- **None by default.** `musicvideos` library type has no built-in remote
metadata fetcher — Jellyfin uses filenames + folder structure.
- Embedded ID3-style tags in `mp4` (artist, title) ARE read.
- Plugins: there is no first-party music-video scraper. Some users use
the **MusicBrainz** plugin to cross-reference, but coverage is poor.
### 5.4 Edge cases
- **Full live concerts** are sometimes better as `tvshows` (one episode per
song) or `movies` (single file). For this deploy use **movies** for
full concert recordings, **musicvideos** for individual song clips.
- **Fan-made / unofficial videos** — fine here, since no scraper to
mismatch.
- **Music VIDEOS attached to a music album** — Jellyfin doesn't link a
music-video item to a music-album item natively. Live with the
separation.
---
## 6. Documentaries
Documentaries split on form:
### 6.1 Single-film documentaries → Movies library
```
/media/movies/
└── Free Solo (2018)/
└── Free Solo (2018).mkv
```
Same rules as § 1. TMDB classifies most documentary films as "Movies".
### 6.2 Multi-episode documentary series → TV library
```
/media/tv/
└── Planet Earth II (2016)/
└── Season 01/
├── Planet Earth II (2016) S01E01.mkv
└── Planet Earth II (2016) S01E02.mkv
```
Same rules as § 2. TVDB classifies most documentary series as "Series".
### 6.3 Optional separate libraries
For users who want documentaries off the main Movies/TV shelves:
- `/media/docs-movies/` with `CollectionType: movies`
- `/media/docs-tv/` with `CollectionType: tvshows`
Same scrapers as the parent type — the tag is purely UI.
### 6.4 Scrapers
- **Films:** TMDb (primary), OMDb (fallback).
- **Series:** TVDB (primary), TMDb (secondary).
- The same NFO override rules apply (§ 11) for obscure docs that aren't on
any provider.
### 6.5 Edge cases
- **Mini-series in 1 long file** (e.g. *The Vietnam War* PBS, 18 hours, one
rip) — treat as a movie or split into episodes. Jellyfin does not chapter-
split a single file into N episode entries.
- **Lecture series** (Crash Course, Khan Academy) — treat as TV. Use
`Crash Course (2011)/Season 01/` etc.
---
## 7. Home videos / personal media
The goal: keep Jellyfin from "fixing" your wedding videos by pulling the
poster of an unrelated 2015 movie called *Wedding*.
### 7.1 Folder structure
```
/media/home/
├── 2024/
│ ├── 2024-06-15 Berlin Trip/
│ │ ├── 2024-06-15 Berlin Trip - clip01.mp4
│ │ └── 2024-06-15 Berlin Trip - clip02.mp4
│ └── 2024-12-25 Christmas/
│ └── 2024-12-25 Christmas.mp4
└── 2025/
└── 2025-08-30 Wedding/
└── 2025-08-30 Wedding.mp4
```
### 7.2 Filename pattern
Free-form. Date-prefix recommended (`YYYY-MM-DD <event>`) — Jellyfin will
parse the date and use it as the item's date.
### 7.3 Library type
`CollectionType: homevideos`**critical**. This is the only collection
type that disables all remote metadata fetchers. With `homevideos`:
- No TMDB / TVDB / OMDb scrapers run.
- Image fetchers are off (use sidecar `.jpg` if you want a thumb).
- The library shows up under "Photos & home videos" in the UI sidebar.
### 7.4 Scrapers
**None.** Local-only. NFO sidecars (§ 11) work if you want to label
individual clips, but no remote lookups.
### 7.5 Edge cases
- **Photos in the same folder as videos** — `homevideos` library accepts
both. Use `.jpg`/`.png`/`.heic` sidecars; they appear in the slideshow.
- **Don't drop home videos in `/media/movies/`.** Even with no provider IDs,
Jellyfin will scan and try to match titles against TMDB. The
`homevideos` library is the only safe place.
- **Smart phone clips** named `IMG_1234.MOV` are fine; they display by
filename. Bulk-rename to `2024-06-15 - IMG_1234.mov` if you want them
sorted by event date.
---
## 8. Extras / special features
Extras attach to a parent item (movie or series) via two mechanisms:
filename suffix, or named subfolder. Either works; mixing is fine.
### 8.1 Suffix method
Append one of these tokens to the filename **before** the extension:
| Suffix | Type | Example |
|---|---|---|
| `-trailer` `.trailer` `_trailer` ` trailer` | Trailer | `Blade Runner (1982) - 1982 Theatrical-trailer.mp4` |
| `-sample` `.sample` `_sample` ` sample` | Sample | `Movie-sample.mp4` |
| `-scene` | Deleted scene / vignette | `Inception (2010) - Hallway-scene.mp4` |
| `-clip` | Promo clip | `Movie - TV Spot-clip.mp4` |
| `-interview` | Interview | `Movie - Director Interview-interview.mp4` |
| `-behindthescenes` | BTS featurette | `Movie - VFX Breakdown-behindthescenes.mp4` |
| `-deleted` `-deletedscene` | Deleted scene | `Movie - Cut Diner Scene-deleted.mp4` |
| `-featurette` | Featurette | `Movie - Anatomy of a Stunt-featurette.mp4` |
| `-short` | Short film | `Movie - Prequel Short-short.mp4` |
| `-other` `-extra` | Catch-all | `Movie - Ephemera-other.mp4` |
A lone trailer/sample file can also be named just `trailer.mp4` or
`sample.mp4` and dropped in the parent item folder.
### 8.2 Folder method
Inside the parent item folder, any of these named subfolders are picked up
and the files inside are tagged with the matching extra type:
```
Inception (2010)/
├── Inception (2010).mkv
├── behind the scenes/
│ └── VFX Breakdown.mp4
├── deleted scenes/
│ ├── Diner Cut.mp4
│ └── Hotel Hallway Cut.mp4
├── featurettes/
│ └── Dreams Within Dreams.mp4
├── interviews/
│ └── Christopher Nolan.mp4
├── scenes/
├── shorts/
├── samples/
├── trailers/
│ └── Theatrical Trailer.mp4
├── clips/
├── theme-music/
│ └── theme.mp3 ← see § 8.3
├── backdrops/
│ └── 2.mp4 ← rotating video backdrops
├── other/
└── extras/ ← generic catch-all
```
Subfolder names **must match exactly** (case-insensitive on `ext4`+Jellyfin):
`Behind the Scenes/` works; `BTS/` does not; `behind-the-scenes/` does not.
### 8.3 Theme music
`theme-music/theme.mp3` plays a track on hover/auto in supported clients
(Swiftfin, JellyfinMediaPlayer). One file per item.
### 8.4 Backdrops
Video backdrops (rotating background loops) go in `backdrops/` as numbered
mp4s. Falls back to image backdrops if not present.
### 8.5 Edge cases
- **Extras attached to a series vs a season vs an episode** — folder method
works at any level: drop `behind the scenes/` inside `Futurama (1999)/`
for series-wide extras, inside `Season 01/` for season extras, or
use suffix on a sibling file for episode extras.
- **Show-level trailers** — Jellyfin's TV scraper auto-attaches trailer
YouTube links from TMDB. You don't need to download them.
---
## 9. Subtitles (sidecar)
See [`03-subtitles.md`](03-subtitles.md) for the full rules. Quick reference:
```
<videobasename>.<lang>[.flag].<ext>
```
- `<videobasename>` = the video filename minus extension.
- `<lang>` = ISO-639-1 (`en`, `pl`) or ISO-639-2 (`eng`, `pol`).
- `<flag>` = optional, any combination of `forced`, `default`, `sdh`, `cc`.
- `<ext>` = `srt`, `ass`, `ssa`, `vtt`, `sub` (+ `.idx` for VobSub).
Examples next to `Futurama.s01e01.pl.mkv`:
```
Futurama.s01e01.pl.eng.srt ← English regular
Futurama.s01e01.pl.en.forced.srt ← English forced (foreign-scene captions)
Futurama.s01e01.pl.en.sdh.srt ← English SDH
Futurama.s01e01.pl.en.default.srt ← marked default, auto-selects
Futurama.s01e01.pl.pl.srt ← Polish (matches the language of the audio)
```
After dropping subs on disk, run `POST /Library/Refresh` (or wait for the
nightly scan) — Jellyfin discovers them and attaches.
---
## 10. Artwork override files
Jellyfin scrapes artwork from TMDB/TVDB/Fanart by default (see doc 01).
Override per-item by dropping a sidecar image with one of these recognised
filenames in the item's folder.
### 10.1 Movie / generic item folder
| Filename | ImageType | Notes |
|---|---|---|
| `poster.jpg` / `poster.png` | Primary | Main poster (vertical 2:3). |
| `folder.jpg` | Primary | Alias of poster (Windows / Plex compat). |
| `cover.jpg` | Primary | Alias of poster (also used in music). |
| `default.jpg` | Primary | Alias. |
| `movie.jpg` | Primary | Alias. |
| `backdrop.jpg` | Backdrop | Hero image (16:9 fanart). |
| `backdrop1.jpg`, `backdrop2.jpg`, ... | Backdrop | Multiple backdrops, numbered. |
| `fanart.jpg` | Backdrop | Plex/Kodi compat alias. |
| `logo.png` | Logo | Transparent text-logo overlay. |
| `clearlogo.png` | Logo | Alias. |
| `banner.jpg` | Banner | Wide ~758×140 strip. |
| `thumb.jpg` | Thumb | 16:9 still. Used as episode thumbnail at item level. |
| `landscape.jpg` | Thumb | Alias. |
| `disc.png` | Disc | DVD/Blu-ray hub icon. |
| `clearart.png` | Art | Transparent character cutout. |
### 10.2 TV series folder (additional)
```
Futurama (1999)/
├── poster.jpg
├── backdrop.jpg
├── logo.png
├── banner.jpg
├── season-all-poster.jpg ← shared across all seasons
├── season01-poster.jpg ← Season 01 specific
├── season02-poster.jpg
├── season-specials-poster.jpg ← Season 00
├── Season 01/
│ ├── Futurama (1999) S01E01.mkv
│ ├── Futurama (1999) S01E01.jpg ← episode thumb (basename match)
│ └── poster.jpg ← also valid as season poster
└── tvshow.nfo
```
The two season-poster paths are equivalent; pick one style. Episode-level
thumbs use `<basename>.jpg` (i.e. drop a `.jpg` next to the `.mkv` with
matching name).
### 10.3 When to override
- Use sidecar files when the scraper's choice is wrong AND you don't want
to upload via the Web UI (which writes into `/config/metadata/library/...`
— wiped on container rebuild).
- Sidecars in the media folder **survive `docker rm`** because they live on
the user's data volume.
- Sidecars take precedence over remote scraper images on next refresh
ONLY if "Save artwork into media folders" is enabled in Dashboard →
Libraries → (each library) → Library options. This deploy has it ON.
### 10.4 Edge cases
- `.png` and `.jpg` are both accepted; `.webp` works for backdrops but
not all clients render it — prefer `.jpg`.
- Image larger than 4K is downscaled by the API on serve. Don't bother with
>2160p source images.
- **Don't put `poster.jpg` in `/media/movies/`** (the library root) — it
becomes the library's primary image, often unwanted.
---
## 11. NFO sidecars
NFO files are XML metadata, written next to the media. They override remote
scrapers entirely. From upstream: "Local metadata will always be fetched and
has priority over remote metadata providers like TMDb."
### 11.1 Filenames
| Item type | Required filename |
|---|---|
| Movie | `movie.nfo` (in the movie folder), OR `<videobasename>.nfo` next to the file, OR `VIDEO_TS.nfo` for DVD rips |
| TV series | `tvshow.nfo` in the series folder |
| TV season | `season.nfo` in the season folder |
| TV episode | `<episode_filename>.nfo` (e.g. `Futurama (1999) S01E01.nfo`) |
| Music artist | `artist.nfo` |
| Music album | `album.nfo` |
### 11.2 When to write one
- Obscure indie film not on TMDB / IMDB → `movie.nfo` lets you fill the
metadata yourself.
- Show whose IDs scrape wrong every time → `tvshow.nfo` with locked
`<tmdbid>` / `<tvdbid>` is faster than the API `RemoteSearch/Apply`
workflow (doc 02 § 5).
- Home videos / personal — usually not needed (homevideos lib doesn't
scrape) but useful for nice titles.
### 11.3 Minimal `movie.nfo` example
```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<movie>
<title>The Obscure Film</title>
<originaltitle>Niejasny film</originaltitle>
<year>2014</year>
<plot>A short description that overrides whatever TMDB returns.</plot>
<genre>Drama</genre>
<runtime>97</runtime>
<director>Jane Director</director>
<actor>
<name>Lead Actor</name>
<role>Protagonist</role>
</actor>
<uniqueid type="imdb" default="true">tt12345678</uniqueid>
</movie>
```
### 11.4 Minimal `tvshow.nfo` example
```xml
<?xml version="1.0" encoding="UTF-8"?>
<tvshow>
<title>Futurama</title>
<year>1999</year>
<plot>...</plot>
<uniqueid type="tmdb" default="true">615</uniqueid>
<uniqueid type="tvdb">73871</uniqueid>
</tvshow>
```
### 11.5 NFO Saver (write-back)
Enable Dashboard → Libraries → (each) → Metadata Savers → "Nfo". Jellyfin
will write `*.nfo` next to media files whenever metadata changes. This is
how you survive container rebuilds without losing manual fixes — the NFO
on disk is canonical, the SQLite DB is regenerable.
### 11.6 Edge cases
- **Empty/malformed NFO** stops the scraper from running on that item AT
ALL. Either write valid XML or delete the file.
- **NFO + provider ID conflict** — local always wins. If you set
`<tmdbid>615</tmdbid>` and the filename has `[tmdbid-9999]`, NFO wins.
- **Episode `.nfo` per file is verbose.** Most people only write `tvshow.nfo`
and let the episode metadata come from the provider.
---
## 12. CollectionType-to-scraper mapping (library creation)
Verbatim values from `Jellyfin.Data/Enums/CollectionType.cs` (master,
verified 2026-05). These are the strings to pass when creating a library
via API. (`unknown` is reserved; `tvshowseries`+ are virtual aggregates not
used at library creation.)
| `CollectionType` | UI name | Default scrapers (10.10.x) | Use for |
|---|---|---|---|
| `movies` | Movies | TMDb, OMDb, Fanart.tv (plugin) | Films, stand-up, doc films |
| `tvshows` | Shows | TVDB, TMDb, Fanart.tv, OMDb | TV, anime, doc series, kids' shows |
| `music` | Music | MusicBrainz, AudioDB | Albums, tracks |
| `musicvideos` | Music Videos | none (filename only) | Music videos, short concerts |
| `homevideos` | Home Videos & Photos | none | Personal recordings, photo albums |
| `boxsets` | Collections | TMDb collections | Manually-curated cross-library box sets |
| `books` | Books | requires "Bookshelf" plugin | epub, mobi, pdf, comics |
| `photos` | Photos | none | Photo-only library |
| `livetv` | Live TV | tuner-driven | Real-time TV (HDHomeRun etc.) |
| `trailers` | Trailers | bundled | Standalone trailers library (rare) |
| `playlists` | Playlists | n/a | Internal Jellyfin construct |
| `folders` | Folders | n/a | Internal Jellyfin construct |
| `mixed` (no enum, legacy) | Mixed | TMDb + TVDB | Don't use — drops most parsing rules |
### 12.1 Creating libraries via API
```bash
TOKEN=*redacted*
H="-H \"X-Emby-Token: ${TOKEN}\""
B="https://arrflix.s8n.ru"
# Movies library
curl -s -X POST $H "$B/Library/VirtualFolders?name=Movies&collectionType=movies" \
-H "Content-Type: application/json" \
-d '{"LibraryOptions":{"PathInfos":[{"Path":"/media/movies"}],"EnableInternetProviders":true,"PreferredMetadataLanguage":"en","MetadataCountryCode":"US","SaveLocalMetadata":true,"SubtitleDownloadLanguages":["eng"]}}'
# TV library
curl -s -X POST $H "$B/Library/VirtualFolders?name=Shows&collectionType=tvshows" \
-H "Content-Type: application/json" \
-d '{"LibraryOptions":{"PathInfos":[{"Path":"/media/tv"}],"EnableInternetProviders":true,"PreferredMetadataLanguage":"en","SaveLocalMetadata":true,"SubtitleDownloadLanguages":["eng"]}}'
# Anime library (still tvshows type)
curl -s -X POST $H "$B/Library/VirtualFolders?name=Anime&collectionType=tvshows" \
-H "Content-Type: application/json" \
-d '{"LibraryOptions":{"PathInfos":[{"Path":"/media/anime"}],"EnableInternetProviders":true,"PreferredMetadataLanguage":"en","SaveLocalMetadata":true,"SubtitleDownloadLanguages":["eng"]}}'
# Music videos
curl -s -X POST $H "$B/Library/VirtualFolders?name=Music%20Videos&collectionType=musicvideos" \
-H "Content-Type: application/json" \
-d '{"LibraryOptions":{"PathInfos":[{"Path":"/media/musicvideos"}]}}'
```
After creation, trigger an initial scan: `POST /Library/Refresh`.
---
## 13. Canonical layout for THIS deploy
### 13.1 Architecture decision
**Adopted: Architecture A** — flat by category at `/home/user/media/`, one
Jellyfin library per category.
```
/home/user/media/
├── movies/ ← collectionType: movies
└── tv/ ← collectionType: tvshows
```
> 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.
### 13.2 Why Architecture A (not B or C)
**B (nested, `media/video/{movies,tv,anime}/...`)** — rejected. Adds a
useless directory level. Jellyfin's library config takes a path; nesting
buys nothing on the client side. Increases the chance of a typo in the
container's bind-mount.
**C (split disks: `/media-fast/` NVMe + `/media-slow/` HDD)** — rejected
**for now**. Nullstone has a single 2 TB NVMe and 4 TB HDD. Total media
today is ~50 GB (Futurama + future). When the library exceeds 1 TB, we'll
revisit and migrate cold catalogue (older movies, finished anime) to the
HDD, mounted as a second library path:
```
LibraryOptions.PathInfos = [
{"Path": "/media/movies"}, ← /home/user/media/movies on NVMe
{"Path": "/media-archive/movies"} ← /mnt/hdd/media/movies
]
```
Jellyfin natively merges multiple paths into one logical library. No URL
or client-facing change needed at migration time.
**A wins because:**
- One library per `collectionType` is the simplest correct mapping.
### 13.3 Concrete mkdir commands
Run on nullstone as `user` (not root — the existing tree is already owned
by `user:user`):
```bash
ssh user@192.168.0.100 'mkdir -p \
/home/user/media/movies \
/home/user/media/tv'
```
Verify:
```bash
ssh user@192.168.0.100 'ls -la /home/user/media/'
```
### 13.4 Container bind mounts
`/opt/docker/jellyfin/docker-compose.yml` should mount each as read-only
under `/media/<name>`:
```yaml
volumes:
- /home/user/media/movies:/media/movies:ro
- /home/user/media/tv:/media/tv:ro
```
### 13.5 Initial state after applying
```
Movies library → /media/movies (empty, ready)
TV Shows library → /media/tv (Futurama 1999, S01-S04, 72 eps + 9 featurettes)
```
---
## 14. Verification checklist
Before declaring a new addition "done":
1. Filename matches the regex anchor for the category (§ 17).
2. Year is in `(YYYY)` and matches the actual release year.
3. Folder name byte-for-byte matches the filename prefix (movies multi-version).
4. No forbidden chars (`< > : " / \ | ? *`).
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://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.
10. Refresh (`Items/{id}/Refresh?ReplaceAllMetadata=true`) AFTER fixing the
provider ID — otherwise the wrong cached metadata sticks.
---
## 15. Quick reference card
| Category | Folder | Filename | CollectionType | Scraper |
|---|---|---|---|---|
| Movie | `movies/Title (year)/` | `Title (year).mkv` | `movies` | TMDb |
| Movie multi-version | `movies/Title (year)/` | `Title (year) - 1080p.mkv` | `movies` | TMDb |
| Movie multi-disc | `movies/Title (year)/` | `Title (year) - cd1.mkv` | `movies` | TMDb |
| TV episode | `tv/Show (year)/Season 01/` | `Show (year) S01E01.mkv` | `tvshows` | TVDB |
| TV multi-ep | `tv/Show (year)/Season 01/` | `Show (year) S01E01-E02.mkv` | `tvshows` | TVDB |
| TV special | `tv/Show (year)/Season 00/` | `Show (year) S00E01.mkv` | `tvshows` | TVDB |
| Anime (seasonal) | `anime/Show (year)/Season 01/` | `Show (year) S01E01.mkv` | `tvshows` | TVDB+AniDB plugin |
| Anime (Shoko) | `anime-shoko/<any>` | any | `tvshows` | Shoko |
| Stand-up | `movies/Comedian - Title (year)/` | `Comedian - Title (year).mkv` | `movies` | TMDb |
| Music video | `musicvideos/Artist/Track/` | `Artist - Track.mp4` | `musicvideos` | none |
| Doc film | `movies/Title (year)/` | `Title (year).mkv` | `movies` | TMDb |
| Doc series | `tv/Show (year)/Season 01/` | `Show (year) S01E01.mkv` | `tvshows` | TVDB |
| Extra (suffix) | `movies/Title (year)/` | `Anything-behindthescenes.mp4` | (parent) | n/a |
| Extra (folder) | `movies/Title (year)/behind the scenes/` | any | (parent) | n/a |
---
## 16. Top three gotchas (in order of frequency)
1. **No per-item folder.** Loose `Movie (2020).mkv` directly in `/media/movies/`
parses, but extras / NFO / artwork sidecars cannot attach. Always make a
folder.
2. **Year not in parens.** `Movie 2020.mkv` → year is part of the title;
scraper search for "Movie 2020" returns wrong results. Always
`Movie (2020).mkv`.
3. **Anime absolute numbering > 99 episodes** without Shoko, mixed with
season folders → episodes shuffle into Season 0. Either split by
TVDB seasons OR run Shoko. Never half-and-half.
---
End of doc 05. For questions about parsing edge cases not covered here,
read `Emby.Naming.xml` inside the container (`docker exec jellyfin cat
/jellyfin/Emby.Naming.xml`) — it has the canonical regex chain.