Libraries and media layout
A library is a named collection of one type (MOVIE, SHOW, MUSIC, BOOK, COMIC or
PODCAST). A directory is a path on disk attached to a library; a library can span several
directories, even across nodes. The scanner decides what a file is from its path, so the
on-disk layout matters.
Configuring libraries and directories
Indexed properties, from disk/src/main/resources/disk.properties (as env vars:
APP_ISTER_DISK_LIBRARIES_0_NAME etc.):
app.ister.disk.libraries[0].name=shows
app.ister.disk.libraries[0].type=SHOW
app.ister.disk.libraries[1].name=books
app.ister.disk.libraries[1].type=BOOK
app.ister.disk.directories[0].name=disk1
app.ister.disk.directories[0].path=/disk1
app.ister.disk.directories[0].library=shows
app.ister.disk.directories[1].name=disk2
app.ister.disk.directories[1].path=/disk2
app.ister.disk.directories[1].library=shows
Directory names must be unique across the whole cluster — they name the per-directory work
queues (Multi-node). Write paths without a trailing slash and keep them
stable: the path is stored verbatim in the database and compared as a string prefix, so changing
/disk1 to /disk1/ later counts as a path change. At startup this config is applied
asymmetrically: a directory's path is updated in the database, but a library is only
created — changing libraries[n].type after the first start silently does nothing (delete the
library and re-create it instead). And if a directory name is already claimed by another node,
startup aborts (Multi-node).
Expected layout per type
This is the short version; chapter 8 is the full naming reference (exact patterns, accepted extensions, special files, and common mistakes).
Shows — Show Name (year)/Season NN/sNNeNN.mkv:
The Wire (2002)/Season 01/s01e01.mkv
Movies — one file (or a folder) per movie, name ending in the year:
Heat (1995)/Heat (1995).mkv
Music — Artist/Album/track:
Miles Davis/Kind of Blue/01 So What.flac
Books — one logical book per author, in two interchangeable forms that converge on the same book entry: an epub directly under the author, and/or an audiobook folder of numbered chapters:
Terry Pratchett/Guards! Guards!.epub
Terry Pratchett/Guards! Guards!/001_Chapter 1.mp3
Read-aloud (EPUB 3 media-overlay) epubs are detected automatically from the epub's contents, never from the filename.
Comics — series-first: {Series Name (optional year)}/Volume 27.cbz. Also .pdf and
.epub; loose patterns like attackontitan_vol27.pdf, series_issue8.pdf and name#3.cbz
are tolerated.
In SHOW and MOVIE libraries the recognised video containers are mkv and mp4; subtitles: .srt
next to the video (image subtitles inside mkv are extracted and OCR'd); local artwork: jpg/png;
.nfo files are read for metadata hints. Other library types accept their own extension lists —
see the naming reference.
Podcasts
A PODCAST library needs no directory at all — it is feed-based:
- Subscribe from the client (or the GraphQL
subscribePodcast(feedUrl)mutation — admin-only, as isunsubscribePodcast); the directory search in the client uses the free iTunes Search API. - Feeds refresh hourly; the newest episodes (default 3,
auto-download-count) are downloaded automatically into the cache directory, older ones on demand when a user plays them. - Downloads expire after 30 days (
podcast-retention-days) unless someone is mid-episode.
Scanning and analyzing
The maintenance mutations (also exposed in the client's admin screens):
| Action | When to use it | Cost |
|---|---|---|
scanLibraries(libraryId?) | New files were added — there is no filesystem watcher. Optionally scoped to one library. | Cheap; known files are skipped. |
refreshMetadata(MISSING, libraryId?) | Backfill: fetch metadata/artwork only where missing (e.g. after adding a TMDB key), recompute missing blur-hashes. | Cheap and idempotent; safe anytime. |
refreshMetadata(FORCE, libraryId) | Rebuild one library: wipe stored metadata, artwork and stream info, then re-fetch everything (e.g. after a wrong match, or to pick up newly added fields on old items). | Heavy: an external fetch per item, ffprobe per file. |
refreshMovie/Show/Episode/Person/Album/Track(id) | The same wipe-and-refetch for a single item (the ⋮ menu on its detail page). There is no per-item refresh for books, comics or podcasts. | One item (a show fans out to its episodes). |
rebuildSearchIndex | Rebuild the Typesense index into a fresh collection (after enabling search or changing languages). | Reads the whole database once; search stays available. |
refreshPodcasts | Re-fetch every subscribed feed now instead of waiting for the hourly refresh. Not admin-only. | Cheap (conditional GET per feed). |
downloadPodcastEpisode(episodeId) | Pull one older episode into the cache on demand. Not admin-only. | One download. |
All are asynchronous — they queue events and return immediately; progress is visible in the client's activity view. A scan does not re-fetch metadata for existing items, and a MISSING refresh does not touch items that are already complete. Details of the pipeline are in the architecture documentation.
Where to next
- Multi-node — directories spread over several servers
- Maintenance — what happens to caches over time