Files
LlamaCasty/TASKS/task-09-live-stream-management.md
T
gramps 87509bcf99 docs: restructure TASKS.md into a catalog — one file per task in TASKS/
TASKS.md is now the index (status table, open items, research pointer).
33 files: 32 task files + 1 research facts file. The full take-saga
narrative and all design decisions are preserved verbatim; the catalog
makes the queue readable without opening every task body. Schema and
AGENTS.md updated to reflect the new layout.
2026-09-05 16:31:46 -07:00

5.8 KiB
Raw Blame History

TASK 9 — YouTube Live Stream Management

Catalog: TASKS.md — status and requirements live here.

Goal: Create/bind broadcasts, monitor YouTube-side stream health — the v3 way.

Status: ⏳ In progress — items 1–3 SHIPPED (reusable stream 2026-08-16; report-by-exception health 2026-08-16); item 4 SHIPPED (status reconciled 2026-09-01); item 5 open; item 6 = deliberate lock (TASK 36); item 7 scoped to core fields — advanced tab is out-of-product

  1. ☑ Broadcast creation — title/description/privacy/scheduledStartTime via API, with the v3 flags above (SHIPPED: CreateBroadcast sends enableAutoStart/Stop, enableMonitorStream=false, latencyPreference=low, selfDeclaredMadeForKids=false)
  2. ☑ Reusable stream — create once, cache + reuse; bind to broadcast (SHIPPED: GetOrCreateReusableStreamAsync lists-then-inserts the variable/isReusable stream, cached via LayoutStore Settings, bound at broadcast insert via boundStreamId; _rtmpUrlProvider yields the ingest URL so go-live actually encodes + pushes)
  3. ☑ Health monitoring — poll liveStreams.list healthStatus + configurationIssues[], surface banner only on warning/error (SHIPPED: GetStreamHealthAsync(streamId) 30s while live; pure StreamHealthReporter.BannerFor = report-by-exception; banner strip under the top bar, amber warning / dark-red error, via HealthIssueBanner/HealthIssueBackground; poll failures log-only; ONE integration test GetStreamHealthAsync_Report_By_Exception_Banner_Only_On_Warning_Or_Error)
  4. ☑ Live chat — poll liveChat/messages, render in right panel, support Super Chat + membership badges — SHIPPED (status reconciled 2026-09-01: YouTubeChatService polls + parses superChatEvent/newSponsorEvent with badge/level on ChatMessage; the panel MOVED to the left-panel live state by Control Surface UX, "right panel" superseded; the remaining reward types land with the monetization chain, TASK 10 → related work)
  5. ☐ Error handling — the YouTube error codes: errorStreamInactive, invalidTransition, redundantTransition, liveStreamDeletionNotAllowed, liveStreamModificationNotAllowed, liveBroadcastBindingNotAllowed
  6. ❌ Visibility picker — DELIBERATE TEST-PHASE LOCK (creator decision 2026-09-01), not open work: "always Private" stays during multi-month real-world testing so breakage VODs never clutter the channel. recordFromStart/DVR stay on — Private VODs are invisible review tapes; bulk-delete pre-GA. Unlock = TASK 36 gold-pass item (remove the override in YouTubeStreamService.CreateBroadcast, wire the dialog selection). See ai.md → YouTube Live API constraints.
  7. 🔶 Full broadcast form — RESCOPED 2026-09-01, not the old two-tab plan. Core fields (SHIPPED as the Text drawer, 2026-08-24): title, description, tags, visibility, made-for-kids — live-editable. Scheduling: ships as TASK 34 (Scheduled checkbox + datetime in the same drawer). Advanced tab is PERMANENTLY OUT (the 10% margin): latency (locked low), DVR/record-from-start (locked on), embed, projection, CC, region restrictions stay fixed at sane defaults, invisible — every exposed field is a support ticket. categoryId removal: done (not a liveBroadcast field). Monetization enablement (if ever needed) rides the reward-events chain via liveBroadcasts.update, not a form field

Design decisions (v3)

  1. One-click go-live — liveBroadcasts.insert with enableAutoStart=true, enableAutoStop=true, enableMonitorStream=false, selfDeclaredMadeForKids=false, latencyPreference=low. No transition(live) call, no testing stage, no liveStarting polling. Encoder starts → YouTube brings it live by itself.
  2. Variable reusable stream — cdn.resolution=variable, cdn.frameRate=variable, isReusable=true. Create once per channel, cache ingestion URL + stream name, reuse for every broadcast. Any quality tier works without recreation; auto step-down needs no API calls.
  3. Report-by-exception — poll liveStreams.list; banner only on healthStatus warning/error issues (configurationIssues[]). Bottom strip = YouTube logo + green/red connection dot (clickable → opens the dialog).
  4. One dialog, three states — not connected (sign-in) / connected-offline (all editable) / live (title + description + visibility editable; quality + account greyed out). Both entry points (Start Stream button + bottom strip) open it; prefilled from saved session profile.
  5. Live edits — liveBroadcasts.update with part=snippet,status for title/description/privacy.
  6. End stream — stop encoder → transition(complete), with enableAutoStop as the safety net. SHIPPED 2026-09-01 (EndBroadcastAsync, wired into StopStream after RTMP EOF; the call had been specced since TASK 9 but never existed — found during recording verification)
  7. Broadcast ID == Video ID — one ID to track status, health, and the auto-created VOD (recordFromStart + enableDvr).

Requirements:

  1. Broadcast creation — title/description/privacy/scheduledStartTime via API, with the v3 flags above
  2. Reusable stream — create once, cache + reuse; bind to broadcast
  3. Health monitoring — poll liveStreams.list healthStatus + configurationIssues[], surface banner only on warning/error
  4. Live chat — poll liveChat/messages, render in right panel, support Super Chat + membership badges
  5. Error handling — the YouTube error codes: errorStreamInactive, invalidTransition, redundantTransition, liveStreamDeletionNotAllowed, liveStreamModificationNotAllowed, liveBroadcastBindingNotAllowed
  6. Visibility picker — Private/Unlisted/Public from the go-live dialog
  7. Full broadcast form — Core + Advanced tabs with all API-supported fields