TASK 9.4: live chat wiring — right panel with Super Chat + membership badges

- YouTubeStreamService: added GetBroadcastLiveChatIdAsync to fetch liveChatId from broadcast contentDetails
- YouTubeChatService.Poll(): parse snippet.type to handle textMessageEvent, superChatEvent, newSponsorEvent
- MainViewModel: wire _youtubeChat.Start(liveChatId) in PrepareAndStartLiveAsync after broadcast creation
- MainViewModel: wire _youtubeChat.Stop() in StopStream before encoder stop
- MainViewModel: wire _youtubeChat.Dispose() in Shutdown
- MainWindow.xaml: updated chat DataTemplate with member badge (⭐), Super Chat highlight (💰 + gold border), timestamps (HH:mm)
- MainWindow.xaml.cs: auto-scroll ChatListBox on new messages via CollectionChanged
- Chat lifecycle is non-fatal: failures log and the stream continues without on-panel chat
This commit is contained in:
2026-08-19 11:59:29 -07:00
parent e97406d649
commit 1613acadc3
5 changed files with 182 additions and 10 deletions
+42 -2
View File
@@ -73,6 +73,7 @@ public class YouTubeChatService : IDisposable
{
var snippet = item.GetProperty("snippet");
var author = item.GetProperty("authorDetails");
var type = snippet.GetProperty("type").GetString() ?? "textMessageEvent";
var message = new ChatMessage
{
@@ -80,11 +81,50 @@ public class YouTubeChatService : IDisposable
AuthorName = author.GetProperty("displayName").GetString()!,
AuthorChannelId = author.GetProperty("channelId").GetString()!,
AuthorImageUrl = author.GetProperty("profileImageUrl").GetString()!,
Message = snippet.GetProperty("textMessageDetails").GetProperty("messageText").GetString()!,
Timestamp = DateTime.Parse(snippet.GetProperty("publishedAt").GetString()!),
IsMember = author.GetProperty("isChatSponsor").GetBoolean()
IsMember = author.GetProperty("isChatSponsor").GetBoolean(),
MembershipLevel = author.TryGetProperty("badgeText", out var badgeText)
? badgeText.GetString()
: null
};
switch (type)
{
case "superChatEvent":
message.IsSuperChat = true;
if (snippet.TryGetProperty("superChatDetails", out var scDetails))
{
message.SuperChatAmount = scDetails.TryGetProperty("amountMicros", out var amt)
? amt.GetDouble() / 1_000_000.0
: 0;
message.SuperChatCurrency = scDetails.TryGetProperty("currency", out var cur)
? cur.GetString()
: null;
message.Message = scDetails.TryGetProperty("userComment", out var comment)
? comment.GetString() ?? string.Empty
: string.Empty;
}
break;
case "newSponsorEvent":
message.Message = $"🎉 {message.AuthorName} became a member!";
if (snippet.TryGetProperty("membershipDetails", out var memDetails))
{
message.MembershipLevel = memDetails.TryGetProperty("memberLevelName", out var level)
? level.GetString()
: message.MembershipLevel;
}
break;
default: // textMessageEvent
message.Message = snippet.TryGetProperty("textMessageDetails", out var textDetails)
? textDetails.TryGetProperty("messageText", out var text)
? text.GetString() ?? string.Empty
: string.Empty
: string.Empty;
break;
}
MessageReceived?.Invoke(message);
}
}