ARRFLIX/docs/05-file-structure-rules.md
s8n 19382879f9 Drop home/ and music/ from canonical layout per owner
Architecture A trimmed to: movies, tv, anime, musicvideos. Removed
mkdir/compose-mount/library-creation curl for home + music. Sections 6
(Music) and the Home-videos category remain in this doc as reference for
re-introduction later.
2026-05-08 01:49:30 +01:00

43 KiB
Raw Blame History

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:

Sources of truth (check these BEFORE this doc — they update):


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.mkvS00 is required; without it the file falls into "no season" and is ignored.
  • For specials that should appear inside a regular season (e.g. between S03E04 and S03E05), use NFO <airsbefore_season> / <airsafter_season> / <airsbefore_episode> tags AND enable "Display specials within their series" in library settings.

2.4 Multi-episode files

Two formats accepted:

Futurama (1999) S01E01-E02.mkv          ← preferred
Futurama (1999) S01E01 - S01E02.mkv     ← also accepted

Both tag the file as "stacked" — Jellyfin shows it as one entry on the episode list and plays the entire file when either episode is clicked.

2.5 Date-based / daily shows

The official docs do not define a date-based pattern as of 2026-05. The practical workaround for daily shows (talk shows, news) is to fake them into seasonal numbering by year:

The Daily Show/
├── Season 2024/
│   ├── The Daily Show S2024E001 - 2024-01-02.mkv
│   └── The Daily Show S2024E002 - 2024-01-03.mkv

Episode number = day-of-year (001366). Ugly but parser-clean. If the metadata provider (TVDB) supports the date-based show, NFO sidecars can override the episode title to the actual airdate.

2.6 Scrapers

  • Primary: TheTVDB.
  • Secondary: TheMovieDb (TMDb has good TV coverage too).
  • Order matters: Library options → Metadata Fetchers → drag the order. For Futurama on this deploy we used TMDB primary because TVDB had stale episode-still URLs.
  • Image: TVDB ships posters and episode stills; TMDB has higher-resolution backdrops; Fanart.tv has clearart + clearlogo.

2.7 Edge cases

  • Series whose name STARTS with a year (e.g. "1923") — wrap in folder 1923 (2022)/ so the parser doesn't confuse the series-name year with the disambiguation year.
  • Shows that re-run/reboot (Doctor Who, Battlestar Galactica) — keep reboots in separate folders, year disambiguation is mandatory: Doctor Who (1963)/ and Doctor Who (2005)/.
  • Mini-series / limited series — treat as TV, single season is fine (Chernobyl (2019)/Season 01/...).
  • Episode title inside filename is ignored once the series is identified; TMDB/TVDB title overwrites it (see doc 02 § 4).
  • (year) is required only at the series level, not on every episode. Including it on every episode is harmless but verbose.

3. Anime

Anime is the area with the highest "scraper picks the wrong thing" risk because TVDB / TMDB / AniDB / AniList disagree on how to slice multi-cours shows into seasons. Two distinct strategies — pick one per show, never mix.

3.1 Strategy A — TVDB seasonal numbering (default for this deploy)

Use this when:

  • Show has ≤ 100 episodes.
  • TVDB's season split matches the official Blu-ray / streaming split.
  • You're not running Shoko.

Folder structure

/media/anime/
├── Cowboy Bebop (1998)/
│   └── Season 01/
│       ├── Cowboy Bebop (1998) S01E01.mkv
│       └── Cowboy Bebop (1998) S01E02.mkv
└── Mushishi (2005)/
    ├── Season 01/
    └── Season 02/                       ← Mushishi Zoku Shou maps to S02 on TVDB

Filename pattern

Identical to TV shows § 2.2. Include (year) of first broadcast.

Edge case — episodes >99 in a season

S01E100 works for the parser. S01E001 (3-digit) also works — see Issue #17 in jellyfin-plugin-anime. 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: homevideoscritical. 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 videoshomevideos 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 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 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 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

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):

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:

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>:

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 (§ 17).
  2. Year is in (YYYY) and matches the actual release year.
  3. Folder name byte-for-byte matches the filename prefix (movies multi-version).
  4. No forbidden chars (< > : " / \ | ? *).
  5. Per-item folder exists (no loose files in library root, except music videos).
  6. tvshow.nfo / movie.nfo exists IFF you needed to override the scraper.
  7. Subtitles use <basename>.<lang>.srt (doc 03).
  8. Scan: curl -s -X POST -H "X-Emby-Token: $TOKEN" https://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
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.