bb5dcb4ba2
Test button next to Start runs the real private-only go-live pipeline as a session variant (IsTestStream): BeginTestStream -> extracted StartStreamingSession(alsoRecord:false) shared with the dialog path — no Go Live dialog, no 'last live' stamp, no recording. Gold top bar + TEST badge + 'End Test' button face; TEST drawer (third right-rail pull-out, three-way exclusivity) auto-opens once the liveChatId resolves. Chat tooling: Mock Chat Input sends a REAL liveChat/messages.insert (YouTubeStreamService.InsertChatMessageAsync) that round-trips the real ~2s poll and renders through the live overlay; simulated Subscriber/New Member/Super Chat events inject through the poller's MessageReceived seam (YouTubeChatService.InjectSimulatedMessage, ChatMessage.IsSimulated) — the insert API only creates textMessageEvent, so non-text events are local-only by design (ref: developers.google.com/youtube/v3/live/docs/liveChatMessages/insert). Scopes youtube + youtube.force-ssl already cover insert; no re-consent. Good Dog: ytLive.Tests/TestStreamTests.cs — coordinator protocol with fake HTTP + real-window drawer exclusivity/gate. 0 warnings; 314/314 pass (clean run; intermittent host abort is the pre-existing WinRT-webcam flake). Docs ride in the same change: TASKS.md row 41, TASKS/task-41-test-stream-mode.md, ai.md (YouTube Live API constraints), HANDOFF rewrite.
5.5 KiB
5.5 KiB
TASK 41 — Test Stream mode: private test broadcast + TEST drawer
Status: ✅ Done (2026-09-22) — one scope-checked work unit, one Good Dog integration test, 314/314 suite green, 0 warnings.
Creator ask (2026-09-22)
"YouTube Test Stream & Chat Integration": a Test/Preview button beside Start that runs a
private broadcast via the API, monitors stream/ingest, activates the chat endpoint (liveChatId),
and provides a chat simulator: mock chat input (liveChatMessages.insert) + a toggle to
inject mock chat, with alert/event triggers for subscriber, membership, and Super Chat.
Locked decisions (user answers, 2026-09-22)
- Chat-overlay events only — simulated events validate the existing chat overlay styling
(text / member
memberColor/ SuperChatsuperChatColorrows). Dedicated stinger/TTS alert widgets are NOT part of this task — they stay queued under TASK 3 items 16/17/20. - TEST drawer in the right rail — third white tab below YPP, three-way one-open-at-a-time exclusivity, auto-opens when a test broadcast boots.
- Hybrid simulation — text = REAL
liveChat/messages.insert(round-trips the real 2s poll and renders through the live overlay path); member/SuperChat/subscriber = local injection at the poller'sMessageReceivedseam (markedIsSimulated). YouTube's insert API only createstextMessageEvents — non-text events are local-only by design.
Facts the build sits on (verified in code, 2026-09-22 research pass)
- The app is already private-only go-live —
YouTubeStreamService.CreateBroadcasthardcodesprivacyStatus="private"+enableAutoStart/Stop+low latency, no monitor stream (enableMonitorStream=false), and the Go Live dialog is locked to Private. "Test" is therefore a session variant over the real pipeline, not a new broadcast mode. - The liveChatId flow already exists:
GetBroadcastLiveChatIdAsync→_youtubeChat.Start(liveChatId)inPrepareAndStartLiveAsync— the "chat endpoint activation" the ask describes. - OAuth scope already includes
youtube+youtube.force-ssl(YouTubeAuthService.cs:18) — enough forliveChat/messages.insert; no re-consent. - Chat = polling (
YouTubeChatService, 2s): no webhook exists or is planned. - No alert system exists anywhere in the codebase (grep verified) — hence the scope decision above.
- Existing mock-chat preview (
ChatOverlayLayer.StartMockChatPreview) is static placeholders, not live-injectable — separate from this task.
What shipped (TASK 41, 2026-09-22)
Controls/TopBar.xaml— Test button (secondary style) left of Start; visible only whenShowTestButton(offline ∧ not recording ∧ signed-in); TEST amber badge whileIsTestStream(next to the elapsed clock).Controls/PreviewPane.xaml— TEST tab (gold text, below YPP on the rail) + TEST drawer: session caption / liveChatId line, Mock Chat Input (TextBox + Send), SIMULATED EVENTS (Subscriber / New Member / Super Chat buttons), live feedback line, footer "End Test" + the "private, same pipeline as a real live" note; drawer exclusivity wired three-way.Services/TestSessionViewModel.cs(NEW) — the TEST drawer VM:BeginTest(broadcastId, liveChatId)(auto-opens the dock once the liveChatId resolves),SendTestMessageAsync→ real insert,Simulate{Subscriber,Member,SuperChat}Command→InjectSimulatedMessage,EndTestCommand→ hands toMainViewModel.StopStream,EndTestSilently(idempotent, called on any stop/ rollback). Fully testable — takesYouTubeStreamService+YouTubeChatServicevia ctor.Services/YouTubeStreamService.InsertChatMessageAsync(liveChatId, text)—POST liveChat/messages?part=snippet; returns string error or null; never throws.Services/YouTubeChatService.InjectSimulatedMessage(ChatMessage)— raisesMessageReceived, the exact seam the real poll uses, so simulations flow through the whole overlay pipeline; markedChatMessage.IsSimulated.ViewModels/MainViewModel.*—IsTestStream(internal setter, test seam likeIsRecording);BeginTestStream()→ the sharedStartStreamingSession(alsoRecord:false)extracted fromBeginGoLive(no dialog — a Test needs no confirmation, every broadcast is already private; noBroadcastForm.CaptureGoLive"last live" stamp; no recording).StopStreamclears the face and forgets the drawer session.EndButtonText= "End Test" while in a test. Gold top bar + glow (#9c6f1c) identify a rehearsal vs the live red.- Drawer exclusivity (this task): opening TEST collapses Stream Settings + YPP and vice versa
(wired in
MainViewModel.Ypp.cs).
Good Dog — ONE integration test
ytLive.Tests/TestStreamTests.cs — one [Fact], two sections:
- Coordinator (no network,
RecordingHandler-faked HTTP): idle →BeginTestopens the dock- enables chat; Mock Chat Input emits the insert (POST body asserted for the liveChatId +
messageText); Subscriber/New Member/Super Chat simulations arrive through
MessageReceivedmarkedIsSimulatedwith the right shapes (member level,$5.00Super Chat, plain subscriber row); End hands back to the app;EndTestSilentlyclears.
- enables chat; Mock Chat Input emits the insert (POST body asserted for the liveChatId +
messageText); Subscriber/New Member/Super Chat simulations arrive through
- Real window (temp DB): TEST tab/drawer exist, three-way drawer exclusivity, Test button gate follows sign-in deterministically.
Follow-ups (NOT in this task)
- Real stinger/TTS alert widgets → TASK 3 items 16/17/20.
- Test-mode health-panel depth (per-broadcast status inside the drawer) if ever wanted.