No description
Find a file
Andrew Todd 7058829e60
All checks were successful
Build & Push Docker Image / build (push) Successful in 59s
ci: bump workflow only edits files, no tag or release
2026-05-21 01:57:00 -04:00
.claude feat: docker build, forgejo workflow 2026-05-20 19:52:00 -04:00
.forgejo/workflows ci: bump workflow only edits files, no tag or release 2026-05-21 01:57:00 -04:00
db feat(wip): initial implementation 2026-05-20 17:11:15 -04:00
dev feat(wip): initial implementation 2026-05-20 17:11:15 -04:00
docs fix(feed): split For You and rate cards by media type 2026-05-21 01:39:22 -04:00
scripts build: compile TypeScript in Docker build stage, remove tsx from runtime 2026-05-21 01:23:05 -04:00
src fix(feed): split For You and rate cards by media type 2026-05-21 01:39:22 -04:00
tests fix(feed): split For You and rate cards by media type 2026-05-21 01:39:22 -04:00
.env.example feat(wip): initial implementation 2026-05-20 17:11:15 -04:00
.gitignore build: compile TypeScript in Docker build stage, remove tsx from runtime 2026-05-21 01:23:05 -04:00
.npmrc feat(wip): initial implementation 2026-05-20 17:11:15 -04:00
.nvmrc feat(wip): initial implementation 2026-05-20 17:11:15 -04:00
AGENTS.md docs(agents): add commit message style convention 2026-05-21 00:38:24 -04:00
biome.json feat(wip): initial implementation 2026-05-20 17:11:15 -04:00
CLAUDE.md chore: no Co-Authored-By in commits (Claude guardrail) 2026-05-20 22:26:46 -04:00
docker-compose.yml chore: release 0.1.2 2026-05-21 03:45:16 +00:00
Dockerfile build: compile TypeScript in Docker build stage, remove tsx from runtime 2026-05-21 01:23:05 -04:00
knexfile.ts build: compile TypeScript in Docker build stage, remove tsx from runtime 2026-05-21 01:23:05 -04:00
lefthook.yml feat(wip): initial implementation 2026-05-20 17:11:15 -04:00
package-lock.json 0.2.0 2026-05-21 05:53:58 +00:00
package.json 0.2.0 2026-05-21 05:53:58 +00:00
README.md docs: add README, docker-compose, and release workflow 2026-05-20 22:51:15 -04:00
tsconfig.json feat(wip): initial implementation 2026-05-20 17:11:15 -04:00

watchcraft

Self-hosted media recommendation app for Jellyfin libraries. Rate titles from your library, discover similar content via TMDB, and optionally request media through Jellyseerr or get one-line AI explanations via Ollama.

Features

  • Star-rating UI for your Jellyfin library
  • Discovery feed powered by TMDB recommendations
  • Request buttons via Seerr / Jellyseerr (optional)
  • One-sentence AI explanations via Ollama (optional)
  • Multi-user support with shared audiences
  • Three auth modes: local accounts, SSO proxy header, or single implicit user
  • SQLite (zero-config), PostgreSQL, or MariaDB

Requirements

Quick start

cp docker-compose.yml docker-compose.local.yml  # or edit in place
# Fill in JELLYFIN_URL, JELLYFIN_API_KEY, TMDB_API_KEY, SESSION_SECRET
docker compose up -d
# Open http://localhost:3000 — setup wizard runs on first visit (local auth)

Generate a session secret:

openssl rand -hex 32

Configuration

Required

Variable Description
JELLYFIN_URL Base URL of your Jellyfin instance (e.g. http://jellyfin:8096)
JELLYFIN_API_KEY Jellyfin API key
TMDB_API_KEY TMDB v3 API key (free tier is sufficient)
SESSION_SECRET Random string for signing session cookies — use a long random value in production

Auth

Variable Default Description
AUTH_MODE local local | proxy | none
AUTH_HEADER X-authentik-username Header to read username from when AUTH_MODE=proxy

Auth modes:

  • local — bcrypt password auth with Express sessions. A setup wizard runs on first visit; the first account created is always admin.
  • proxy — reads the username from AUTH_HEADER. Use with Authentik, Authelia, or any other SSO proxy. Set AUTH_MODE=proxy and AUTH_HEADER to match your proxy's header name.
  • none — single implicit user named default. For trusted-network-only deployments with no login UI.

Database

Variable Default Description
DB_CLIENT better-sqlite3 better-sqlite3 | pg | mysql2
DB_SQLITE_PATH ./data/db.sqlite SQLite file path (SQLite only)
DB_HOST Hostname (PostgreSQL / MariaDB)
DB_PORT Port (PostgreSQL / MariaDB)
DB_NAME Database name
DB_USER Database user
DB_PASSWORD Database password

SQLite is zero-config and the default. PostgreSQL and MariaDB are useful if you want to run multiple watchcraft users off a shared database instance.

Optional integrations

Variable Description
SEERR_URL Seerr / Jellyseerr base URL — enables Request buttons on discovery cards
SEERR_API_KEY Seerr API key
OLLAMA_URL Ollama base URL — enables one-sentence recommendation explanations
OLLAMA_MODEL Ollama model to use (default: llama3.2:3b)

Cache TTLs

All intervals have sensible defaults. Override if needed.

Variable Default Description
CACHE_LIBRARY_HOURS 6 How often to re-sync the Jellyfin library
CACHE_HISTORY_HOURS 1 How often to re-sync play history
CACHE_TMDB_DAYS 30 How long TMDB recommendation data is cached
CACHE_METADATA_DAYS 90 How long TMDB metadata is cached
CACHE_SEERR_MINUTES 15 How often to poll Seerr for request status
CACHE_MAX_REC_LINKS 50000 Max recommendation graph edges before cache pruning

Misc

Variable Default Description
PORT 3000 HTTP port
TZ Etc/UTC Timezone (used for cron scheduling)
SESSION_STORE_PATH ./data/sessions.sqlite Path for the session SQLite store
LOG_LEVEL info trace | debug | info | warn | error | fatal
LOG_FORMAT pretty pretty (human-readable) | json (for log aggregators)

Data & persistence

The app writes two files to /app/data:

  • db.sqlite — application database
  • sessions.sqlite — session store

Mount /app/data to a persistent volume so data survives container restarts. The example docker-compose.yml uses a named Docker volume (watchcraft-data).

Database migrations run automatically on every startup — no manual migration step needed.

Upgrading

docker compose pull
docker compose up -d

Migrations run on startup; no other steps required.

Health check

GET /health returns 200 OK when the app is ready.