Files
movie-night/CLAUDE.md
T

96 lines
4.3 KiB
Markdown
Raw Normal View History

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
"Movie Night" — an AI-powered media discovery web app for a household with ~53TB of movies on a self-hosted Jellyfin media server. Users describe their mood ("pizza night with the kids", "light fun movie after a hard week") and get AI-ranked recommendations from their unwatched library, with posters and one-click deep links to Jellyfin.
**Users:** Kenny and his wife — the primary use case is "what should we watch tonight?"
## Architecture
```
Frontend (Tailwind + vanilla JS) → FastAPI backend → LLM (Claude/OpenAI/Ollama)
Jellyfin API → SQLite cache
```
- **Backend:** Python 3.12 + FastAPI (app/)
- **Frontend:** Single-page HTML/CSS/JS with Tailwind CDN (app/static/)
- **LLM:** Modular provider — Anthropic Claude, OpenAI, or Ollama (app/services/llm/)
- **Auth:** Jellyfin credentials (like Jellyseerr) — no separate user database
- **Data:** SQLite cache of movie metadata synced from Jellyfin, persisted at /data/library.db
- **Deployment:** Docker Compose on bondelie-media (192.168.5.254), port 5210
## Key Files
- `app/main.py` — FastAPI app entry, lifespan (DB init + background sync), poster proxy, static mount
- `app/config.py` — Settings via pydantic-settings (env vars)
- `app/database.py` — SQLite schema and query helpers
- `app/routers/auth.py` — Jellyfin-based login/logout/session management
- `app/routers/mood.py` — POST /api/mood — the core recommendation flow
- `app/services/jellyfin.py` — Jellyfin API client (auth, library fetch, watch state, posters)
- `app/services/prefilter.py` — Tier-1 keyword/genre scoring to narrow candidates before LLM
- `app/services/llm/` — Modular LLM providers (anthropic, openai, ollama)
- `app/services/library_sync.py` — Background sync of movie metadata + watch state
- `app/static/index.html` — Frontend: login, mood input, result cards
- `app/static/app.js` — Frontend logic
## Related Infrastructure
Integrates with the media server documented in `../media-server-migration/`:
| Service | Port | Purpose |
|-------------|-------|--------------------------------------------|
| Jellyfin | 8096 | Library metadata, watch state, posters |
| Movie Night | 5210 | This app |
**Server:** bondelie-media (192.168.5.254) — Ubuntu 24.04 LTS
**Proxy:** `movienight.internal.bondelie.net`
## Recommendation Flow
1. User types mood text
2. Backend fetches unwatched movies from SQLite cache (filtered by selected users' watch state)
3. `prefilter.py` scores movies by genre/keyword match, returns top 200 candidates
4. LLM provider ranks candidates and returns top 6 with reasoning (structured JSON)
5. Backend validates jellyfin_ids, enriches with poster URLs and deep links
6. Frontend renders movie cards with posters, metadata, reasoning, and "Watch" button
## Secrets
Store in `.env` (gitignored), never commit:
- `JELLYFIN_API_KEY` — Jellyfin API key (Settings → API Keys)
- `LLM_API_KEY` — Anthropic/OpenAI API key (not needed for Ollama)
- `SESSION_SECRET` — Random string for cookie signing
## Development Commands
```bash
# Local development (requires Python 3.12+)
pip install -r requirements.txt
cp .env.example .env # then fill in API keys
uvicorn app.main:app --reload --port 5210
# Docker build and run
docker compose up --build
# Deploy to bondelie-media
scp -r . kenny@192.168.5.254:/srv/stacks/movie-night/
ssh kenny@192.168.5.254 "cd /srv/stacks/movie-night && docker compose up --build -d"
```
## Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| JELLYFIN_URL | Yes | http://192.168.5.254:8096 | Jellyfin server URL |
| JELLYFIN_API_KEY | Yes | — | Jellyfin API key |
| JELLYFIN_EXTERNAL_URL | Yes | https://jellyfin.internal.bondelie.net | URL for deep links |
| LLM_PROVIDER | No | anthropic | anthropic, openai, or ollama |
| LLM_API_KEY | Yes* | — | API key (*not needed for ollama) |
| LLM_MODEL | No | varies by provider | Model name override |
| LLM_BASE_URL | No | — | Base URL override (for ollama) |
| SESSION_SECRET | Yes | — | Random string for sessions |