arr-summary/PRODUCT.md
Barely Removable 4b19cbdfb2 Redesign Media Centre as a teletext broadcast information service
Replaces the neutral dark card-based theme with "Page 100": teletext's
locked eight colours at full saturation on pure black, numbered pages
reachable by typing the number (or the r/g/y/c fastext keys), and no
borders, boxes, or radius anywhere — grouping is carried by full-bleed
colour fields and row weight instead.

Chosen via the impeccable skill's replacement-world flow: seven visual
candidates derived from the audience's own world, a mandatory roll
assigned Ceefax/US-cable-guide teletext, six catalog challengers weighed
against it, and the user locked the assigned direction on the decision
page (seed aab4510e).

- New templates for all nine pages (home/watch/library/discover/
  requests/system/activity/login/settings) behind a shared _shell.html
  carrying the direction contract and _macros.html for the row/bar/tile
  vocabulary; deletes the old single-page dashboard.html and its
  dedicated stylesheets/script.
- static/css/base.css + teletext.css: the locked palette, a two-height
  type scale (Chivo Mono / DotGothic16), and the five disciplines the
  system is held to (no in-between tone, no enclosure, one shared bar
  ruler, trend only where real history exists, pages weighted by state).
- static/js/service.js: header clock, page-number jump, fastext keys,
  two-tap arm/confirm add, guarded live polling — replaces dashboard.js.
- app.py: nav_items now carry page numbers; fastext defaults added to
  the page-chrome context processor; version bumped to 2.0.0.
- DESIGN.md + .impeccable/design.json regenerated from the built world
  by the documenter pass; PRODUCT.md and CLAUDE.md updated to describe
  the new system in place of the retired one.
- Fixed a live bug found along the way: settings.html called
  url_for('dashboard'), an endpoint removed earlier in the branch, which
  would have 500'd the Settings page.
- Fixed a One-Ruler violation the finish review's recapture request
  surfaced: --bar-cells disagreed with the bar() macro's hardcoded cell
  count below the 760px breakpoint, so a fill could land mid-cell on a
  phone. Removed the mobile override; the ruler is now 20 cells at every
  width, matching the macro unconditionally.

Passed two inspection rounds (desktop + true-390px mobile, via an
iframe harness since macOS Chrome clamps --window-size) and a finish
review; QA screenshots and interview logs are gitignored as scratch,
not shipped in the repo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBw1QodG8ZMYHrGeQfqyDS
2026-08-29 21:00:45 -07:00

134 lines
6.3 KiB
Markdown

# Product
<!-- impeccable:product-schema 1 -->
## Platform
web
## Users
Single primary user: the self-hoster who administers the Sonarr/Radarr/
SABnzbd/Tautulli/Prowlarr/Ombi stack and the Unraid server it runs on. They
are both the audience and the operator — checking on the stack is also how
they'd notice something needs fixing. Multi-user login support
(`manage_users.py`) exists for credential hygiene / future-proofing, not
because other people actively use the dashboard day to day.
Used mostly on a desktop/tablet-class screen at home, not primarily on a
phone over cellular — though the 5-second parallel-fetch deadline and
per-service error degradation mean it still has to stay usable and fast on
a phone away from home when needed.
## Product Purpose
A single glanceable page (well, now a small set of focused pages) that
answers "is my media stack healthy and what's new" without opening six
different service UIs. It exists because Sonarr/Radarr/SABnzbd/Tautulli/
Prowlarr/Ombi each have their own web UI with their own login, and none of
them show "what's playing right now + what just finished downloading + is
the array full" in one glance.
Success is a page that loads fast (parallel fetch, 5s wall-clock cap),
degrades gracefully per-card when a service is offline rather than failing
the whole page, and surfaces the handful of numbers that actually change
day to day (active streams, queue, disk usage, new library adds, what's
popular that isn't owned yet).
## Positioning
Not a general-purpose *arr dashboard project (like Organizr or Homarr) —
this is a bespoke, single-tenant tool built around exactly this user's
stack, their Unraid box, and their own judgment calls about what's worth
surfacing (e.g. Rotten Tomatoes "popular but not owned yet" filtered
against the actual library, not just a generic trending feed). It trades
general configurability for being exactly right for one specific setup.
## Operating Context
Runs as a single Docker container (`arr-summary`) on the user's Unraid
home server, deployed via Forgejo push-to-deploy. Fronted by Nginx Proxy
Manager over HTTPS (DNS-01/Porkbun certs — port 80 is blocked by the ISP,
so this is not optional). Session-based login (single shared password or
per-user accounts) gates the whole app; there's no public/unauthenticated
view.
**In-progress restructure (branch `meridian-marquee`):** the app is
splitting from one long single-page dashboard into focused pages — Home
("marquee": what's playing, one line of system state, what's new),
Watch, Library, Discover, Requests, System, Activity — each answering one
question well rather than one long scroll trying to show everything.
This also resolves cases where a content-heavy section (e.g. the per-disk
storage list) would otherwise force awkward row heights on a shared
"Right now" strip. `_shell.html` and `_macros.html` are new shared-layout
scaffolding for this split; `dashboard.html` is the outgoing single-page
template being superseded by `home.html` + the per-page templates.
Each page fetches only the services it needs (e.g. `/watch` fetches only
Tautulli) rather than the old single route's all-services-at-once fetch —
narrower per-page payloads, same parallel-fetch/caching discipline
described in CLAUDE.md.
## Capabilities and Constraints
- Aggregates: Sonarr, Radarr, SABnzbd, Tautulli, Prowlarr, Ombi, Unraid
array stats, and scraped Rotten Tomatoes/IMDB "popular, not yet owned"
data.
- Per-service REST clients with independent, tuned cache TTLs; active
streams and the SABnzbd queue are always live, never cached.
- Every dev-machine service call fails by design (dev laptop isn't on the
home network) — per-card error states are the expected local baseline,
not a bug.
- Can add missing titles directly to Radarr/Sonarr from the Discover
flow (`/add-to-radarr`, `/add-to-sonarr`, lookup endpoints).
- RT/IMDB data comes from HTML scraping, not an official API — brittle
to upstream markup changes by nature; this is a known, accepted
tradeoff, not a defect to "fix" by finding an API.
- Reverse-proxy-subpath capable (`SCRIPT_NAME`) though currently deployed
at domain root.
## Brand Commitments
Branded **"Media Centre"**. One committed visual identity, never a
themeable shell — that has held across two redesigns and the five
original novelty themes before them.
As of the 2026-08-29 redesign (v2.0.0) that identity is a **broadcast
information service** in the teletext tradition. The visual system is
DESIGN.md's business, not this file's; what belongs here is that pages
are now numbered and reachable by typing their number, which makes the
page-number model part of how the product is *navigated*, not merely how
it looks — and that the earlier neutral card-based dark theme was
deliberately replaced rather than refined.
Server-side project directory is `greendale`; container/repo are
`arr-summary` — internal naming detail, not user-facing.
## Evidence on Hand
No real service data is reachable from the dev environment (see
Operating Context / CLAUDE.md's "expect every service to be offline on
the dev machine" note) — every card's real, non-error rendering must be
checked either on the deployed Unraid box or by monkey-patching a
fetcher locally per the CLAUDE.md recipe. Do not fabricate sample
Sonarr/Radarr/Tautulli/etc. data as if it were representative; use
realistic-shaped placeholders only when a card must be visually
evaluated offline, and say so.
## Product Principles
1. **Never let one slow/offline service take down the page.** Parallel
fetch with a hard deadline and per-card `service_error()` degradation
is non-negotiable — a single point of failure defeats the purpose.
2. **Show what changes, cache what doesn't.** Cache TTLs are tuned per
service to how often that data actually moves; live-only data (active
streams, download queue) stays live.
3. **One focused question per page, not one page trying to answer all
of them.** The meridian-marquee split exists to keep each page's
"quietest state" reasonable, not to add navigation for its own sake.
4. **Built for this one stack, not a general product.** Configurability
that would matter for other people's setups is explicitly out of
scope.
5. **A committed visual identity, not a themeable shell.** "Media
Centre"'s dark theme is the one look; don't reintroduce per-user or
per-page theme switching.