--- id: epic-programming-manager type: explanation status: draft tags: [epic, lineup, webui, programming-manager] --- # EPIC-programming-manager Build a real "Programming Manager" surface for curating the live channel lineup: category-first bulk selection, per-channel include/exclude, deliberate custom ordering, and exact-EPG-match backup grouping. ## Why this exists The current product can ingest, dedupe, score, and expose channels, but it does not yet give operators a first-class curation workflow. The tester request is not just "another settings page"; it is a product surface for deciding: - which provider categories belong in the lineup - which exact channels inside those categories stay or go - what order those channels should appear in - which exact-EPG sibling channels should be retained as backup sources behind one visible lineup row That should become a server-backed, operator-friendly control surface, not a collection of ad hoc env vars and one-off recipes. ## External patterns worth copying These are the useful patterns, not literal UI clones. 1. Channels DVR collections: manual curation plus explicit sort modes and custom drag ordering. Source: 2. xTeVe/XEPG style channel mapping: one canonical visible channel row with stream + EPG mapping control and explicit channel order control. Source: What those tools get right: - bulk-add from a grouped source, then refine - one visible lineup row can still hide richer mapping underneath - sorting is deliberate, not just "whatever the source emitted" - server-side saved order matters more than client-side temporary filtering ## Product stance - Category selection should be the fast path. - Per-channel inclusion/exclusion should be the refinement path. - Custom order should be saved server-side and replayable across refreshes. - Backup sources should be grouped automatically when there is a trustworthy exact EPG/channel identity match, but the operator should be able to inspect and override that grouping. ## UX shape The Programming Manager should have four lanes: 1. **Sources** - provider category groups (`Sling`, `DirecTV`, `NBC`, `ABC`, `FOX`, etc.) - counts, confidence, and whether a group is already included 2. **Selection** - bulk include/exclude category - drill into category and toggle specific channels 3. **Order** - preset taxonomy order - manual drag-and-drop custom order - preview of final `lineup.json` order 4. **Backups** - show channels grouped by exact EPG/channel identity - one primary visible row - zero or more backup sources behind it ## Default taxonomy order When the operator chooses "recommended order", the server should sort by this bucket sequence first, then by saved manual rank if present, then by guide number/name: 1. Local Broadcast 2. General Entertainment 3. News & Info 4. Sports 5. Lifestyle & Home 6. Documentary & History 7. Children & Family 8. Reality & Specialized 9. Premium Networks 10. Regional Sports 11. Religious 12. International This needs a stable, testable classifier rather than a hand-wavy UI-only label. ## Story list | ID | Goal | Acceptance criteria | |----|------|---------------------| | PM-001 | Source category inventory | Server can build a category inventory from provider/XMLTV/group-title inputs with counts and stable IDs. | | PM-002 | Saved lineup recipe model | Persist selected categories, per-channel include/exclude, and saved ordering in a durable JSON or SQLite-backed model. | | PM-003 | Category-first selection API | Add API endpoints to bulk include/exclude categories and inspect category members. | | PM-004 | Per-channel selection API | Add API endpoints to include/exclude exact channels and preview the resulting curated lineup. | | PM-005 | Recommended taxonomy ordering | Server can classify channels into the requested bucket order and produce a deterministic recommended sort. | | PM-006 | Manual order persistence | Operators can save a custom order; refresh/index runs preserve it unless explicitly rebuilt. | | PM-007 | Backup grouping by exact match | Exact EPG/channel matches across markets/providers can be retained as backup stream sources behind one visible row. | | PM-008 | Control-deck UI | Add a Programming Manager lane to the web UI with category picker, detail drawer, and order view. | | PM-009 | Release-grade testing | Unit tests, API tests, and smoke coverage prove category selection, saved order, and backup grouping survive refreshes. | ## Status - **2026-03-21:** `PM-001` and `PM-002` foundation slice shipped. - `internal/programming` now builds stable category inventory from the raw post-intelligence lineup and persists a durable JSON recipe file. - `Server.UpdateChannels` now preserves `raw catalog -> intelligence/dedupe` input separately from the final exposed lineup, then applies the saved programming recipe before existing lineup-shape/cap logic. - First backend endpoints are live: - `/programming/categories.json` - `/programming/channels.json` - `/programming/recipe.json` - `/programming/preview.json` - This is intentionally backend-first. The control-deck UI lane (`PM-008`) and explicit mutation conveniences (`PM-003` / `PM-004`) still follow. - **2026-03-21:** `PM-003`, `PM-004`, and the first visible `PM-005` slice shipped. - `/programming/categories.json` now supports operator-guarded bulk include/exclude/remove mutations for category selection. - `/programming/channels.json` now supports operator-guarded include/exclude/remove mutations for exact channel overrides. - `order_mode: "recommended"` now classifies channels into the requested taxonomy buckets and sorts deterministically by bucket -> saved manual rank -> guide number/name. - `/programming/preview.json` now reports bucket counts so the output shape is inspectable without scraping the lineup by hand. - **2026-03-21:** `PM-006` and `PM-007` backend slice shipped. - `/programming/order.json` now supports durable server-side manual order mutations (`prepend`, `append`, `before`, `after`, `remove`) and automatically flips the recipe into `order_mode: "custom"` when operators start pinning rows deliberately. - `/programming/backups.json` now reports exact-match sibling groups using strong identity only (`tvg_id` exact, else `dna_id` exact). - `collapse_exact_backups: true` on the saved recipe now collapses those exact sibling rows into one visible lineup channel with merged `stream_urls`, so “Sling SyFy” and “DirecTV SyFy” can become one visible row with backup sources behind it. - **2026-03-21:** `PM-008` deck lane shipped. - The dedicated `internal/webui` control deck now has a real Programming lane instead of leaving the feature as backend JSON only. - Operators can bulk include/exclude categories, pin or block exact channels, nudge manual order from the curated preview (`prepend` / relative up/down / drop order), toggle `collapse_exact_backups`, and inspect exact backup groups. - The lane also exposes raw Programming payload drill-down so recipe, preview, categories, channels, order, and backups stay debuggable from the same control plane. - **2026-03-21:** first visible `PM-009` release-grade coverage shipped. - Added tuner regression coverage proving saved programming recipe mutations survive `UpdateChannels` refresh churn instead of only passing in a static one-shot API flow. - Expanded the binary smoke lane so it restarts `iptv-tunerr serve` against a reshuffled catalog while reusing the same programming recipe file, then reasserts curated lineup shape, persisted custom order, and `collapse_exact_backups` behavior after the restart. - Remaining `PM-009` depth is richer operator/browser automation, not missing persistence coverage. - **2026-03-21:** exact-backup preference override shipped on top of `PM-007`. - `/programming/backups.json` now supports durable preferred-primary mutations in the saved recipe. - Collapsed exact-backup rows now honor that preference instead of always picking the first ingested sibling as the visible primary. - The deck exposes one-click backup preference from the same Programming lane, and binary smoke now gates the preference path too. - **2026-03-21:** Programming browse and channel-aware diagnostics shipped on top of the existing lane. - `/programming/browse.json` now gives one-category batch browse with cached guide-health status, next-hour programme titles/counts, exact-backup counts, recipe inclusion flags, and feed descriptors, so category exploration no longer needs one-channel-at-a-time detail polling. - The deck can switch categories into that browse view directly and launch bounded `stream-compare` or `channel-diff` captures for the current selection or one of its exact backups. - This turns the tester’s curses-side “press `e` and wait on 313 channels” workflow into one server-side cached request plus in-app diagnostics hooks instead of N ad hoc probes. ## Technical approach ### Data model Add a new durable curation layer, separate from raw ingest: - `programming_categories` - `programming_recipe` - `programming_overrides` - `programming_order` - `programming_backup_groups` The raw catalog stays source truth for ingest. The programming layer becomes the operator truth for final exposed lineup shape. ### Match strategy for backup sources Only auto-group channels when the identity signal is strong: - same exact `tvg_id`, or - same `dna_id`, or - same exact normalized guide identity with an explicit confidence threshold Do not group fuzzy near-matches automatically. ### Runtime contract The final exposed lineup should become: `raw catalog -> intelligence/dedupe -> programming recipe -> final lineup` That keeps curation deterministic and replayable after refreshes. ## Verification plan - unit tests for category classification and taxonomy ordering - API tests for category and channel selection mutations - persistence tests for saved order surviving catalog refresh - backup-group tests for exact-match grouping and operator override - binary smoke for a minimal saved recipe being applied to `lineup.json` ## Out of scope - replacing Plex's own channel UI - free-form fuzzy matching for every possible market alias in one pass - one-shot import of arbitrary third-party lineup-editor configs - building a full public-facing admin app ## See also - [EPIC-live-tv-intelligence](EPIC-live-tv-intelligence.md) - [EPIC-lineup-parity](EPIC-lineup-parity.md) - [project-backlog](../explanations/project-backlog.md)