fix(stream): TEST-tab chat — resolve liveChatId from snippet, poll until the broadcast is live
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.
This commit is contained in:
@@ -183,36 +183,6 @@ public partial class MainViewModel : ViewModelBase
|
||||
_notifications.Warning("Complete your broadcast details",
|
||||
"Your title and description are empty — fill them in from the Text panel on the right.");
|
||||
}
|
||||
|
||||
// Start chat polling (TASK 9.4): fetch the liveChatId from the broadcast's
|
||||
// contentDetails, then start the poll loop. Chat is non-fatal — if it fails,
|
||||
// the stream continues without on-panel chat.
|
||||
string? liveChatId = null;
|
||||
try
|
||||
{
|
||||
liveChatId = await _youtubeStream.GetBroadcastLiveChatIdAsync(_currentBroadcastId);
|
||||
if (liveChatId != null)
|
||||
{
|
||||
_youtubeChat.Start(liveChatId);
|
||||
AppLog.Write($"Chat polling started: {liveChatId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
AppLog.Write("Could not retrieve liveChatId; chat polling disabled");
|
||||
}
|
||||
}
|
||||
catch (Exception chatEx)
|
||||
{
|
||||
AppLog.Write($"Chat start failed (non-fatal): {chatEx.Message}");
|
||||
}
|
||||
|
||||
// TASK 41: a test broadcast hands its session to the Test drawer once
|
||||
// the liveChatId resolves — Mock Chat Input + simulated events need it.
|
||||
if (IsTestStream && _currentBroadcastId != null)
|
||||
{
|
||||
TestSession.BeginTest(_currentBroadcastId, liveChatId);
|
||||
AppLog.Write($"Test session dock open for {_currentBroadcastId}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -222,8 +192,40 @@ public partial class MainViewModel : ViewModelBase
|
||||
return;
|
||||
}
|
||||
|
||||
// The pump is what pushes RTMP; enableAutoStart flips the broadcast ready→live
|
||||
// only once frames flow. The liveChatId (snippet.liveChatId) does not exist
|
||||
// before that (2026-09-25 diagnosis), so chat resolution runs AFTER the pump
|
||||
// starts. Chat is non-fatal — if it fails, the stream continues without chat.
|
||||
await _framePump.StartAsync(); // never throws; failures log + surface via Failed
|
||||
|
||||
string? liveChatId = null;
|
||||
try
|
||||
{
|
||||
if (_currentBroadcastId != null)
|
||||
liveChatId = await _youtubeStream.GetBroadcastLiveChatIdAsync(_currentBroadcastId);
|
||||
if (liveChatId != null)
|
||||
{
|
||||
_youtubeChat.Start(liveChatId);
|
||||
AppLog.Write($"Chat polling started: {liveChatId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
AppLog.Write("Could not retrieve liveChatId; chat polling disabled");
|
||||
}
|
||||
}
|
||||
catch (Exception chatEx)
|
||||
{
|
||||
AppLog.Write($"Chat start failed (non-fatal): {chatEx.Message}");
|
||||
}
|
||||
|
||||
// TASK 41: a test broadcast hands its session to the Test drawer once
|
||||
// the liveChatId resolves — Mock Chat Input + simulated events need it.
|
||||
if (IsTestStream && _currentBroadcastId != null)
|
||||
{
|
||||
TestSession.BeginTest(_currentBroadcastId, liveChatId);
|
||||
AppLog.Write($"Test session dock open for {_currentBroadcastId}");
|
||||
}
|
||||
|
||||
// Health polling (TASK 5 item 3): poll immediately, then every 30s while
|
||||
// live. Report-by-exception — a banner appears only on warning/error.
|
||||
_healthPollTimer.Start();
|
||||
|
||||
Reference in New Issue
Block a user