Skip to main content

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

ShowsShow 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

MusicArtist/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 is unsubscribePodcast); 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):

ActionWhen to use itCost
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).
rebuildSearchIndexRebuild the Typesense index into a fresh collection (after enabling search or changing languages).Reads the whole database once; search stays available.
refreshPodcastsRe-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