# 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 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=<JELLYFIN_API_TOKEN>
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 (§ 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://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.
