# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What This Is Lumière is a personal cinema diary — a server-rendered FastAPI web app backed by SQLite. A single owner logs in with a password; the `/tyler` public profile is read-only for guests. Films live on three shelves: `diary`, `queue`, and `abandoned`. TMDB enriches metadata (posters, directors, runtime, countries, cast, overview); OMDb supplies third-party ratings (IMDb, Rotten Tomatoes, Metacritic) on the detail page. ## Commands ```bash # Setup python -m venv .venv .venv/bin/pip install -r requirements.txt # Run dev server .venv/bin/uvicorn main:app --reload # Syntax check (run before any PR) .venv/bin/python -m py_compile main.py database.py models.py routers/*.py services/*.py # Tests (no suite yet — use pytest when adding tests) .venv/bin/pytest ``` Requires a `.env` file with: ``` TMDB_API_KEY=... OMDB_API_KEY=... # for IMDb/RT/Metacritic ratings OWNER_PASSWORD_HASH=... # argon2 hash; generate with: python -c "from argon2 import PasswordHasher; print(PasswordHasher().hash('your-password'))" SESSION_SECRET=... # random secret for cookie signing ``` App runs at `http://127.0.0.1:8000`. ## Architecture **Request flow:** Browser → FastAPI router → SQLAlchemy (SQLite) → Jinja2 template → HTML response. `static/app.js` handles star ratings (JSON PATCH) and TMDB search autocomplete without a build step. **Key files:** - `main.py` — app entry, router registration, `AuthMiddleware` (session-based, password protected) - `database.py` — SQLite engine, sessions, self-healing schema migration (no Alembic; renames old table on column mismatch and migrates data) - `models.py` — single `Film` ORM model with a `shelf` column - `routers/films.py` — shelf listing, CRUD, director page, queue random pick, star rating API, infinite-scroll partial - `routers/imports.py` — Letterboxd and watchlist CSV import, deduplication, enrichment - `routers/stats.py` — all-time stats and year-in-review (pure Python aggregation, no SQL GROUP BY) - `routers/auth.py` — `/login` / `/logout` using argon2 password hashing - `routers/profile.py` — public `/tyler` profile page (no auth required) - `routers/about.py` — static about page - `routers/tmdb.py` — `/tmdb/search?q=` proxy for JS autocomplete - `services/tmdb.py` — TMDB API client: search, detail fetch, metadata application, director bio/image - `services/omdb.py` — OMDb API client: fetches IMDb/Rotten Tomatoes/Metacritic ratings for detail page - `services/film_people.py` — director name normalization and URL slug helpers - `services/countries.py` — ISO numeric country code mapping for stats world map **Patterns:** - Route logic stays in `routers/`, shared service logic in `services/` - Stats computed in-memory from full query results (appropriate for personal-scale SQLite) - TMDB fields are additive/optional; all core film fields work without enrichment - Import pipeline dedupes by TMDB ID first, then normalized title+year+date ## Coding Conventions - 4-space indentation; `snake_case` for functions/variables, `PascalCase` for ORM models, uppercase for shelf/status constants - Jinja templates stay minimal; defer behavior to `static/app.js` - `lumiere.db` and `*.db.bak-*` files are local artifacts — do not drive code changes from them