Self-hosted tracker for series, movies and anime — with games, books and more planned. Built as a TV Time replacement you fully own: your data lives in your own PostgreSQL, catalogues come live from TMDB (movies & series) and AniList (anime).
- Monorepo pnpm workspaces, 100% TypeScript
apps/api— NestJS + Prisma + PostgreSQL (JWT auth, rotating refresh tokens)apps/web— SvelteKit PWA (installable, dark UI), talks to the APIpackages/shared— DTOs/enums shared between front and back
Key design choice: the database is an on-demand cache. Searching queries TMDB/AniList live; a media is persisted (with its seasons, episodes and external IDs — TMDB/AniList/TVDB/IMDB) only when a user tracks it. Episode watches are stored one row per viewing, so rewatches are first-class.
cp .env.example .env
# Edit .env: set POSTGRES_PASSWORD, both JWT secrets and TMDB_API_TOKEN.
docker compose up -d --buildThen open http://localhost:8080 (or WEB_PORT), create an account, done.
On a NAS, set PUBLIC_API_URL and WEB_ORIGIN to the host's address
(e.g. http://nas.local:3000/api and http://nas.local:8080).
Upgrades: git pull && docker compose up -d --build — database migrations
run automatically when the API boots.
Create a free account on themoviedb.org, then copy the API Read Access
Token (v4) from Settings → API into TMDB_API_TOKEN. Anime search (AniList)
works without any key.
Password reset, email verification, account change confirmations and email
alerts for new episodes all go through SMTP_* in .env. Empty = email is
silently disabled, everything else still works (the reset-password flow just
has no way to reach you).
Brevo has a free SMTP relay (300 emails/day, no
credit card): create an account (personal accounts are fine, no company or
website required), then SMTP & API in the sidebar for SMTP_HOST,
SMTP_USER and SMTP_PASS.
Install it as a PWA on your phone while the stack keeps running on your computer, reachable from anywhere. A public HTTPS URL is required (the service worker and Web Push refuse plain HTTP on a real device); ngrok provides one by tunnelling to the local proxy. Your computer must stay on.
-
Create a free ngrok account, then claim your one free static domain at https://dashboard.ngrok.com/domains (a stable URL is required for an installed PWA). Copy
ngrok.example.ymltongrok.ymland fill in your authtoken + domain. -
Set that domain once in
.env— the override derivesPUBLIC_API_URL,WEB_ORIGINand the ngrok header from it:NGROK_DOMAIN=your-domain.ngrok-free.app
-
Start the stack with the ngrok override, then the tunnel:
docker compose -f docker-compose.yml -f docker-compose.ngrok.yml up -d --build ngrok start tracklore
-
Open the domain on your phone → browser menu → Add to home screen.
On ngrok's free tier the first page load shows a one-time warning page you
click through; API calls skip it via PUBLIC_NGROK. For daily use by more than
one person, host the app publicly instead (VPS or a PaaS).
pnpm install
docker run -d --name tracklore-dev-db -e POSTGRES_USER=tracklore \
-e POSTGRES_PASSWORD=tracklore -e POSTGRES_DB=tracklore \
-p 5433:5432 postgres:17-alpine
cp apps/api/.env.example apps/api/.env # then add your TMDB token
pnpm --filter @tracklore/api exec prisma migrate dev
pnpm --filter @tracklore/shared build
pnpm dev # api on :3000, web on :5173Tests:
pnpm --filter @tracklore/api test # unit (provider mapping)
pnpm --filter @tracklore/api test:e2e # full API flow, isolated "e2e" schema- P1 — MVP ✓: auth, search, tracking, episode progress, PWA, Docker
- P1.5 — TV Time import ✓: interactive reconciliation (analyze → review collection by collection → commit), matched through TVDB IDs, with manual overrides. Source-agnostic pipeline, ready for more import sources.
- P2 — push notifications ("new episode out") + Capacitor.
In-app notifications, a periodic scan/refresh of tracked shows and Web Push
(VAPID, service-worker
pushhandler, per-device subscriptions) are shipped ✓. Email is shipped ✓: password reset, email verification, account change confirmations and (opt-in) new-episode alerts, via SMTP — see "Email" above. Mobile access is shipped ✓: the app installs as a PWA, and a ready-made ngrok setup (single-origin Caddy proxy) exposes the local stack to your phone from anywhere — see "Mobile access" above. The native (Capacitor) wrapper is still to do. - P3 (current) — games & books modules: games (IGDB, library + statuses +
playtime + Steam import) and books (Google Books/Open Library, library +
reading progress + StoryGraph import) are built, with per-domain stats and
community ratings. A unified global search covers all three domains, and
enabledDomainsis enforced server-side on search/stats and filters notifications. Manual match-correction is available on Steam and StoryGraph imports. Remaining: more import sources (Goodreads, Babelio, Backloggd), e2e coverage for games/books. - P4 — social (friends, activity feed, shared lists)
- P5 — hosted offer / entitlements (open core)
AGPL-3.0 — self-host freely; run it as a service, share your changes.