Files
kbondelie 3d5de06b44 Initial commit — Movie Night media discovery app
AI-powered web app that recommends unwatched movies from a Jellyfin
library based on natural language mood input. Jellyfin auth, modular
LLM backend (Claude/OpenAI/Ollama), two-tier pre-filter + AI ranking,
mobile-responsive dark theme UI with poster cards and deep links.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 19:20:56 -07:00

4.3 KiB

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

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

# 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