📺
whats-on
TV show tracker - What's On tonight?
- Primary: https://whats-on.onrender.com
- GitHub: https://github.com/klill6506/whats-on
- Local:
D:\Personal\whats-on
README
# What's On? 📺
A simple, mobile-first TV show tracker. No automatic syncing, no clutter from shared accounts — just your shows.
## Features
- 🟢 **Ready to Watch** — Shows you're current on with new episodes
- ⏳ **Catching Up** — Shows you're behind on
- 📱 **Mobile-first** — Designed for phone/iPad
- 🦉 **Henry Integration** — Tell Henry what you watched, he updates the tracker
## Tech Stack
- FastAPI + Jinja2
- SQLite
- Tailwind CSS (CDN)
- Deployed on Render
## API Endpoints
- `GET /` — Main dashboard
- `GET /api/shows` — List all shows
- `POST /api/shows` — Add a show
- `PUT /api/shows/{id}` — Update a show
- `DELETE /api/shows/{id}` — Delete a show
- `POST /api/shows/{id}/caught-up` — Mark show as caught up
## Local Development
```bash
pip install -r requirements.txt
python main.py
```
Then open http://localhost:8005
# Trigger redeploy
STATUS
---
type: project-status
project: What's On
last_updated: 2026-06-11
---
# STATUS — What's On
*The freshest file. Answers "where am I on this project?" Updated at the end of every substantive session.*
---
## Current state
**Live on Coolify at https://whatson.kenlill.com (2026-06-11)** — migrated off
Render with all production data (19 shows, 21 dismissed recs, 40 cached
recommendations). SQLite on a persistent volume (`/data/whats_on.db`), verified
to survive container restarts. Render (https://whats-on.onrender.com) is still
running in parallel as the fallback; **Ken suspends it himself** after a few
days of the new deploy behaving.
Also fixed this session: the home-page 500 that had been live since the 06-05
recommender deploy. Root cause was a Postgres migration bug (`ALTER TABLE ADD
COLUMN` on an existing column aborts the transaction; the swallowed exception
silently rolled back the new columns + `show_tags` every startup), plus a Jinja
guard that let `Undefined` through. Fixed in `a56fc3a`; Render healed on deploy.
Migration playbook: `V:\dev\sherpa-server\MIGRATION-RENDER-TO-COOLIFY.md`
(D:\dev\sherpa-server on the main machine).
## Ken's post-migration checklist (CC can't do these)
- [ ] **GitHub webhook for auto-deploy** (until then, deploys are manual via
Coolify API/dashboard): repo Settings → Webhooks → Add webhook —
url `https://coolify.kenlill.com/webhooks/source/github/events/manual`,
content type `application/json`, secret = contents of
`C:\temp\whatson-repro\webhook_secret.txt` (on the second computer; also
already PATCHed into the Coolify app), events: just push.
- [ ] **Add `TMDB_API_KEY` to the Coolify app** (copy the value from the Render
dashboard env vars) — without it, new recommendation refreshes lose
poster + streaming-service data (the imported cache still displays fine).
- [ ] After the TMDB key is in: **POST `https://whatson.kenlill.com/api/admin/retag`**
once to regenerate taste t
…(truncated for upload size)
DECISIONS
--- type: project-decisions project: What's On last_updated: 2026-06-11 --- # DECISIONS — What's On *Architectural and scope choices. Append-only log. Each entry is a decision that shouldn't be re-litigated without new information. If you find yourself reopening a decision, either add a new entry that overrides the old (and say why) or leave both so the history is visible.* --- ## How to use this file Each decision gets a dated entry with: what was decided, why, what was considered instead, and what would change our mind. Never delete entries — if a decision is reversed, add a new one that supersedes it. --- ## 2026-06-11 — Hosting moves to Coolify (kenlill.com); SQLite on a volume **Decision:** Primary hosting is Coolify on Ken's kenlill.com server (https://whatson.kenlill.com), built from the repo's Dockerfile, with **SQLite on a persistent volume** (`DATABASE_PATH=/data/whats_on.db`) as the production database. Render stays up in parallel until Ken personally suspends it. Data moved (and moves again if ever needed) via the new `/api/admin/export` / `/api/admin/import` JSON endpoints. **Context:** Part of the multi-app campaign off Render onto Ken's own Coolify box. Render had also drifted from its config: the dashboard's `DATABASE_URL` meant prod silently ran Postgres while render.yaml described SQLite-on-disk — that drift is what let the migration bug hide. **Alternatives considered:** Postgres container on Coolify — rejected (Ken's call 2026-06-11): more moving parts than a single-user hobby app needs, and the dual-engine code keeps Postgres available if that ever changes. **Reasoning:** One container, one volume, no separate DB service to operate. SQLite matches the app's actual scale (one user, ~20 rows of shows) and the original render.yaml intent. **Would reconsider if:** Multi-user ever happens (that's the Supabase V2 conversation), or volume-backed SQLite proves fragile under Coolify redeploys (verified surviving restarts on day one). --- ## …(truncated for upload size)
MEMORY
---
type: project-memory
project: What's On
last_updated: 2026-06-11
---
# MEMORY — What's On
*Standing facts, preferences, and accumulated context. Long-lived — not "what I did yesterday" (that's STATUS.md). Update when you learn something worth keeping.*
---
## Purpose and scope
A personal TV-show tracker for Ken — single user, no login. Answers "what's on / what
should I watch next?" on a phone or iPad. Tracks watch progress, surfaces taste-aware
recommendations, and pulls air days/posters from external APIs. Standalone personal
app, not part of the tax suite.
## Domain knowledge
- **Show categories on the home page** (computed in `main.py:home`):
- `between_seasons` — `status == 'hiatus'`
- `backup_shows` — `priority == 3` ("watch if nothing else is on")
- `catching_up` — `current_episode != 99` and `status == 'watching'`
- `priority_shows` — everything else (the "ready to watch" hero list)
- **`current_episode = 99` is a sentinel meaning "caught up"** — not a real episode
number. Lots of logic keys off this; don't treat 99 as data.
- **Statuses:** `watching`, `current`, `hiatus`, `dropped` (`VALID_STATUSES`).
`dropped` shows are hidden from `get_all_shows()`.
- **Priority** 1–5 (1 = highest). **Rating** 1–5 (drives recommendation weighting;
unrated defaults to 2).
- **Services** Ken uses (`USER_SERVICES`): Max, Apple TV+, Hulu, Peacock, Paramount+,
Prime Video, Netflix. Plus Disney+/Other allowed as `VALID_SERVICES`.
## User preferences discovered
- Wants the standard four project files (CLAUDE/MEMORY/STATUS/DECISIONS) maintained and
kept current as the repo changes — established 2026-06-04 when cloning the repo local.
- Mobile-first, low-clutter UI. Brand-colored service badges, "yeti/Henry" mascot.
## Hosting (since 2026-06-11)
- **Primary: Coolify on kenlill.com** — https://whatson.kenlill.com, app uuid
`jpv73ok9hboffxm5np0kpu2v`, Dockerfile build, SQLite at `/data/whats_on.db`
on persistent volume `pzor8tj1k0h4meyp5h9ffv3h`.
…(truncated for upload size)
CLAUDE.md
# CLAUDE.md — What's On
*Project-specific rules. User-level conventions come from `~/.claude/CLAUDE.md` — do not duplicate here.*
---
## What this is
"What's On?" — a personal, mobile-first TV show tracker for Ken. Tracks what he's
watching, what he's caught up on, what's on hiatus, and surfaces taste-aware
recommendations. Single-user, no auth, no clutter. **This is a standalone personal
app — NOT part of the Sherpa tax suite.**
## Tech stack
- **Backend:** FastAPI (Python) — `main.py` (routes) + `database.py` (data layer)
- **Frontend:** Server-rendered Jinja2, single template `templates/index.html`; Tailwind CSS via CDN
- **Database:** Dual-mode — SQLite when no `DATABASE_URL` (local + Coolify prod), PostgreSQL (psycopg3) when set (legacy Render prod)
- **Hosting:** Coolify on kenlill.com — https://whatson.kenlill.com (Dockerfile build,
SQLite on a `/data` volume). Render (`render.yaml`) is the legacy deploy, running in
parallel until Ken suspends it; it runs Postgres via a dashboard `DATABASE_URL`.
Migration playbook: `dev\sherpa-server\MIGRATION-RENDER-TO-COOLIFY.md`
- **Dependencies:** pip (`requirements.txt`) — not Poetry
## Session startup
Read all four root files (CLAUDE.md, MEMORY.md, STATUS.md, DECISIONS.md) at the
start of substantive work. No other required reading.
## Conventions
- This app does **not** inherit the Sherpa-suite database/color rules from
`~/.claude/CLAUDE.md`. Specifically:
- **No shared Supabase DB.** It owns its own SQLite/Postgres database. No `firm_id`,
no central `clients` table.
- **No tax data-entry color system** (red/yellow/green). UI colors here are
service-brand colors (Max, Hulu, etc.) in `index.html`.
- Tests, if added, go in `tests/` as `test_{module}.py` (pytest) — per global rules.
- Keep the SQLite/Postgres dual-path working: `database.py` branches on `DATABASE_URL`.
Any new query must use the `_ph()` placeholder helper so it works on both engines.
## Do not redesign without asking
- Th
…(truncated for upload size)
Diary mentions
No recent diary mentions.
Render
- Service:
whats-on - Status: live
- Last deploy: 2026-06-11T21:39:45.139650Z