Skip to content

youngc01/curatio-server

Repository files navigation

Curatio Server

Your media. Your server. Your rules.

Curatio Server is the self-hosted backend that powers the Curatio app. Point it at your own video files and every device running Curatio — Apple TV, iPhone, iPad, Mac — gets a fully featured streaming experience without any cloud subscription, external service, or third-party account.

Your library never leaves your home.


How it works

Curatio Server runs on any machine you control — a home server, a NAS, a VPS, a spare Mac mini. It scans your local video files, pulls rich metadata and artwork from TMDB, and serves everything directly to the Curatio app over your network or the internet. The app pairs with the server in seconds using a simple on-screen code, then your full library appears just like any streaming service — hero images, cast info, episode guides, Continue Watching, everything.

Your files on disk
       ↓
Curatio Server (this project)
       ↓  metadata from TMDB
       ↓  streams directly
Curatio app on Apple TV / iPhone / iPad / Mac

Features

  • Native Curatio app support — pairs instantly with the Curatio iOS and tvOS app using the device-code pairing flow; no manual URL entry required on the TV
  • Automatic library scanning — point at your Movies and TV folders; the server matches files to TMDB metadata and artwork automatically
  • Direct streaming with seek support — files are served with HTTP range requests so the Curatio player can jump to any point instantly, no buffering from the start
  • Continue Watching — the server tracks exactly where you stopped across every device; pick up on your iPhone where you left off on Apple TV
  • Invite-gated accounts — you create one-time invite codes; friends and family register with them; no open sign-ups, no strangers in your library
  • Two-factor authentication — optional TOTP (authenticator app) 2FA for every account
  • Web interface — a full browser UI is built in, so you can browse and play directly from any browser as well as the Curatio app
  • Admin dashboard — manage users, create invites, trigger library scans, and monitor your server from /admin
  • TMDB-powered search — search your library and discover titles not yet in it, with clear labelling of what you own and what you don't

Requirements

Requirement Notes
Docker + Docker Compose The recommended way to run Curatio Server
TMDB API key Free — themoviedb.org/settings/api
Your video files Movies and TV shows organised in standard folders (see below)

Quick start

1. Get the server running

git clone https://github.com/your-org/curatio-server.git
cd curatio-server
cp .env.example .env

Open .env and fill in these values:

TMDB_API_KEY=your_key_here
MASTER_PASSWORD=choose_a_strong_password
SECRET_KEY=generate_this_below
BASE_URL=https://yourserver.com
MEDIA_ROOT=/path/to/your/media

Generate a secure SECRET_KEY:

python3 -c "import secrets; print(secrets.token_urlsafe(48))"

2. Mount your media and start

In docker-compose.yml the MEDIA_ROOT path from your .env is automatically mounted into the container. Just start it:

docker-compose up -d

The server will be available at http://localhost:8000. The library scan runs on startup — check the logs to watch progress:

docker-compose logs -f app | grep -E "scan|movie|episode"

3. Create your admin account and first invite

  1. Open http://localhost:8000/admin and sign in with your MASTER_PASSWORD
  2. Go to the Invites tab and click Create Invite
  3. Copy the invite code — you'll use it to register your first user account

4. Register and pair the Curatio app

  1. Open http://localhost:8000 in any browser and register using your invite code
  2. Open the Curatio app on your Apple TV or iPhone
  3. The app will show a 6-character code on screen and ask you to visit your server's pairing page
  4. Go to https://yourserver.com/pair on your phone, sign in, and enter the code
  5. The Curatio app connects immediately — your full library appears on screen

That's it. Every device you pair this way gets its own account with independent watch progress.


Organising your media

Curatio Server uses the same folder structure as Plex, so if you're already organised that way you're ready to go.

/media/
├── Movies/
│   ├── The Dark Knight (2008)/
│   │   └── The Dark Knight (2008).mkv
│   ├── Inception (2010).mp4            ← flat layout works too
│   └── Parasite.2019.1080p.mkv         ← dot separators are fine
└── TV/
    ├── Breaking Bad/
    │   ├── Season 01/
    │   │   ├── Breaking Bad - S01E01 - Pilot.mkv
    │   │   └── Breaking Bad - S01E02.mp4
    │   └── Season 02/
    │       └── S02E01.mkv              ← season folders are optional
    └── Severance/
        └── Severance S01E01.mkv        ← SxxEyy anywhere in the filename works

Movies: the server looks for Title (Year) in the filename and searches TMDB with that. Separators like dots and underscores are normalised to spaces automatically.

TV shows: the folder name inside TV/ is used as the show name. Episodes are matched by finding SxxEyy (e.g. S01E04) anywhere in the filename. Files without a recognisable episode code are skipped with a log message.


Adding files after the server is running

When you add new files to your media folders, trigger a rescan from the admin dashboard under Library → Scan Now, or from the command line:

curl -X POST https://yourserver.com/admin/api/scan \
  -H "Cookie: admin_session=YOUR_ADMIN_TOKEN"

The scan only processes new and changed files — it won't re-fetch metadata for things already in the database.


Supported file formats

Curatio Server streams files as-is without transcoding, so what plays depends on the device. The Curatio app uses AVKit on Apple platforms, which has native support for:

Format Notes
MP4 (H.264 / AAC) Best compatibility — works everywhere
MP4 (H.265 / HEVC) Supported on Apple Silicon and A9+ devices
MKV Supported on newer Apple devices; may not play on older hardware
WebM Supported in the web browser UI
HLS (.m3u8) Natively supported by AVKit

If you have a large collection of MKV files with HEVC or DTS audio that won't play on older devices, the recommended solution is to add an FFmpeg transcoder as a sidecar — Curatio Server is designed to sit alongside one if needed.


Exposing your server to the internet

For access outside your home network you'll need to expose the server publicly. A few options:

Behind a reverse proxy (recommended)

Use the included nginx.conf as a starting point. Put your domain behind Cloudflare or use Let's Encrypt for TLS.

Tailscale or WireGuard

Run the server on your home network and access it over a private VPN — no ports exposed publicly, and the Curatio app works fine over Tailscale.

Direct port forwarding

Forward port 443 from your router to the machine running Curatio Server and set BASE_URL to your public domain.

Whatever method you use, always run with HTTPS in production — the FORCE_SECURE_COOKIES=true setting in .env ensures session cookies are always secure.


Admin dashboard

The admin panel lives at /admin. Log in with your MASTER_PASSWORD (not your user account password — these are separate). From here you can:

  • Overview — see total movies, TV shows, episodes, and active users at a glance
  • Users — enable or disable accounts, promote users to admin, or remove them entirely
  • Invites — create single-use invite codes with optional expiry dates for new user registration
  • Library — trigger a full rescan and see detailed file counts

The admin panel is separate from user accounts by design, so you can manage the server without exposing your personal account.


API reference

The Curatio app communicates with the server over a clean REST API. You can also call it directly from scripts or other tools.

Authentication

Method Route Description
POST /auth/register Create an account using an invite code
POST /auth/login Sign in with email and password (supports TOTP)
POST /auth/logout End the current session
GET /auth/me Return the current user's profile
POST /auth/setup-2fa Begin two-factor authentication setup
POST /auth/confirm-2fa Activate 2FA with a TOTP code

Device pairing (Curatio app)

Method Route Description
POST /auth/device/code Generate a pairing code for the Curatio app
GET /auth/device/{token}/status Poll for pairing completion
POST /auth/device/claim Claim a code after signing in (used by /pair page)
GET /pair The pairing web page the app directs users to

Curatio app protocol

All routes below use {user_key} — a permanent token the app stores after pairing.

Method Route Description
GET /{user_key}/manifest.json App manifest — catalogs and capabilities
GET /{user_key}/catalog/{type}/{id}.json Catalog row (supports search and skip)
GET /{user_key}/meta/{type}/{id}.json Full metadata — cast, episodes, similar
GET /{user_key}/stream/{type}/{id}.json Stream URL for the given item
GET /{user_key}/file/{file_key} Range-aware video delivery
POST /{user_key}/scrobble Report playback progress

Web library API

Method Route Description
GET /api/library/movies Paginated movie list
GET /api/library/tv TV shows (one entry per show)
GET /api/library/tv/{tmdb_id}/episodes All episodes for a specific show
GET /api/continue-watching In-progress items for the current user
GET /api/recently-added Most recently added files
GET /api/search?q=… Search local library and TMDB
GET /api/detail?tmdb_id=…&media_type=… Full detail page data
GET /stream/{file_key} Browser video stream (cookie-authenticated)

Admin

Method Route Description
POST /admin/api/login Sign in with master password
GET /admin/api/stats Library and user statistics
POST /admin/api/scan Trigger a library scan
GET /admin/api/users List all users
POST /admin/api/users/{id}/toggle Enable or disable a user
POST /admin/api/users/{id}/admin Toggle admin privileges
DELETE /admin/api/users/{id} Remove a user
GET /admin/api/invites List invite codes
POST /admin/api/invites Create an invite code
DELETE /admin/api/invites/{code} Delete an invite

Security

  • File paths are never exposed. The streaming endpoints use opaque random tokens (file_key) that the server resolves to disk paths internally. It is structurally impossible for a client to request an arbitrary path.
  • All streaming is authenticated. Requests without a valid session or user_key receive a 404 — nothing is served anonymously.
  • Media is mounted read-only. The Docker Compose configuration mounts your media folder with :ro, so the server process cannot modify your files even if something goes wrong.
  • TOTP secrets are encrypted in the database. Secrets are never stored in plain text.
  • Admin and user sessions are separate. Compromising a user account doesn't affect admin access, and vice versa.

Project structure

curatio-server/
├── app/
│   ├── main.py             — FastAPI app, all web routes, embedded browser UI
│   ├── client_protocol.py  — Curatio app protocol (manifest, catalog, meta, stream, pairing)
│   ├── models.py           — Database models
│   ├── config.py           — Environment variable configuration
│   ├── database.py         — Database engine and session management
│   ├── auth.py             — Password hashing, TOTP, session tokens
│   ├── tmdb.py             — TMDB metadata client and cache
│   ├── scanner.py          — Library scanner
│   └── streaming.py        — Range-aware file delivery
├── Dockerfile
├── docker-compose.yml
├── nginx.conf              — Production reverse proxy config
├── requirements.txt
└── .env.example

License

MIT — free to use, modify, and self-host.


Desktop installers (Windows .exe and macOS .dmg)

Curatio Server can also be shipped as a desktop app for people who do not want to install Docker. The desktop build bundles the FastAPI server, a small native setup/launcher UI, and a local SQLite database.

What the desktop launcher does

  • Shows a first-run setup UI for the TMDB API key, admin password, media folder, Movies/TV folder names, and local port.
  • Saves settings in the current user's app data folder instead of the install directory.
  • Runs the server on http://127.0.0.1:8000 by default and opens the web UI in the user's browser.
  • Stores the desktop SQLite database and log file next to the user's launcher settings.

Build a Windows installer

Build on Windows so PyInstaller can produce a Windows executable:

.\scripts\build_windows_installer.ps1

Requirements:

  • Python 3.11+
  • Inno Setup with ISCC.exe on PATH

Output:

dist\installer\CuratioServerSetup.exe

Build a macOS DMG

Build on macOS so PyInstaller can produce a signed/notarizable .app bundle for macOS:

./scripts/build_macos_dmg.sh

Requirements:

  • Python 3.11+
  • Xcode command line tools
  • hdiutil (included with macOS)

Output:

dist/CuratioServer.dmg

Note: The included scripts create unsigned local builds. For public distribution, sign and notarize the .app/.dmg on macOS and code-sign the Windows installer/executable with your publisher certificate.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages