1166 lines
43 KiB
Markdown
1166 lines
43 KiB
Markdown
|
|
# 05 — File & Folder Structure Rules (tv.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 0–12).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 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 (001–366). 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://tv.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"}]}}'
|
|||
|
|
|
|||
|
|
# Home videos
|
|||
|
|
curl -s -X POST $H "$B/Library/VirtualFolders?name=Home%20Videos&collectionType=homevideos" \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"LibraryOptions":{"PathInfos":[{"Path":"/media/home"}],"EnableInternetProviders":false}}'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
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
|
|||
|
|
├── anime/ ← collectionType: tvshows (separate library)
|
|||
|
|
├── musicvideos/ ← collectionType: musicvideos
|
|||
|
|
├── music/ ← collectionType: music (future)
|
|||
|
|
├── docs-movies/ ← collectionType: movies (future, optional)
|
|||
|
|
├── docs-tv/ ← collectionType: tvshows (future, optional)
|
|||
|
|
└── home/ ← collectionType: homevideos
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 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.
|
|||
|
|
- Anime as its own library lets us set provider order and language
|
|||
|
|
preference independently from `tv/`.
|
|||
|
|
- `home/` MUST be a separate library to get `homevideos` collection type
|
|||
|
|
(the only way to disable scrapers for personal media).
|
|||
|
|
|
|||
|
|
### 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 \
|
|||
|
|
/home/user/media/anime \
|
|||
|
|
/home/user/media/musicvideos \
|
|||
|
|
/home/user/media/music \
|
|||
|
|
/home/user/media/home'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
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
|
|||
|
|
- /home/user/media/anime:/media/anime:ro
|
|||
|
|
- /home/user/media/musicvideos:/media/musicvideos:ro
|
|||
|
|
- /home/user/media/music:/media/music:ro
|
|||
|
|
- /home/user/media/home:/media/home:ro
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The current compose only mounts `movies` and `tv`; extend it before adding
|
|||
|
|
the new libraries via API.
|
|||
|
|
|
|||
|
|
### 13.5 Initial state after applying
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Movies library → /media/movies (empty, ready)
|
|||
|
|
TV Shows library → /media/tv (Futurama 1999, S01-S03, 44 eps)
|
|||
|
|
Anime library → /media/anime (empty, ready)
|
|||
|
|
Music Videos lib → /media/musicvideos (empty, ready)
|
|||
|
|
Music library → /media/music (empty, ready)
|
|||
|
|
Home Videos lib → /media/home (empty, ready)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 14. Verification checklist
|
|||
|
|
|
|||
|
|
Before declaring a new addition "done":
|
|||
|
|
|
|||
|
|
1. Filename matches the regex anchor for the category (§ 1–7).
|
|||
|
|
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://tv.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 |
|
|||
|
|
| Home video | `home/YYYY/YYYY-MM-DD Event/` | any | `homevideos` | none |
|
|||
|
|
| 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.
|