jake 1f8b3d8f37 feat: redesign dashboard stats cards with sparklines, icons, and period selection
- Replace horizontal scrolling stats list on native with a 2×2 grid layout; each card gets a color-coded icon, tint background, and an inline sparkline
- Add `Sparkline` component to `apps/native/src/components/ui/` (path-based SVG rendered via `react-native-svg`)
- Upgrade `StatsCard` (native) to support `icon`, `color`/`tintColor`/`bgColor` theming, `sparklineData`, and a `zeego` dropdown menu for switching the time period (today / this week / this month / this year)
- Wire `moviePeriod` / `episodePeriod` state in the home screen and fetch `dashboard.watchHistory` for each so counts and sparklines react to the selected period
- Rewrite `apps/web/src/components/dashboard/sparkline.tsx` as a self-contained SVG sparkline (no Recharts), and update `stats-display.tsx` accordingly
- Remove `apps/web/src/components/ui/chart.tsx` (Recharts wrapper) and drop the `recharts` dependency from `apps/web/package.json`
2026-03-19 11:42:30 -04:00
2026-03-01 11:52:03 -05:00

Sofa
Sofa — Self-hosted movie & TV tracker

GitHub License GitHub Actions Workflow Status GitHub Release GitHub top language Crowdin

Sofa is a self-hosted movie and TV tracker for nerds. Track what you've watched, discover what's next, and plug your data into your existing home media stack. All without anything leaving your homelab. 🍿

Title page screenshot

What it does

  • Track episode-level progress for TV series and pick shows back up from a dedicated "Continue Watching" view
  • Mark movies as watched to discover more like them
  • Rate titles, browse cast and crew, and get recommendations based on what you are already tracking
  • Search TMDB and explore trending movies and shows without leaving your own instance
  • Show streaming availability from TMDB's US provider data
  • Automatically log completed watches from Plex, Jellyfin, or Emby webhooks
  • Import existing watch history, ratings, and watchlists from Trakt, Simkl, or Letterboxd
  • Expose your watchlist as import lists for Sonarr and Radarr
  • Runs on SQLite with local image caching, built-in backups, and no external database requirement
  • Supports local accounts or OIDC SSO for private instances

Note

Sofa is extremely US-centric right now, in terms of streaming providers, content rating systems, etc. Contributions to address this are more than welcome!

Quick start

A minimal docker-compose.yml is provided in this repo. For most setups, the shortest path is:

  1. Copy the example environment file:
cp .env.example .env
  1. Fill in the required values:
TMDB_API_READ_ACCESS_TOKEN=your_tmdb_read_access_token
BETTER_AUTH_SECRET=generate_a_long_random_secret
BETTER_AUTH_URL=http://localhost:3000
  1. Start the container:
docker compose up -d
  1. Open http://localhost:3000 and create the first account. The first account becomes the admin automatically, and registration closes after that by default.

The included Compose file uses ghcr.io/jakejarvis/sofa:edge. If you prefer to pin releases, switch to a published version tag like ghcr.io/jakejarvis/sofa:<version> or use the matching jakejarvis/sofa:<version> image on Docker Hub. Multi-arch images are published for linux/amd64 and linux/arm64.

Required setup

TMDB

Sofa uses TMDB for metadata, posters, cast, recommendations, and streaming availability. You need a TMDB API Read Access Token before the app can do anything useful.

Create one here:

Tip

We want the API Read Access Token, not the shorter API key.

Auth secret

BETTER_AUTH_SECRET should be a long random string. To generate one:

npx @better-auth/cli@latest secret
# or
openssl rand -base64 32

Public URL

Set BETTER_AUTH_URL to the real external URL of your instance. This especially matters for login flows and OIDC callbacks behind a reverse proxy (like nginx or Traefik).

Configuration

Variable Required Notes
TMDB_API_READ_ACCESS_TOKEN Yes TMDB metadata and discovery
BETTER_AUTH_SECRET Yes Session and auth secret
BETTER_AUTH_URL Yes Public base URL of the app
DATA_DIR No Root data directory. Defaults to /data in the container
IMAGE_CACHE_ENABLED No Defaults to enabled. Set to false to use TMDB images directly instead of caching them locally
LOG_LEVEL No error, warn, info, or debug
OIDC_CLIENT_ID No Enable OIDC when set with the matching secret and issuer
OIDC_CLIENT_SECRET No OIDC client secret
OIDC_ISSUER_URL No OIDC issuer URL
OIDC_PROVIDER_NAME No Login button label. Defaults to SSO
OIDC_AUTO_REGISTER No Defaults to true
DISABLE_PASSWORD_LOGIN No Set to true to hide email/password login when OIDC is configured

See .env.example for the full list.

Integrations

Sofa ships with two kinds of integrations (for now): incoming watch activity and outgoing import lists.

Incoming watch activity

  • Plex: logs completed watches through a webhook URL generated in Sofa. Requires an active Plex Pass license.
  • Jellyfin: works through the Jellyfin Webhook plugin.
  • Emby: logs completed watches through webhooks. Requires Emby Server 4.7.9+ and an Emby Premiere subscription.

These integrations are user-specific, so each user can connect their own media server account and watch history.

Outgoing import lists

  • Sonarr: expose your Sofa TV watchlist as a custom import list
  • Radarr: expose your Sofa movie watchlist as a custom import list

Development

For local development:

bun install
cp .env.example .env
bun run dev

Useful commands:

  • bun test
  • bun run lint
  • bun run format
  • bun run check-types
  • cd packages/db && bun run db:generate
  • cd packages/db && bun run db:migrate

TMDB notice

This product uses the TMDB API but is not endorsed or certified by TMDB.

License

MIT

S
Description
🛋️ Self-hosted movie and TV show scrobbler.
https://sofa.watch Readme MIT
9.3 MiB
Languages
TypeScript 99.1%
CSS 0.4%
Swift 0.3%
Dockerfile 0.1%