Skip to main content

Architecture overview

Ister Server is a self-hosted media server (in the vein of Plex/Jellyfin) built with Spring Boot 4 on Java 25. It scans media libraries — movies, TV shows, music, books, comics and podcasts — fetches metadata from external providers (TMDB, MusicBrainz, Open Library, Wikipedia/Wikidata, iTunes), and streams HLS-transcoded media to clients over REST and GraphQL. All significant work runs asynchronously through RabbitMQ; PostgreSQL (schema managed by Flyway) is the source of truth; FFmpeg (driven via the Jaffree library) does the transcoding; Typesense provides optional full-text search. Multiple nodes can form one deployment, where one node transcodes media owned by another.

The production artifact is a GraalVM native image, which constrains how features are toggled and how reflection/resources are configured — see chapter 8.

Modules

See the module diagram for the picture.

ModuleResponsibility
serverSpring Boot entry point (@SpringBootApplication, scanBasePackages="app.ister")
coreShared infra: the Handle<T> interface, MessageQueue base names, MessageSender, event-data DTOs, Jaffree utilities
databaseJPA entities, repositories, and the EventType enum
apiREST controllers, GraphQL schema and resolvers
diskFilesystem scanning, startup tasks, file-type event handlers (media, audio, image, subtitle, NFO, epub, comic)
workerAsync metadata handlers: TMDB, MusicBrainz, Open Library, Wikipedia/Wikidata, podcast feeds
searchOptional Typesense full-text search (package app.ister.search)
transcoderFFmpeg-based HLS transcoding

Dependency flow: server{api, disk, worker, search, transcoder}coredatabase. core declares api project(':database'), so entities, repositories and enums arrive transitively. That is the direction, and it is easy to get backwards: database depends on nothing internal, and nothing may ever make it depend on core. api additionally depends directly on search and transcoder.

Split-package warning: core/ and database/ both contribute to package app.ister.core.*. Entities, repositories and EventType live in database/; the Handle/MessageQueue/status/config infrastructure lives in core/. The package name alone does not tell you which module a class is in.

Tech stack

ConcernTechnology
FrameworkSpring Boot 4, Java 25, Gradle multi-module
MessagingRabbitMQ (work queues + a status fanout exchange)
DatabasePostgreSQL, Flyway migrations (database/.../db/migration/, forward-only)
TranscodingFFmpeg via Jaffree, HLS output
SearchTypesense (optional, runtime-toggled)
AuthOAuth2 JWT resource server (Keycloak-compatible OIDC) + short-lived stream tokens
Prod artifactGraalVM native image (nativeCompile, Dockerfile.native)
Quality gateSonarCloud + Jacoco (no separate linter/formatter)

Chapters

  1. Event systemHandle<T>, EventType vs MessageQueue, queue scoping, retries and the dead-letter queue
  2. Scanning and analysis — startup bootstrap, library scans, analyze flows, the BlurHash sweep
  3. Media types and metadata — per-type pipelines: film/TV, music, books, comics, podcasts, NFO, languages
  4. Transcoding — lazy HLS segments, one pass per quality, concurrency, pre-transcode, multi-node
  5. Continue watching and live status — the precomputed continue_watching table and the status fanout
  6. Search — Typesense indexing, reindex with alias swap, multilingual schema
  7. API and auth — REST/GraphQL surface, subscriptions, OIDC and stream tokens
  8. Native image and testing — GraalVM constraints, Flyway discipline, test setup, CI

Diagrams

Modules · Startup · Scan flow · Analyze flows · Transcode flow · Search flow · Continue-watching flow · Full event overview