A personal, self-hosted TV show tracker. Built as a replacement for TV Time, which shut down on 15 July 2026, taking a decade of watch history with it unless users exported their data first.
WatchNext imports that export and keeps tracking going: no accounts, no server, no ads. All data lives in your own browser, with one-click backup to a JSON file.
- Up Next: continue-watching list sorted by recency, plus upcoming air dates for followed shows
- Shows: your full library with posters and progress bars, filterable by watching / finished / not started, with TMDB search to follow new shows
- Episode tracking: seasons and episodes pulled live from TMDB, mark single episodes or whole seasons
- Stats: total episodes, hours and days watched, most-watched shows, episodes per year, movie totals
- TV Time import: converts the official GDPR export (8,000+ watch records tested) into the app
- PWA: installable on a phone home screen, works offline for everything already cached
- Own your data: everything is stored locally; export and re-import backups anywhere
- React 18 + Vite, no other runtime dependencies
- TMDB API for show metadata, posters and air dates
- Python 3 (standard library only) for the TV Time export converter
- localStorage persistence with a small subscribe-based store (
useSyncExternalStore)
-
Install and run
npm install npm run dev
-
Get a TMDB API key (free): create an account at themoviedb.org, then Settings → API → request a key. Paste the v3 key into the app's Settings tab. The key never leaves your browser.
-
Import your TV Time history (optional):
python tools/convert_tvtime.py path/to/gdpr-data.zip -o tvtime_import.json
Then in the app: Settings → Choose import file → select
tvtime_import.json. Finally, on the Shows tab, press Sync with TMDB to resolve every show (the export uses TVDB IDs; the app maps them to TMDB via the/findendpoint) and load posters, episode counts and air dates.
By default your data lives only in the current browser's storage — great for privacy, but it means switching devices loses your history unless you use the manual backup/import above every time.
WatchNext can optionally sync across devices via Firebase, using Google
sign-in so it's still only you accessing your own data (enforced by
firestore.rules, not by hiding any keys).
One-time setup:
- Go to console.firebase.google.com → Add project → name it anything (e.g.
watchnext) → skip Google Analytics (not needed) → Create. - Build → Authentication → Get started → enable Google as a sign-in provider → Save.
- Build → Firestore Database → Create database → start in production mode → pick any region → Enable.
- In Firestore, go to the Rules tab, replace the contents with everything in
firestore.rulesfrom this repo, and click Publish. - Go to Project settings (gear icon) → scroll to "Your apps" → click the </> (web) icon → register an app (any nickname) → you'll be shown a
firebaseConfigobject. - Copy those values into a
.envfile in the project root (copy.env.exampleto.envfirst):VITE_FIREBASE_API_KEY=... VITE_FIREBASE_AUTH_DOMAIN=... VITE_FIREBASE_PROJECT_ID=... VITE_FIREBASE_STORAGE_BUCKET=... VITE_FIREBASE_MESSAGING_SENDER_ID=... VITE_FIREBASE_APP_ID=... - Rebuild and redeploy:
npm run deploy. - In Authentication → Settings → Authorized domains, add your GitHub Pages domain (e.g.
aceasif.github.io) if it isn't already listed — otherwise Google sign-in will reject the popup.
After that, open the deployed app → Settings → Sign in with Google. Your existing local data merges into the cloud automatically, and any other device that signs in with the same Google account will pick up the same shows and watched episodes within a few seconds of any change.
Cost: Firebase's free "Spark" plan covers this comfortably — a single-user watch history is a tiny fraction of the free Firestore read/write quota.
npm run build produces a static dist/ folder. Host it anywhere static files go: GitHub Pages, Netlify, Cloudflare Pages, or a home server. Because storage is per-browser, use Settings → Download backup to move history between devices.
data/,*.zipexports and backup files are gitignored so watch history never lands in the repo- A TMDB key entered in Settings is stored in localStorage only; a key baked in via
.envends up visible in the built JavaScript, so prefer the Settings route for anything public
tools/convert_tvtime.py TV Time GDPR zip -> import JSON (pure stdlib)
src/store/db.js localStorage store, import/merge logic, mutations
src/api/tmdb.js TMDB client (search, TVDB id resolution, seasons)
src/pages/ Up Next, Shows, ShowDetail, Stats, Settings
public/ PWA manifest, icons, service worker
Simkl and Showly are good apps. My app exists because the TV Time shutdown made the lesson obvious: when your watch history lives in someone else's product, it can disappear on two weeks' notice. Here the history is a JSON file you own.