94f015044a
The open creator report ('I still cannot post a chat message in the TEST tab',
feedback 'Chat polling couldn't start — Mock Chat Input is disabled') was a single
cause: the liveChatId never resolved. Two YouTube API facts:
1. The id lives in snippet.liveChatId — contentDetails has no such property
(TASK 44 read part=contentDetails: could never resolve).
https://developers.google.com/youtube/v3/live/docs/liveBroadcasts
2. It only exists once the broadcast is LIVE — the official sample lists
broadcastStatus=active, and our fetch ran before the frame pump pushed RTMP
(enableAutoStart flips ready→live). A ready-state list legitimately returns
no id.
https://github.com/youtube/api-samples/blob/master/java/src/main/java/com/google/api/services/samples/youtube/cmdline/live/GetLiveChatId.java
Fix: GetBroadcastLiveChatIdAsync reads part=snippet and polls with a bounded
retry (10x/2s); PrepareAndStartLiveAsync starts the frame pump FIRST, then
resolves the id. Chat stays non-fatal. Docs ai.md/TASKS.md/HANDOFF.md +
MyMistakes recipe updated same commit. TASK 44.
Good Dog: ONE integration test (GetBroadcastLiveChatIdAsync_Polls_Snippet_...
) drives a broadcast that gains its id mid-retry and asserts every request used
part=snippet. Gate: clean build 0 warnings, vstest 318/318.
58 lines
3.4 KiB
Markdown
58 lines
3.4 KiB
Markdown
# TASK 44 — TEST-tab chat fix: resolve the liveChatId (snippet + live-timing)
|
|
|
|
> Catalog: [`TASKS.md`](../TASKS.md). Status: ✅ **SHIPPED 2026-09-25** — the open
|
|
> creator report from HANDOFF ("I still cannot post a chat message in the TEST tab")
|
|
> diagnosed and fixed as its own scoped unit.
|
|
|
|
## Provenance
|
|
|
|
- **2026-09-24, HANDOFF** filed the creator report as open: "I still cannot post a chat
|
|
message in the TEST tab." The TEST drawer's Mock Chat Input posts a real
|
|
`liveChat/messages.insert`; the drawer only enables it when a liveChatId is resolved.
|
|
- **2026-09-25 diagnosis:** the reported feedback line "Chat polling couldn't start —
|
|
Mock Chat Input is disabled" is `TestSessionViewModel.BeginTest` running with a **null
|
|
liveChatId**. That id comes from `YouTubeStreamService.GetBroadcastLiveChatIdAsync`,
|
|
and two API facts (from the official liveBroadcasts reference + the
|
|
`GetLiveChatId.java` sample) pin why it never resolved:
|
|
1. **It lives under `snippet.liveChatId`** — `contentDetails` has no such property at
|
|
all. The code requested `part=contentDetails` and read `contentDetails.liveChatId`,
|
|
a path that can NEVER resolve.
|
|
2. **YouTube only populates it once the broadcast is `live`** (the official sample
|
|
lists `broadcastStatus=active`). The fetch ran right after broadcast insert
|
|
(lifecycleStatus `ready`) AND before `_framePump.StartAsync()` pushed the RTMP
|
|
stream that `enableAutoStart` needs to flip the broadcast to live — so even the
|
|
correct property would have been empty at fetch time.
|
|
|
|
## What shipped
|
|
|
|
- `Services/YouTubeStreamService.cs`:
|
|
- `GetBroadcastLiveChatIdAsync(broadcastId, maxAttempts = 10, delayMs = 2000)` now
|
|
requests **`part=snippet`** and reads **`snippet.liveChatId`**, and **polls with a
|
|
bounded retry** until the id appears or the attempts exhaust (never throws). The
|
|
default retry (~20s) gives the encoder time to connect and the broadcast to go live.
|
|
- New private `FetchLiveChatIdAsync` holds the single read; the public method owns
|
|
the retry loop.
|
|
- `ViewModels/MainViewModel.Streaming.Operations.cs`:
|
|
- `PrepareAndStartLiveAsync` **starts the frame pump FIRST**, then resolves the
|
|
liveChatId — the reorder is required for the id to ever exist. Chat remains
|
|
non-fatal: failure logs and the stream continues.
|
|
- TEST drawer wiring preserved: `TestSession.BeginTest(broadcastId, liveChatId)`
|
|
still opens the dock once the id resolves → Mock Chat Input becomes sendable.
|
|
- Docs: `ai.md` (Test Stream section), `TASKS.md` (row 44), `HANDOFF.md` rewritten.
|
|
|
|
## Validation (Good Dog: ONE integration test)
|
|
|
|
`ytLive.Tests/YouTubeStreamServiceTests.cs`:
|
|
`GetBroadcastLiveChatIdAsync_Polls_Snippet_Until_Id_Appears` — a fake broadcast that
|
|
starts WITHOUT a liveChatId (not yet live) and gains it on a later poll; asserts the
|
|
retry eventually resolves `Cg0LIVECHAT42`, that it polled ≥3 times, and that every
|
|
request asked for **snippet** (never contentDetails) — the exact seam that was broken.
|
|
|
|
**Gate: clean build 0 warnings; full vstest 318/318 passed.**
|
|
|
|
## Reference
|
|
|
|
- https://developers.google.com/youtube/v3/live/docs/liveBroadcasts (snippet.liveChatId;
|
|
contentDetails has no liveChatId)
|
|
- https://github.com/youtube/api-samples/blob/master/java/src/main/java/com/google/api/services/samples/youtube/cmdline/live/GetLiveChatId.java
|
|
(sample lists broadcastStatus=active — id only present on live broadcasts) |