# 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): - - - - - - - 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:** `^ \((?<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 Niejasny film 2014 A short description that overrides whatever TMDB returns. Drama 97 Jane Director Lead Actor Protagonist tt12345678 ``` ### 11.4 Minimal `tvshow.nfo` example ```xml Futurama 1999 ... 615 73871 ``` ### 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 `615` 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"}]}}' ``` 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 ├── docs-movies/ ← collectionType: movies (future, optional) └── docs-tv/ ← collectionType: tvshows (future, optional) ``` > `home/` and `music/` libraries removed from the canonical layout per owner > decision 2026-05-08. Sections 6 (Music) and the Home-videos category remain > in this doc as reference for re-introduction later — 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. - Anime as its own library lets us set provider order and language preference independently from `tv/`. ### 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' ``` 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/`: ```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 ``` 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) ``` --- ## 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 `..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 | `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.