MoodFlix is an end-to-end mood-based recommendation platform: users share how they feel, and the system returns what to watch, why it fits, where it is streaming, and dubbed language suggestions.
- Frontend: React + Tailwind + Framer Motion
- Backend: Node.js + Express
- DB: PostgreSQL
- External APIs: TMDB (discovery + providers fallback), optional JustWatch adapter
- AI: OpenAI Responses API for mood tags + explanation text
- User selects mood + preferences.
- Backend resolves location via IP and language defaults.
- TMDB discovery fetches candidate movies/TV/anime/talk shows/docs/web series.
- Mood-tagging engine generates emotional tags (OpenAI or heuristic fallback).
- Hybrid scorer ranks by mood fit, taste profile, genre affinity, popularity, diversity, dark tolerance, energy and length fit.
- Top 20 recommendations are returned with explanation + platform + dubbed language hints.
- Feedback updates taste weights and future recommendations.
stream-motion/
backend/
sql/
schema.sql
seed.sql
src/
app.js
server.js
config/
controllers/
db/
routes/
scripts/
services/
utils/
frontend/
src/
api/
components/
pages/
styles/
utils/
index.html
docker-compose.yml
package.json
- Node.js 20+
- Docker (for local PostgreSQL)
- Start PostgreSQL:
docker compose up -d- Install dependencies:
npm install- Create env files:
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env- Initialize schema + seed demo user:
npm --workspace backend run db:init
npm --workspace backend run seed- Start backend + frontend:
npm run devFrontend: http://localhost:5173
Backend: http://localhost:4000
Note: if TMDB_API_KEY is not set, MoodFlix now uses a built-in fallback catalog so recommendations still appear.
POST /recommendPOST /saveTasteGET /watchHistory?userId=<uuid>POST /feedbackGET /streamingProviders?tmdbId=<id>&mediaType=<type>&countryCode=US
{
"userId": "uuid",
"mood": "Stressed",
"genre": "Comedy",
"language": "Hindi",
"ageRating": "PG-13",
"darkTolerance": "Mild",
"lengthPreference": "Series",
"watchContext": "Alone",
"energyLevel": "Low"
}Tables implemented:
userstaste_profilecontentmood_tagswatch_historyrecommendations_cachefeedback
Schema lives in backend/sql/schema.sql.
- Demo user in
backend/sql/seed.sql - Demo taste profile with initial genre/mood weights
- Discover/Landing page
- Hero: "What should we watch today?"
- Emoji mood selector
- Preference controls (dark tolerance, energy, length, genre, language, context)
- Results page
- Poster cards
- Mood explanation text
- Streaming platform badges
- Dubbed language hints
- Like/Dislike/Watchlist actions
- Profile page
- Taste graph
- Favorite genres/languages
- Watch history
- Tailwind design tokens + gradient backdrop
- Entry animations (Framer Motion)
- Loading skeleton cards
- Mobile-responsive layout
- Dark mode toggle
- Mood tag generation:
backend/src/services/openaiService.js - Recommendation explanation generation:
backend/src/services/openaiService.js
Set in backend/.env:
OPENAI_API_KEY=...
OPENAI_MODEL=gpt-4.1-miniFallback behavior: heuristics are used if OpenAI key is absent/fails. TMDB fallback behavior: if TMDB is unset/unreachable, local fallback content is used.
npm --workspace backend run tag:moodsThis regenerates mood tags for cached content rows.
- Provision PostgreSQL instance.
- Set env vars (
DB_*,TMDB_API_KEY,OPENAI_API_KEY, optional JustWatch). - Run:
npm --workspace backend run db:init
npm --workspace backend run seed
npm --workspace backend run start- Set
VITE_API_BASE_URLto deployed backend URL. - Build and serve:
npm --workspace frontend run build
npm --workspace frontend run preview- Frontend: Vercel/Netlify
- Backend: Render/Railway/Fly.io
- DB: Managed PostgreSQL (Neon/Supabase/RDS)