Tags

Expo RouterReact NativeTypeScriptSQLiteDrizzle ORMNativeWindTanStack Querywebmobilemachine-learningmovies

What Do I Watch is a universal movie/TV recommendation app — one Expo Router codebase running on web, iOS, and Android. Pick filters or search for titles you like, and it returns a hybrid recommendation blend: a local content-based ML model (cosine similarity over genres, keywords, cast, and director) mixed with TMDB's live recommendations, for movies and TV together.

This is a full rebuild of the project — the original stack was a Django backend paired with a React frontend, which itself replaced an even earlier Flask + scikit-learn app (Binge-It-On). Both legacy versions are preserved in _legacy_backup/ rather than deleted.

What It Does

  • Hybrid recommender — blends the local ML model (60% weight) with TMDB's own /recommendations endpoint (40% weight) so new releases (which the offline corpus hasn't seen yet) still surface sensible picks
  • Surprise Me — a random-pick flow for when filtering feels like too much work
  • On-device watchlist — seen / not-interested feedback persisted locally, no account required
  • Streaming-provider info per region, via TMDB's watch-provider data
  • Cross-type recommendations — movies and TV are vectorized together, so a movie can recommend a show and vice versa

The Dataset Pipeline

The ML half comes from an offline pipeline (pandas + scikit-learn, no Python at runtime) that pages through TMDB's /discover endpoint, fetches genre/keyword/cast/director metadata per title, and computes one CountVectorizer + cosine-similarity pass over the entire ~16k-title corpus. The output — precomputed nearest-neighbor similarity lists — is seeded into the app's server database. Per the locked design decision, the corpus is refreshed manually every few months rather than on a schedule; TMDB's live recommendations cover the gap for anything released since the last refresh.

Tech Stack

Expo SDK 57 + Expo Router (file-based routing, universal web + native) with TypeScript throughout. The backend is Expo API Routes — server code living in the same repo, running on Node.js. Data layer is SQLite everywhere: a read-only server DB via node:sqlite + Drizzle ORM for the seeded corpus, and an on-device watchlist via expo-sqlite + Drizzle on native (falling back to localStorage on web). Styling is NativeWind v4 (Tailwind for React Native), data fetching is TanStack React Query, and external data comes from TMDB (search, discover, details, videos, watch providers) and Watchmode (IMDb rating only, cached 7 days to stay under its free-tier cap).