Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/rivenmedia/riven/llms.txt

Use this file to discover all available pages before exploring further.

Riven is built around a single-process Python application that coordinates every stage of media automation — from discovering what to fetch, through to presenting finished files inside your media server. Understanding how these stages connect helps you configure Riven correctly, diagnose problems faster, and extend behaviour when needed.

The Pipeline at a Glance

Every media item travels through the same ordered set of stages. Each stage is handled by a dedicated service; the EventManager moves items between them by dispatching events on a thread-safe queue.
Content Sources  →  Indexer  →  Scraper  →  Downloader  →  Filesystem (VFS)  →  Updater
(Overseerr, Plex     (TMDB /      (RTN-        (Debrid        (RivenVFS          (Plex /
 Watchlist,           TVDB         ranked       provider:       FUSE mount)        Jellyfin /
 Listrr, Mdblist,     metadata)    torrents)    Real-Debrid,                       Emby)
 Trakt)                                         AllDebrid,
                                                DebridLink)
StageService classWhat it does
Content sourcesOverseerr, PlexWatchlist, Listrr, Mdblist, TraktContentPoll external services for new requests; emit Requested items
IndexerIndexerServiceFetch title metadata (TMDB / TVDB) and advance item to Indexed
ScraperScrapingQuery configured scraper backends; score torrents with RTN; advance to Scraped
DownloaderDownloaderSend the winning torrent to the debrid provider; advance to Downloaded
FilesystemFilesystemServiceRivenVFSRegister the debrid stream in the FUSE virtual filesystem; advance to Symlinked
UpdaterUpdaterTrigger a library scan in Plex / Jellyfin / Emby; advance to Completed
Post-processingPostProcessingRun any configured post-processing tasks after Completed
NotificationsNotificationServiceDeliver Apprise / SSE alerts at configured state transitions
“Symlinked” is the historical state name for the filesystem step. In practice, no symlinks are created — items are registered in RivenVFS and streamed on demand.

The Program Class

Program (defined in program/program.py) is a threading.Thread subclass and the single owner of all runtime state.
class Program(threading.Thread):
    def __init__(self):
        self.em = EventManager()           # event queue + job dispatcher
        self.scheduler_manager = ProgramScheduler(self)  # APScheduler wrapper
        self.services = None               # populated in initialize_services()
On startup, Program.start():
  1. Registers settings observers so services are re-initialised whenever the settings file changes.
  2. Creates the data directory and writes default settings if they are missing.
  3. Bootstraps all external API clients (bootstrap_apis()).
  4. Validates the database connection; creates the database if it does not exist yet.
  5. Runs Alembic database migrations (run_migrations()).
  6. Instantiates all services into the Services dataclass.
  7. Starts the APScheduler background scheduler.
  8. Calls super().start() to launch the main event loop thread.

Services Dataclass

All services are held together in a typed dataclass so that any part of the codebase can retrieve them via dependency injection:
@dataclass
class Services:
    # Content sources
    overseerr:      Overseerr
    plex_watchlist: PlexWatchlist
    listrr:         Listrr
    mdblist:        Mdblist
    trakt:          TraktContent

    # Core pipeline
    indexer:        IndexerService
    scraping:       Scraping
    updater:        Updater        # Plex / Jellyfin / Emby
    downloader:     Downloader     # Real-Debrid / AllDebrid / DebridLink
    filesystem:     FilesystemService  # wraps RivenVFS

    # Auxiliary
    post_processing: PostProcessing
    notifications:   NotificationService
Services expose enabled and initialized properties. The pipeline only routes items to a service when both are True. If no content service is initialised at startup, Riven logs a warning and waits for items to be added manually via the API.

Event-Driven Processing

The main loop (Program.run()) dequeues one event at a time and calls process_event() from state_transition.py:
while self.initialized:
    event = self.em.next()                          # blocking dequeue
    processed = process_event(
        event.emitted_by,
        existing_item,
        event.content_item,
        event.overrides,
    )
    # submit items to next_service via em.submit_job()
process_event inspects item.last_state and returns the next Service plus the list of items to forward. State transitions are deterministic:
Requested  → IndexerService
Indexed    → Scraping
Scraped    → Downloader
Downloaded → FilesystemService
Symlinked  → Updater
Completed  → PostProcessing
Items in Paused or Failed states are skipped entirely until they are explicitly retried or unpaused through the API.

Database

Riven uses PostgreSQL as its primary store. The ORM layer is SQLAlchemy with declarative models; schema evolution is managed by Alembic migrations that run automatically on every start. Key tables include MediaItem (and its polymorphic sub-tables Movie, Show, Season, Episode), FilesystemEntry, Stream, and StreamRelation.
The database connection string is configured via the DATABASE_URL environment variable (or the equivalent settings key). Riven will attempt to create the database if it does not exist.

Scheduler

ProgramScheduler wraps APScheduler and registers two categories of jobs:
  • Content polling — each enabled content service is polled on a configurable interval (default: every 30 minutes) to discover new requests.
  • Retry / maintenance — stale Scraped, Downloaded, and Indexed items are periodically re-queued; the schedule backs off exponentially after repeated failures (configurable via scraping.after_2, after_5, after_10).

REST API

Riven exposes a FastAPI application on port 8080. All routes are grouped under /api/v1 and require an API key passed as the X-API-Key header (or apikey query parameter).
GET  /api/v1/items          # list / filter items
POST /api/v1/items/add      # add item by IMDb / TMDB ID
POST /api/v1/items/retry    # re-queue failed items
POST /api/v1/items/pause    # pause items
POST /api/v1/items/unpause  # unpause items
GET  /api/v1/scrape         # manual scrape with ranking overrides
GET  /api/v1/mount          # list all files in the VFS mount
GET  /api/v1/vfs_stats      # streaming statistics
Interactive Swagger docs are available at http://localhost:8080/docs.

Notifications

Riven ships two notification channels:
  • Apprise — delivers alerts to any service supported by the Apprise library (Slack, Telegram, Discord, email, etc.) when configured via notifications.apprise_url.
  • Server-Sent Events (SSE) — the frontend (and any connected client) subscribes to GET /api/v1/events to receive real-time state change notifications without polling.

Build docs developers (and LLMs) love