A self-hosted media manager I built and use daily. Pulls new releases through Radarr and Sonarr, filters them against my taste rules, and uses an LLM to pick what to watch next from what's already in the library.
- Agentic recommender —
ai_recs.pyruns an async OpenAI tool-calling loop against a small registry of inspection / search / discovery tools (ai_tools/registry.py). The model iterates: looks up a candidate, checks prior feedback, fetches attributes. - Vector recall on synopses. Weaviate embeddings of every item's synopsis seed the candidate pool before the LLM picks.
- GraphQL + Relay with live subscriptions. Strawberry on the server, Relay on the client,
graphql-wsso background ingest jobs push UI updates. - Scheduler-driven ingest. APScheduler runs the indexer checks on a schedule and writes through a Redis cache; the GraphQL layer reads through the cache, not the upstream APIs.
| Layer | Stack |
|---|---|
| Backend | Python 3.12, FastAPI, Strawberry GraphQL, SQLAlchemy, Alembic, APScheduler |
| Frontend | TypeScript, React, Relay, MUI |
| Data | MySQL (primary), Weaviate (vectors), Redis (cache) |
| Integrations | Plex, Radarr, Sonarr, TMDB, OpenAI |
| Tests | pytest, Playwright |
| Infra | Docker, Docker Compose, GitHub Actions |
Needs a Plex server, Radarr, Sonarr, and API keys for them plus TMDB and OpenAI.
cp .env.sample .env
docker compose up -dDev loop without containers:
bash dev_server.sh # FastAPI, port 8000
npm run dev # webpack + relay-compiler, watchingGraphQL at /graphql. Frontend served by FastAPI at root.
Tailored to my setup. Auth is handled at the reverse proxy in front. Some pieces (the feedback loop, the agent tool set) are shaped by what I want from the tool.
MIT — see LICENSE.