From a48d2ca84fdcd3c93c43ac51f6a99dfbe79f3300 Mon Sep 17 00:00:00 2001 From: GD-Slime <82302542+GD-Slime@users.noreply.github.com> Date: Sat, 1 Jun 2024 10:26:21 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9B=B4=E6=92=AD=E9=97=B4=20=E6=8A=BD?= =?UTF-8?q?=E5=A5=96=E3=80=81=E5=BC=B9=E5=B9=95=E5=BB=B6=E8=BF=9F=E3=80=81?= =?UTF-8?q?=E9=86=92=E7=9B=AE=E7=95=99=E8=A8=80=E3=80=81=E8=88=B0=E9=95=BF?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E3=80=81=E5=90=84=E7=B1=BB=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E5=B0=8F=E9=97=AE=E9=A2=98=E7=BB=BC=E5=90=88=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=B8=8E=E4=BC=98=E5=8C=96=20(#651)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 红包/天选实际加入api 5cce86e6 fix #566 2. 央视新闻等官方直播间有问题 90c11d6c fix #647 3. 自己弹幕加框 3a0ba220 4. 自己和主播的弹幕特殊化 328bd45b 5. 弹幕延迟修复 79fd1df6 fix #633 6. 个人主页点击进入直播间按钮fix 8bdc79ba fix (在无直播间的用户主页还能看到直播间按钮) 7. 区别续费与新舰长 c1a96f6a 8. 支持调整对齐方式 1299633c 9. SuperChat加入弹幕与chatList & 天选红包信息加入屏蔽 & 禁言观众历史发言展示 & chat栏自动滚动优化 2a4b1826 --- .../Extensions/ApiModelExtensions.cs | 2 +- .../Extensions/StringExtensions.cs | 9 +- .../Extensions/StringHttpExtensions.cs | 11 +- src/BiliLite.UWP/Models/Common/Enumerates.cs | 30 +- .../Models/Common/Live/DanmuMsgModel.cs | 29 +- .../Models/Common/Live/GuardBuyMsgModel.cs | 10 + .../Models/Common/Live/InteractWordModel.cs | 2 +- .../Live/LiveMessageHandleActionsMap.cs | 122 +++++++- .../Live/LiveRoomAnchorLotteryInfoModel.cs | 19 +- .../Live/LiveRoomSuperChatUserInfoModel.cs | 2 +- .../Models/Common/SettingConstants.cs | 6 + .../Models/Requests/Api/Live/LiveRoomAPI.cs | 63 ++++ src/BiliLite.UWP/Modules/Live/LiveMessage.cs | 81 +++-- src/BiliLite.UWP/Modules/SettingVM.cs | 4 +- src/BiliLite.UWP/Pages/LiveDetailPage.xaml | 36 ++- src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs | 159 ++++++---- src/BiliLite.UWP/Pages/SettingPage.xaml.cs | 14 +- src/BiliLite.UWP/Pages/UserInfoPage.xaml | 4 +- src/BiliLite.UWP/Pages/UserInfoPage.xaml.cs | 3 +- .../Services/FrostMasterDanmakuController.cs | 1 + .../Services/NsDanmakuController.cs | 2 +- .../Live/LiveRoomLotteryViewModel.cs | 5 +- .../ViewModels/Live/LiveRoomViewModel.cs | 292 +++++++++++++++--- .../ViewModels/Live/SuperChatMsgViewModel.cs | 23 +- .../ViewModels/User/UserDetailViewModel.cs | 9 +- 25 files changed, 738 insertions(+), 200 deletions(-) diff --git a/src/BiliLite.UWP/Extensions/ApiModelExtensions.cs b/src/BiliLite.UWP/Extensions/ApiModelExtensions.cs index f1f4e24e..f36c74f1 100644 --- a/src/BiliLite.UWP/Extensions/ApiModelExtensions.cs +++ b/src/BiliLite.UWP/Extensions/ApiModelExtensions.cs @@ -36,7 +36,7 @@ public static async Task Request(this ApiModel api, [CallerMemberNa { if (api.need_cookie) { - return await api.url.PostHttpResultsWithCookie(api.body, api.headers); + return await api.url.PostHttpResultsWithCookie(api.body, api.headers, api.ExtraCookies); } return await api.url.PostHttpResultsAsync(api.body, api.headers); diff --git a/src/BiliLite.UWP/Extensions/StringExtensions.cs b/src/BiliLite.UWP/Extensions/StringExtensions.cs index 559b7981..7e72be4f 100644 --- a/src/BiliLite.UWP/Extensions/StringExtensions.cs +++ b/src/BiliLite.UWP/Extensions/StringExtensions.cs @@ -84,7 +84,7 @@ public static string TraditionalToSimplified(this string input) /// public static RichTextBlock ToRichTextBlock(this string txt, JObject emote, bool isLive = false, string fontColor = null, string fontWeight = "Normal", string lowProfilePrefix = "", - bool enableVideoSeekTime = false) + string textAlignment = "Left", bool enableVideoSeekTime = false) { var input = txt; try @@ -124,12 +124,13 @@ public static RichTextBlock ToRichTextBlock(this string txt, JObject emote, bool var xaml = string.Format(@" - {4}{0} - ", input, + {4}{0} + ", input, isLive ? 22 : 20, fontColor == null ? "" : $"Foreground=\"{fontColor}\"", $"FontWeight=\"{fontWeight}\"", - lowProfilePrefix); + lowProfilePrefix, + $"TextAlignment=\"{textAlignment}\""); if (!xaml.IsXmlString()) { throw new CustomizedErrorException("不是有效的xml字符串"); diff --git a/src/BiliLite.UWP/Extensions/StringHttpExtensions.cs b/src/BiliLite.UWP/Extensions/StringHttpExtensions.cs index 7847b839..9be6d292 100644 --- a/src/BiliLite.UWP/Extensions/StringHttpExtensions.cs +++ b/src/BiliLite.UWP/Extensions/StringHttpExtensions.cs @@ -208,7 +208,7 @@ public static async Task PostHttpResultsAsync(this string url, stri return httpResult; } - public static async Task PostHttpResultsWithCookie(this string url, string body, IDictionary headers = null) + public static async Task PostHttpResultsWithCookie(this string url, string body, IDictionary headers = null, IDictionary extraCookies = null) { try { @@ -223,6 +223,15 @@ public static async Task PostHttpResultsWithCookie(this string url, } cookies = cookieService.Cookies; var cookiesCollection = cookies.ToDictionary(x => x.Name, x => x.Value); + + if (extraCookies != null) + { + foreach (var kvp in extraCookies) + { + cookiesCollection.Add(kvp.Key, kvp.Value); + } + } + return await url.PostHttpResultsAsync(body, headers, cookiesCollection); } catch (Exception ex) diff --git a/src/BiliLite.UWP/Models/Common/Enumerates.cs b/src/BiliLite.UWP/Models/Common/Enumerates.cs index a8d96782..7c1be225 100644 --- a/src/BiliLite.UWP/Models/Common/Enumerates.cs +++ b/src/BiliLite.UWP/Models/Common/Enumerates.cs @@ -361,6 +361,11 @@ public enum MessageType /// GuardBuy, + /// + /// 新上舰消息, 可区分续费和新人 + /// + GuardBuyNew, + /// /// 房间信息更新 /// @@ -409,7 +414,7 @@ public enum MessageType /// /// 直播间等级禁言 /// - RoomSlient, + ChatLevelMute, } public enum MessageDelayType { @@ -443,4 +448,27 @@ public enum UserDynamicShowType Season = 2, Article = 3 } + + /// + /// 用户大航海等级 + /// + public enum UserCaptainType + { + None = 0, + + /// + /// 总督 + /// + Zongdu = 1, + + /// + /// 提督 + /// + Tidu = 2, + + /// + /// 舰长 + /// + JianZhang = 3, + } } \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Common/Live/DanmuMsgModel.cs b/src/BiliLite.UWP/Models/Common/Live/DanmuMsgModel.cs index 1e018fce..3b8c0047 100644 --- a/src/BiliLite.UWP/Models/Common/Live/DanmuMsgModel.cs +++ b/src/BiliLite.UWP/Models/Common/Live/DanmuMsgModel.cs @@ -30,10 +30,23 @@ public class DanmuMsgModel /// public string UserName { get; set; } + private string _userNameColor; /// /// 用户名颜色, 默认灰色 /// - public string UserNameColor { get; set; } = "#FF808080"; + public string UserNameColor + { + get => _userNameColor ?? + UserCaptain switch + { + UserCaptainType.JianZhang => "#FF23709E", + UserCaptainType.Tidu => "#FF7B166F", + UserCaptainType.Zongdu => "#FFC01039", + _ => "#FF808080" + }; + + set => _userNameColor = value; + } /// /// 用户名字重, 默认Normal @@ -83,17 +96,17 @@ public class DanmuMsgModel /// /// 勋章等级 /// - public string MedalLevel { get; set; } + public int MedalLevel { get; set; } /// /// 勋章颜色 /// public string MedalColor { get; set; } - + /// - /// 用户上的舰的名称 + /// 用户大航海等级 /// - public string UserCaptain { get; set; } + public UserCaptainType UserCaptain { get; set; } /// /// 用户上的舰的图片 @@ -102,9 +115,9 @@ public string UserCaptainImage { get => UserCaptain switch { - "舰长" => "ms-appx:///Assets/Live/ic_live_guard_3.png", - "提督" => "ms-appx:///Assets/Live/ic_live_guard_2.png", - "总督" => "ms-appx:///Assets/Live/ic_live_guard_1.png", + UserCaptainType.JianZhang => "ms-appx:///Assets/Live/ic_live_guard_3.png", //舰长 + UserCaptainType.Tidu => "ms-appx:///Assets/Live/ic_live_guard_2.png", //提督 + UserCaptainType.Zongdu => "ms-appx:///Assets/Live/ic_live_guard_1.png", //总督 _ => null, }; } diff --git a/src/BiliLite.UWP/Models/Common/Live/GuardBuyMsgModel.cs b/src/BiliLite.UWP/Models/Common/Live/GuardBuyMsgModel.cs index ff406c28..6299e1b4 100644 --- a/src/BiliLite.UWP/Models/Common/Live/GuardBuyMsgModel.cs +++ b/src/BiliLite.UWP/Models/Common/Live/GuardBuyMsgModel.cs @@ -67,5 +67,15 @@ public Color CardColor /// 礼物名称 /// public string GiftName { get; set; } + + /// + /// 购买的时间单位, 如"月" + /// + public string Unit { get; set;} + + /// + /// 庆祝消息正文 + /// + public string Message { get; set; } } } \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Common/Live/InteractWordModel.cs b/src/BiliLite.UWP/Models/Common/Live/InteractWordModel.cs index 5a46eba7..0b5e7dd5 100644 --- a/src/BiliLite.UWP/Models/Common/Live/InteractWordModel.cs +++ b/src/BiliLite.UWP/Models/Common/Live/InteractWordModel.cs @@ -12,7 +12,7 @@ public class InteractWordModel public string MedalName { get; set; } - public string MedalLevel { get; set; } + public int MedalLevel { get; set; } public string MedalColor { get; set; } diff --git a/src/BiliLite.UWP/Models/Common/Live/LiveMessageHandleActionsMap.cs b/src/BiliLite.UWP/Models/Common/Live/LiveMessageHandleActionsMap.cs index 660db35c..e5ae4779 100644 --- a/src/BiliLite.UWP/Models/Common/Live/LiveMessageHandleActionsMap.cs +++ b/src/BiliLite.UWP/Models/Common/Live/LiveMessageHandleActionsMap.cs @@ -6,8 +6,9 @@ using Newtonsoft.Json; using BiliLite.Extensions; using BiliLite.Services; -using Windows.UI; using Windows.UI.Xaml.Media; +using Color = Windows.UI.Color; +using System.Text.RegularExpressions; namespace BiliLite.Models.Common.Live { @@ -28,6 +29,7 @@ public LiveMessageHandleActionsMap() { MessageType.AnchorLotteryStart, AnchorLotteryStart }, { MessageType.AnchorLotteryAward, AnchorLotteryAward }, { MessageType.GuardBuy, GuardBuy }, + { MessageType.GuardBuyNew, GuardBuyNew}, { MessageType.RoomChange, RoomChange }, { MessageType.RoomBlock, RoomBlock }, { MessageType.WaringOrCutOff, WaringOrCutOff }, @@ -37,7 +39,7 @@ public LiveMessageHandleActionsMap() { MessageType.RedPocketLotteryWinner, RedPocketLotteryWinner }, { MessageType.OnlineRankChange, OnlineRankChange }, { MessageType.StopLive, StopLive }, - { MessageType.RoomSlient, RoomSlient }, + { MessageType.ChatLevelMute, ChatLevelMute }, }; } @@ -51,6 +53,10 @@ public LiveMessageHandleActionsMap() public event EventHandler AnchorInfoLiveInfo; + public event EventHandler AddShieldWord; + + public event EventHandler DelShieldWord; + private void ConnectSuccess(LiveRoomViewModel viewModel, object message) { viewModel.Messages.Add(new DanmuMsgModel() @@ -71,10 +77,21 @@ private void WatchedChange(LiveRoomViewModel viewModel, object message) private void Danmu(LiveRoomViewModel viewModel, object message) { var m = message as DanmuMsgModel; - if (viewModel.Messages.Count >= viewModel.CleanCount) + + // 自己的消息和主播的消息特殊处理 + if (m.Uid == SettingService.Account.UserID.ToString() || m.Uid == viewModel.AnchorUid.ToString()) { - viewModel.Messages.RemoveAt(0); + // 暂时借用了直播间标题修改的颜色... 找不到好看的颜色了 + m.CardColor = new SolidColorBrush(Color.FromArgb(255, 228, 255, 233)); + m.RichText = m.Text.ToRichTextBlock(m.Emoji, true, fontWeight: "Medium", fontColor: "#ff1e653a"); + if (m.Uid == viewModel.AnchorUid.ToString()) + { + m.Role = "主播"; + m.ShowAdmin = Visibility.Visible; + } } + + if (viewModel.Messages.Count >= viewModel.CleanCount) viewModel.Messages.RemoveAt(0); viewModel.Messages.Add(m); AddNewDanmu?.Invoke(null, m); } @@ -142,21 +159,49 @@ private void InteractWord(LiveRoomViewModel viewModel, object message) private void SuperChat(LiveRoomViewModel viewModel, object message) { - viewModel.SuperChats.Add(message as SuperChatMsgViewModel); + viewModel.SuperChats.Insert(0, message as SuperChatMsgViewModel); // 新sc总会显示在最前 + + // SuperChat也是Chat :) + var info = message as SuperChatMsgViewModel; + var msg = new DanmuMsgModel + { + Text = info.Message, + DanmuColor = info.FontColor, + RichText = info.Message.ToRichTextBlock(null, true, fontWeight: "Medium", fontColor: "White"), + CardColor = new SolidColorBrush(info.BackgroundBottomColor.StrToColor()), + Face = info.Face, + UserName = info.Username, + UserNameFontWeight = "Semibold", + Uid = info.Uid.ToString(), + UserCaptain = info.GuardLevel, + ShowCaptain = Visibility.Visible, + MedalColor = info.MedalColor, + MedalName = info.MedalName, + MedalLevel = info.MedalLevel, + ShowMedal = (!string.IsNullOrEmpty(info.MedalColor) && + !string.IsNullOrEmpty(info.MedalName) && + info.MedalLevel > 0) ? Visibility.Visible : Visibility.Collapsed, + }; + + Danmu(viewModel, msg); } private void AnchorLotteryStart(LiveRoomViewModel viewModel, object message) { if (!viewModel.ReceiveLotteryMsg) return; - var info = message.ToString(); - viewModel.LotteryViewModel.SetAnchorLotteryInfo(JsonConvert.DeserializeObject(info)); + var info = JsonConvert.DeserializeObject(message.ToString()); + viewModel.LotteryDanmu["AnchorLottery"] = info.Danmu; + AddShieldWord?.Invoke(info, info.Danmu); + viewModel.LotteryViewModel.SetAnchorLotteryInfo(info); } private void RedPocketLotteryStart(LiveRoomViewModel viewModel, object message) { if (!viewModel.ReceiveLotteryMsg) return; - var info = message.ToString(); - viewModel.LotteryViewModel.SetRedPocketLotteryInfo(JsonConvert.DeserializeObject(info)); + var info = JsonConvert.DeserializeObject(message.ToString()); + viewModel.LotteryDanmu["RedPocketLottery"] = info.Danmu; + AddShieldWord?.Invoke(info, info.Danmu); + viewModel.LotteryViewModel.SetRedPocketLotteryInfo(info); viewModel.ShowRedPocketLotteryWinnerList = false; viewModel.RedPocketSendDanmuBtnText = viewModel.Attention ? "一键发送弹幕" : "一键关注并发送弹幕"; @@ -167,6 +212,7 @@ private void RedPocketLotteryWinner(LiveRoomViewModel viewModel, object message) if (!viewModel.ReceiveLotteryMsg) return; var info = JsonConvert.DeserializeObject(message.ToString()); RedPocketLotteryEnd?.Invoke(this, info); + DelShieldWord?.Invoke(info, viewModel.LotteryDanmu["RedPocketLottery"]); } private void AnchorLotteryAward(LiveRoomViewModel viewModel, object message) @@ -174,6 +220,7 @@ private void AnchorLotteryAward(LiveRoomViewModel viewModel, object message) if (!viewModel.ReceiveLotteryMsg) return; var info = JsonConvert.DeserializeObject(message.ToString()); AnchorLotteryEnd?.Invoke(this, info); + DelShieldWord?.Invoke(info, viewModel.LotteryDanmu["AnchorLottery"]); } private void GuardBuy(LiveRoomViewModel viewModel, object message) @@ -192,6 +239,36 @@ private void GuardBuy(LiveRoomViewModel viewModel, object message) viewModel.ReloadGuardList().RunWithoutAwait(); } + private void GuardBuyNew(LiveRoomViewModel viewModel, object message) + { + var info = message as GuardBuyMsgModel; + + var isNewGuard = info.Message.Contains("开通"); + + int accompanyDays = -1; + var match = Regex.Match(info.Message, @"今天是TA陪伴主播的第(\d+)天"); + if (match.Success) accompanyDays = match.Groups[1].Value.ToInt32(); + + var text = info.UserName + + (isNewGuard ? "\n新开通了" : "\n续费了") + + $"主播的{info.GiftName}" + + (info.Num > 1 ? $"×{info.Num}个{info.Unit}" : "") + + "🎉" + + ((match.Success && accompanyDays > 1) ? $"\nTA已陪伴主播{accompanyDays}天💖" : ""); + + var msg = new DanmuMsgModel + { + ShowUserName = Visibility.Collapsed, + ShowUserFace = Visibility.Collapsed, + RichText = text.ToRichTextBlock(null, fontWeight: "SemiBold", fontColor: info.FontColor, textAlignment: "Center"), + CardColor = new SolidColorBrush(info.CardColor), + CardHorizontalAlignment = HorizontalAlignment.Center, + }; + + viewModel.Messages.Add(msg); + if (isNewGuard) viewModel.ReloadGuardList().RunWithoutAwait(); + } + private void RoomChange(LiveRoomViewModel viewModel, object message) { var info = message as RoomChangeMsgModel; @@ -199,7 +276,7 @@ private void RoomChange(LiveRoomViewModel viewModel, object message) { ShowUserFace = Visibility.Collapsed, ShowUserName = Visibility.Collapsed, - RichText = ($"直播间标题已修改:\n{viewModel.RoomTitle} ➡️ {info.Title}").ToRichTextBlock(null, fontWeight: "SemiBold", fontColor: "#ff1e653a"), //一种绿色 + RichText = ($"直播间标题已修改:\n{viewModel.RoomTitle}\n🔽\n{info.Title}").ToRichTextBlock(null, fontWeight: "SemiBold", textAlignment:"Center", fontColor: "#ff1e653a"), //一种绿色 CardColor = new SolidColorBrush(Color.FromArgb(255, 228, 255, 233)), CardHorizontalAlignment = HorizontalAlignment.Center, }; @@ -214,12 +291,31 @@ private void RoomBlock(LiveRoomViewModel viewModel, object message) { ShowUserFace = Visibility.Collapsed, ShowUserName = Visibility.Collapsed, - RichText = (info.UserName + " 被直播间禁言🚫").ToRichTextBlock(null, fontWeight: "SemiBold", fontColor: "White"), // 白色 + RichText = (info.UserName + " 被直播间禁言🚫").ToRichTextBlock(null, fontWeight: "SemiBold", fontColor: "White", textAlignment: "Center"), CardColor = new SolidColorBrush(Color.FromArgb(255, 235, 45, 80)), // 一种红色 CardHorizontalAlignment = HorizontalAlignment.Center, }; viewModel.Messages.Add(msg); + + var text = info.UserName + " 发言历史:\n"; + var previousChatList = viewModel.Messages + .Where(item => item.Uid == info.UserID) // 筛选出符合条件的对象 + .OrderByDescending(item => viewModel.Messages.IndexOf(item)) // 根据索引倒序排序 + .Take(3) // 取前三个 + .Select(item => item.Text); // 提取Text字段 + if (previousChatList.Count() == 0) return; + + text += string.Join("\n", previousChatList); + msg = new DanmuMsgModel() + { + ShowUserFace = Visibility.Collapsed, + ShowUserName = Visibility.Collapsed, + RichText = (text).ToRichTextBlock(null, fontWeight: "SemiBold", fontColor: "White", textAlignment: "Center"), + CardColor = new SolidColorBrush(Color.FromArgb(255, 235, 45, 80)), // 一种红色 + CardHorizontalAlignment = HorizontalAlignment.Center, + }; + viewModel.Messages.Add(msg); } private void WaringOrCutOff(LiveRoomViewModel viewModel, object message) @@ -241,7 +337,7 @@ private void WaringOrCutOff(LiveRoomViewModel viewModel, object message) { ShowUserFace = Visibility.Collapsed, ShowUserName = Visibility.Collapsed, - RichText = (text + "\n" + info.Message).ToRichTextBlock(null, fontColor: "White", fontWeight: "SemiBold"), + RichText = (text + "\n" + info.Message).ToRichTextBlock(null, fontColor: "White", fontWeight: "SemiBold", textAlignment: "Center"), CardColor = cardColor, CardHorizontalAlignment = HorizontalAlignment.Center, }; @@ -280,7 +376,7 @@ private void StopLive(LiveRoomViewModel viewModel, object message) }); } - private void RoomSlient(LiveRoomViewModel viewModel, object level) + private void ChatLevelMute(LiveRoomViewModel viewModel, object level) { viewModel.Messages.Add(new DanmuMsgModel() { diff --git a/src/BiliLite.UWP/Models/Common/Live/LiveRoomAnchorLotteryInfoModel.cs b/src/BiliLite.UWP/Models/Common/Live/LiveRoomAnchorLotteryInfoModel.cs index 8881fe2b..ce70f905 100644 --- a/src/BiliLite.UWP/Models/Common/Live/LiveRoomAnchorLotteryInfoModel.cs +++ b/src/BiliLite.UWP/Models/Common/Live/LiveRoomAnchorLotteryInfoModel.cs @@ -36,7 +36,7 @@ public class LiveRoomAnchorLotteryInfoModel public StackPanel WinnerList => AwardUsers == null ? new StackPanel() : new LiveRoomEndAnchorLotteryInfoModel().GenerateWinnerList(AwardUsers); /// - /// 未知... + /// 需要发礼物才能参与的抽奖中, 已发送的礼物数量 /// [JsonProperty("cur_gift_num")] public int CurGiftNum { get; set; } @@ -44,10 +44,13 @@ public class LiveRoomAnchorLotteryInfoModel [JsonProperty("current_time")] public long CurrentTime { get; set; } + /// + /// 抽奖弹幕 + /// public string Danmu { get; set; } /// - /// 次级礼物信息? 因为有可能是b站礼物所以有id + /// 需要发礼物才能参与的抽奖中, 需要的礼物Id /// [JsonProperty("gift_id")] public int GiftId { get; set; } @@ -56,19 +59,19 @@ public class LiveRoomAnchorLotteryInfoModel public bool ShowGift => GiftId != 0; /// - /// 次级礼物名称 + /// 参与抽奖需要发送的礼物名字 /// [JsonProperty("gift_name")] public string GiftName { get; set; } /// - /// 次级礼物数量 + /// 参与抽奖需要发送的礼物数量 /// [JsonProperty("gift_num")] public int GiftNum { get; set; } /// - /// 次级礼物价格 + /// 参与抽奖需要发送的礼物价格 /// [JsonProperty("gift_price")] public int GiftPrice { get; set; } @@ -79,8 +82,14 @@ public class LiveRoomAnchorLotteryInfoModel [JsonProperty("goaway_time")] public int GoawayTime { get; set; } + /// + /// 抽奖Id + /// public int Id { get; set; } + /// + /// 是否已加入, 1为已参与 + /// [JsonProperty("join_type")] public int JoinType { get; set; } diff --git a/src/BiliLite.UWP/Models/Common/Live/LiveRoomSuperChatUserInfoModel.cs b/src/BiliLite.UWP/Models/Common/Live/LiveRoomSuperChatUserInfoModel.cs index 2468d4f0..c628772e 100644 --- a/src/BiliLite.UWP/Models/Common/Live/LiveRoomSuperChatUserInfoModel.cs +++ b/src/BiliLite.UWP/Models/Common/Live/LiveRoomSuperChatUserInfoModel.cs @@ -12,7 +12,7 @@ public class LiveRoomSuperChatUserInfoModel public string FaceFrame { get; set; } [JsonProperty("guard_level")] - public int GuardLevel { get; set; } + public UserCaptainType GuardLevel { get; set; } [JsonProperty("user_level")] public int UserLevel { get; set; } diff --git a/src/BiliLite.UWP/Models/Common/SettingConstants.cs b/src/BiliLite.UWP/Models/Common/SettingConstants.cs index e4170d21..976a990d 100644 --- a/src/BiliLite.UWP/Models/Common/SettingConstants.cs +++ b/src/BiliLite.UWP/Models/Common/SettingConstants.cs @@ -466,6 +466,12 @@ public class Live [SettingKey(typeof(bool))] public const string HIDE_LOTTERY = "LiveHideLottery"; + /// + /// 隐藏抽奖弹幕关键字 + /// + [SettingKey(typeof(bool))] + public const string HIDE_LOTTERY_DANMU = "LiveHideLotteryDanmu"; + /// /// 直播流默认源 /// diff --git a/src/BiliLite.UWP/Models/Requests/Api/Live/LiveRoomAPI.cs b/src/BiliLite.UWP/Models/Requests/Api/Live/LiveRoomAPI.cs index 29a287c1..4a4e7456 100644 --- a/src/BiliLite.UWP/Models/Requests/Api/Live/LiveRoomAPI.cs +++ b/src/BiliLite.UWP/Models/Requests/Api/Live/LiveRoomAPI.cs @@ -1,11 +1,20 @@ using BiliLite.Extensions; using BiliLite.Services; +using Microsoft.Extensions.DependencyInjection; using System; +using System.Collections.Generic; namespace BiliLite.Models.Requests.Api.Live { public class LiveRoomAPI : BaseApi { + private readonly CookieService m_cookieService; + + public LiveRoomAPI() + { + m_cookieService = App.ServiceProvider.GetRequiredService(); + } + /// /// 直播间信息 /// @@ -314,6 +323,11 @@ public ApiModel RoomEntryAction(int roomId) return api; } + /// + /// 获取弹幕连接信息 + /// + /// 房间号 + /// public ApiModel GetDanmuInfo(int roomId) { var api = new ApiModel() @@ -326,6 +340,10 @@ public ApiModel GetDanmuInfo(int roomId) return api; } + /// + /// 获取BUVID + /// + /// public ApiModel GetBuvid() { var api = new ApiModel() @@ -336,5 +354,50 @@ public ApiModel GetBuvid() }; return api; } + + /// + /// 参与天选抽奖 + /// + /// 房间号 + /// 抽奖Id + /// + public ApiModel JoinAnchorLottery(int roomId, int lottery_id, string buvid3, int gift_id = 0, int gift_num = 0) + { + var csrf = m_cookieService.GetCSRFToken(); + var api = new ApiModel + { + method = RestSharp.Method.Post, + baseUrl = "https://api.live.bilibili.com/xlive/lottery-interface/v1/Anchor/Join", + body = $"room_id={roomId}&id={lottery_id}&platform=pc&csrf={csrf}", + need_cookie = true, + headers = ApiHelper.GetDefaultHeaders(), + }; + if (gift_id != 0 && gift_num != 0) { api.body += $"&gift_id={gift_id}&gift_num={gift_num}"; } + api.ExtraCookies = new Dictionary() { { "buvid3", buvid3 } }; + return api; + } + + /// + /// 参与人气红包抽奖 + /// + /// 用户uid + /// 房间号 + /// 主播uid + /// 抽奖id + /// + public ApiModel JoinRedPocketLottery(long uid, int room_id, long ruid, int lot_id) + { + var csrf = m_cookieService.GetCSRFToken(); + var api = new ApiModel() + { + method = RestSharp.Method.Post, + baseUrl = "https://api.live.bilibili.com/xlive/lottery-interface/v1/popularityRedPocket/RedPocketDraw", + parameter = $"csrf={csrf}", + body = $"uid={uid}&room_id={room_id}&ruid={ruid}&lot_id={lot_id}&ts={TimeExtensions.GetTimestampS()}", + need_cookie = true, + headers = ApiHelper.GetDefaultHeaders() + }; + return api; + } } } diff --git a/src/BiliLite.UWP/Modules/Live/LiveMessage.cs b/src/BiliLite.UWP/Modules/Live/LiveMessage.cs index 894b3efe..36308c4b 100644 --- a/src/BiliLite.UWP/Modules/Live/LiveMessage.cs +++ b/src/BiliLite.UWP/Modules/Live/LiveMessage.cs @@ -15,6 +15,7 @@ using System.IO.Compression; using BiliLite.Models.Common.Live; using BiliLite.ViewModels.Live; +using System.Diagnostics; /* * 参考文档: @@ -31,12 +32,13 @@ public class LiveMessage : IDisposable public delegate void MessageHandler(MessageType type, object message); public event MessageHandler NewMessage; ClientWebSocket ws; + Stopwatch Stopwatch; - long PreviousDanmuPackageTimeStamp = 0; public LiveMessage() { ws = new ClientWebSocket(); + Stopwatch = new Stopwatch(); } private static System.Timers.Timer heartBeatTimer; public async Task Connect(int roomID, long uid, string token, string buvid, string host, CancellationToken cancellationToken) @@ -149,12 +151,10 @@ private async Task ParseData(byte[] data) // 记录此包中的普通弹幕信息个数 var danmuCount = Regex.Matches(text, Regex.Escape("DANMU_MSG"), RegexOptions.IgnoreCase).Count; - // 记录时间戳 - var timeStampNow = (long)0; if (danmuCount > 0) { - timeStampNow = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - PreviousDanmuPackageTimeStamp = PreviousDanmuPackageTimeStamp == 0 ? timeStampNow : PreviousDanmuPackageTimeStamp; + if (Stopwatch.IsRunning == true) Stopwatch.Stop(); + else Stopwatch.Start(); } //可能有多条数据,做个分割 @@ -163,10 +163,13 @@ private async Task ParseData(byte[] data) foreach (var item in textLines) { // 使用上波弹幕到这波的间隔确定延迟 - var danmuDelay = danmuCount > 0 ? ((timeStampNow - PreviousDanmuPackageTimeStamp) / danmuCount * 1.3) : 0; + var danmuDelay = danmuCount > 0 ? (Stopwatch.Elapsed.TotalMilliseconds / danmuCount * 1.2) : 0; + // 控制弹幕延迟最小和最大长度 + danmuDelay = danmuDelay < 20 ? 20 : danmuDelay; + danmuDelay = danmuDelay > 1000 ? 1000 : danmuDelay; var delay = ParseMessage(item) switch { - MessageDelayType.DanmuMessage => danmuDelay <= 30 ? 30 : danmuDelay, // 常规弹幕类型, 加延迟 + MessageDelayType.DanmuMessage => danmuDelay, // 常规弹幕类型, 加延迟 MessageDelayType.GiftMessage => 100 / textLines.Length, // 礼物消息类型, 加一点延迟 MessageDelayType.SystemMessage => 0.0, // 其他系统消息类型, 无需加延迟 _ => 0.0 @@ -182,12 +185,9 @@ private async Task ParseData(byte[] data) } } - if (danmuCount > 0) - { - PreviousDanmuPackageTimeStamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + if (danmuCount > 0) Stopwatch.Restart(); } } - } private MessageDelayType ParseMessage(string jsonMessage) { @@ -261,24 +261,9 @@ private MessageDelayType ParseMessage(string jsonMessage) } // 是否为舰长 - if (obj["info"][3] != null && obj["info"][3].ToArray().Length != 0 && - obj["info"][3][10] != null && Convert.ToInt32(obj["info"][3][10].ToString()) != 0) + if (obj["info"][7] != null && obj["info"][7].ToString().Length != 0) { - switch (Convert.ToInt32(obj["info"][3][10].ToString())) - { - case 3: - msg.UserCaptain = "舰长"; - msg.UserNameColor = "#FF23709E"; - break; - case 2: - msg.UserCaptain = "提督"; - msg.UserNameColor = "#FF7B166F"; - break; - case 1: - msg.UserCaptain = "总督"; - msg.UserNameColor = "#FFC01039"; - break; - } + msg.UserCaptain = (UserCaptainType)obj["info"][7].ToInt32(); msg.ShowCaptain = Visibility.Visible; } @@ -286,7 +271,7 @@ private MessageDelayType ParseMessage(string jsonMessage) if (obj["info"][3] != null && obj["info"][3].ToArray().Length != 0) { msg.MedalName = obj["info"][3][1].ToString(); - msg.MedalLevel = obj["info"][3][0].ToString(); + msg.MedalLevel = obj["info"][3][0].ToInt32(); msg.MedalColor = obj["info"][3][4].ToString(); msg.ShowMedal = Visibility.Visible; } @@ -350,7 +335,7 @@ private MessageDelayType ParseMessage(string jsonMessage) if (obj["data"]["fans_medal"]["medal_level"].ToInt32() != 0) { w.MedalName = obj["data"]["fans_medal"]["medal_name"].ToString(); - w.MedalLevel = obj["data"]["fans_medal"]["medal_level"].ToString(); + w.MedalLevel = obj["data"]["fans_medal"]["medal_level"].ToInt32(); w.MedalColor = obj["data"]["fans_medal"]["medal_color"].ToString(); w.ShowMedal = Visibility.Visible; } @@ -414,6 +399,15 @@ private MessageDelayType ParseMessage(string jsonMessage) msgView.Message = obj["data"]["message"].ToString(); msgView.Price = obj["data"]["price"].ToInt32(); msgView.Username = obj["data"]["user_info"]["uname"].ToString(); + msgView.GuardLevel = (UserCaptainType)obj["data"]["user_info"]["guard_level"].ToInt32(); + msgView.Uid = obj["data"]["uid"].ToInt64(); + + if (obj["data"]["medal_info"] != null && obj["data"]["medal_info"].ToArray().Length != 0) + { + msgView.MedalLevel = obj["data"]["medal_info"]["medal_level"].ToInt32(); + msgView.MedalName = obj["data"]["medal_info"]["medal_name"].ToString(); + msgView.MedalColor = obj["data"]["medal_info"]["medal_color"].ToString(); + } NewMessage?.Invoke(MessageType.SuperChat, msgView); return MessageDelayType.SystemMessage; } @@ -432,19 +426,38 @@ private MessageDelayType ParseMessage(string jsonMessage) } return MessageDelayType.SystemMessage; } - else if (cmd == "GUARD_BUY") + //else if (cmd == "GUARD_BUY") + //{ + // if (obj["data"] != null) + // { + // NewMessage?.Invoke(MessageType.GuardBuy, new GuardBuyMsgModel() + // { + // GiftId = obj["data"]["gift_id"].ToInt32(), + // GiftName = obj["data"]["gift_name"].ToString(), + // Num = obj["data"]["num"].ToInt32(), + // Price = obj["data"]["price"].ToInt32(), + // UserName = obj["data"]["username"].ToString(), + // UserID = obj["data"]["uid"].ToString(), + // GuardLevel = obj["data"]["guard_level"].ToInt32(), + // }); + // } + // return MessageDelayType.SystemMessage; + //} + else if (cmd == "USER_TOAST_MSG") { if (obj["data"] != null) { - NewMessage?.Invoke(MessageType.GuardBuy, new GuardBuyMsgModel() + NewMessage?.Invoke(MessageType.GuardBuyNew, new GuardBuyMsgModel() { GiftId = obj["data"]["gift_id"].ToInt32(), - GiftName = obj["data"]["gift_name"].ToString(), + GiftName = obj["data"]["role_name"].ToString(), Num = obj["data"]["num"].ToInt32(), Price = obj["data"]["price"].ToInt32(), UserName = obj["data"]["username"].ToString(), UserID = obj["data"]["uid"].ToString(), GuardLevel = obj["data"]["guard_level"].ToInt32(), + Unit = obj["data"]["unit"].ToString(), + Message = obj["data"]["toast_msg"].ToString(), }); } return MessageDelayType.SystemMessage; @@ -513,7 +526,7 @@ private MessageDelayType ParseMessage(string jsonMessage) { if (obj["data"] != null) { - NewMessage?.Invoke(MessageType.RoomSlient, obj["data"]["level"].ToInt32()); + NewMessage?.Invoke(MessageType.ChatLevelMute, obj["data"]["level"].ToInt32()); } return MessageDelayType.SystemMessage; } diff --git a/src/BiliLite.UWP/Modules/SettingVM.cs b/src/BiliLite.UWP/Modules/SettingVM.cs index 47a29950..fb0f5866 100644 --- a/src/BiliLite.UWP/Modules/SettingVM.cs +++ b/src/BiliLite.UWP/Modules/SettingVM.cs @@ -87,7 +87,7 @@ public SettingVM() /// 弹幕屏蔽关键词列表 /// public ObservableCollection ShieldWords { get; set; } - public ObservableCollection LiveWords { get; set; } + public ObservableCollection LiveShieldWords { get; set; } public ObservableCollection ShieldUsers { get; set; } public ObservableCollection ShieldRegulars { get; set; } public List CDNServers { get; set; } @@ -95,7 +95,7 @@ public SettingVM() public void LoadShieldSetting() { - LiveWords = SettingService.GetValue>(SettingConstants.Live.SHIELD_WORD, new ObservableCollection() { }); + LiveShieldWords = SettingService.GetValue>(SettingConstants.Live.SHIELD_WORD, new ObservableCollection() { }); ShieldWords = SettingService.GetValue>(SettingConstants.VideoDanmaku.SHIELD_WORD, new ObservableCollection() { }); //正则关键词 diff --git a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml index 1ce645d6..1f1977c5 100644 --- a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml +++ b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml @@ -308,7 +308,7 @@ - 房管 + @@ -373,7 +373,7 @@ - + @@ -461,7 +461,7 @@ - + 设置 @@ -503,24 +503,26 @@ - - + + - + + 关键词屏蔽 + 💡点击灰色按钮即可删除对应的关键词 - - + + - - + + - + @@ -528,7 +530,7 @@ - + @@ -804,7 +806,7 @@ - + @@ -845,7 +847,7 @@ 弹幕口令: 赠送礼物:x 限制条件:--> - + @@ -1119,7 +1121,7 @@ - + @@ -1354,9 +1356,9 @@ - + - 大航海() + 大航海 diff --git a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs index 4045226a..d0c87ab0 100644 --- a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs @@ -61,8 +61,11 @@ public sealed partial class LiveDetailPage : BasePage SettingVM settingVM; DispatcherTimer timer_focus; DispatcherTimer controlTimer; + DispatcherTimer chatScrollTimer; private bool changePlayUrlFlag = false; + private bool isPointerInChatList = false; + private bool isPointerInThisPage = true; public LiveDetailPage() { @@ -87,10 +90,13 @@ public LiveDetailPage() dataTransferManager.DataRequested += DataTransferManager_DataRequested; //每过2秒就设置焦点 timer_focus = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(2) }; - timer_focus.Tick += Timer_focus_Tick; controlTimer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) }; controlTimer.Tick += ControlTimer_Tick; + chatScrollTimer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(3)}; + chatScrollTimer.Tick += ChatScrollTimer_Tick; + chatScrollTimer.Start(); + settingVM = new SettingVM(); m_liveRoomViewModel = new LiveRoomViewModel(); @@ -98,9 +104,22 @@ public LiveDetailPage() m_liveRoomViewModel.AddNewDanmu += LiveRoomViewModelAddNewDanmu; m_liveRoomViewModel.AnchorLotteryEnd += LiveRoomViewModelAnchorLotteryEnd; m_liveRoomViewModel.RedPocketLotteryEnd += LiveRoomViewModelRedPocketLotteryEnd; - m_liveRoomViewModel.ChatScrollToEnd += LiveRoomViewModelChatScrollToEnd; - m_liveRoomViewModel.LotteryViewModel.AnchorLotteryStart += LiveRoomViewModelAnchorLotteryStart; + m_liveRoomViewModel.AnchorLotteryStart += LiveRoomViewModelAnchorLotteryStart; m_liveRoomViewModel.SetManualPlayUrl += LiveRoomViewModelSetManualPlayUrl; + m_liveRoomViewModel.AddLotteryShieldWord += (sender, word) => + { + if (m_liveRoomViewModel.ShowLotteryDanmu) return; + AddShieldWord(word); + if (sender is LiveRoomAnchorLotteryInfoModel) m_liveRoomViewModel.LotteryDanmu["AnchorLottery"] = (sender as LiveRoomAnchorLotteryInfoModel).Danmu; + if (sender is LiveRoomRedPocketLotteryInfoModel) m_liveRoomViewModel.LotteryDanmu["RedPocketLottery"] = (sender as LiveRoomRedPocketLotteryInfoModel).Danmu; + }; + m_liveRoomViewModel.DelShieldWord += (_, word) => DelShieldWord(word); + m_liveRoomViewModel.SpecialLiveRoomHideElements += (_, e) => + { + pivot.Items.Remove(pivot_Guard); + BottomBtnGiftRow.Visibility = Visibility.Collapsed; + BottomGiftBar.Visibility = Visibility.Collapsed; + }; this.Loaded += LiveDetailPage_Loaded; this.Unloaded += LiveDetailPage_Unloaded; @@ -146,6 +165,18 @@ private void ControlTimer_Tick(object sender, object e) } } + private void ChatScrollTimer_Tick(object sender, object e) + { + ChatScrollToBottom(); + chatScrollTimer.Stop(); + chatScrollTimer.Start(); + } + + private void ChatScrollToBottom() + { + if (list_chat.Items.Count > 0 && !isPointerInChatList && isPointerInThisPage) list_chat.ScrollIntoView(list_chat.Items[list_chat.Items.Count - 1]); + } + private void Timer_focus_Tick(object sender, object e) { var element = FocusManager.GetFocusedElement(); @@ -166,19 +197,14 @@ private void LiveRoomViewModelAnchorLotteryEnd(object sender, LiveRoomEndAnchorL var str = e.AwardUsers.Aggregate("", (current, item) => current + (item.Uname + "、")); str = str.TrimEnd('、'); var msg = $"天选时刻 开奖信息:\r\n奖品: {e.AwardName} \r\n中奖用户:{str}"; - foreach(var awardUser in e.AwardUsers) - { - if (awardUser.Uid == SettingService.Account.UserID) - { - msg += $"\r\n你已抽中奖品: {e.AwardName}, 恭喜欧皇~"; - } - } + msg += e.AwardUsers.Any(user => user.Uid == SettingService.Account.UserID) ? $"\r\n你已抽中奖品: {e.AwardName}, 恭喜欧皇~" : ""; + Notify.ShowMessageToast(msg, new List(), 10); AnchorLotteryWinnerList.Content = e.WinnerList; m_liveRoomViewModel.ShowAnchorLotteryWinnerList = true; m_liveRoomViewModel.LoadBag().RunWithoutAwait(); } - + private void LiveRoomViewModelRedPocketLotteryEnd(object sender, LiveRoomEndRedPocketLotteryInfoModel e) { var winners = e.Winners; @@ -188,7 +214,8 @@ private void LiveRoomViewModelRedPocketLotteryEnd(object sender, LiveRoomEndRedP foreach (var winner in winners) { if (winner[0] == (SettingService.Account.UserID).ToString()) { - Notify.ShowMessageToast($"你已在人气红包抽中 {awards[winner[3]].AwardName} , 赶快查看吧~"); + Notify.ShowMessageToast($"你已在人气红包抽奖中抽中 {awards[winner[3]].AwardName} , 赶快到背包中查看吧~", 5); + break; } } m_liveRoomViewModel.LoadBag().RunWithoutAwait(); @@ -198,13 +225,13 @@ private void LiveRoomViewModelAddNewDanmu(object sender, DanmuMsgModel e) { if (m_danmakuController.DanmakuViewModel.IsHide) return; - if (settingVM.LiveWords != null && settingVM.LiveWords.Count > 0) + if (settingVM.LiveShieldWords != null && settingVM.LiveShieldWords.Count > 0) { - if (settingVM.LiveWords.FirstOrDefault(x => e.Text.Contains(x)) != null) return; + if (settingVM.LiveShieldWords.FirstOrDefault(x => e.Text.Contains(x)) != null) return; } try { - m_danmakuController.AddLiveDanmaku(e.Text, false, e.DanmuColor.StrToColor()); + m_danmakuController.AddLiveDanmaku(e.Text, (SettingService.Account.Logined && e.Uid.ToInt64() == SettingService.Account.UserID), e.DanmuColor.StrToColor()); } catch (Exception ex) { @@ -213,11 +240,6 @@ private void LiveRoomViewModelAddNewDanmu(object sender, DanmuMsgModel e) } } - private void LiveRoomViewModelChatScrollToEnd(object sender, EventArgs e) - { - list_chat.ScrollIntoView(list_chat.Items[list_chat.Items.Count - 1]); - } - #region 页面生命周期 private void LiveDetailPage_Unloaded(object sender, RoutedEventArgs e) @@ -706,12 +728,21 @@ private void LoadSetting() //屏蔽抽奖信息 LiveSettingDotReceiveLotteryMsg.IsOn = SettingService.GetValue(SettingConstants.Live.HIDE_LOTTERY, false); m_liveRoomViewModel.ReceiveLotteryMsg = !LiveSettingDotReceiveLotteryMsg.IsOn; - LiveSettingDotReceiveWelcomeMsg.Toggled += new RoutedEventHandler((e, args) => + LiveSettingDotReceiveLotteryMsg.Toggled += new RoutedEventHandler((e, args) => { m_liveRoomViewModel.ReceiveLotteryMsg = !LiveSettingDotReceiveLotteryMsg.IsOn; SettingService.SetValue(SettingConstants.Live.HIDE_LOTTERY, LiveSettingDotReceiveLotteryMsg.IsOn); }); + //屏蔽抽奖弹幕关键字 + LiveSettingDotShowLotteryDanmu.IsOn = SettingService.GetValue(SettingConstants.Live.HIDE_LOTTERY_DANMU, false); + m_liveRoomViewModel.ShowLotteryDanmu = !LiveSettingDotShowLotteryDanmu.IsOn; + LiveSettingDotShowLotteryDanmu.Toggled += new RoutedEventHandler((e, args) => + { + m_liveRoomViewModel.ShowLotteryDanmu = !LiveSettingDotShowLotteryDanmu.IsOn; + SettingService.SetValue(SettingConstants.Live.HIDE_LOTTERY_DANMU, LiveSettingDotShowLotteryDanmu.IsOn); + }); + // 显示底部礼物栏 m_viewModel.ShowBottomGiftBar = SettingService.GetValue(SettingConstants.Live.SHOW_BOTTOM_GIFT_BAR, SettingConstants.Live.DEFAULT_SHOW_BOTTOM_GIFT_BAR); @@ -1017,41 +1048,39 @@ private void ListView_ItemClick(object sender, ItemClickEventArgs e) }); } - private async void BtnSendLotteryDanmu_Click(object sender, RoutedEventArgs e) + private async void BtnSendAnchorLotteryDanmu_Click(object sender, RoutedEventArgs e) { - if (m_liveRoomViewModel.LotteryViewModel != null && - m_liveRoomViewModel.LotteryViewModel.AnchorLotteryInfo != null && - !string.IsNullOrEmpty(m_liveRoomViewModel.LotteryViewModel.AnchorLotteryInfo.Danmu)) + if (!await m_liveRoomViewModel.JoinAnchorLottery()) return; + FlyoutLottery.Hide(); + + var msg = ""; + msg += "弹幕发送成功"; + + if(m_liveRoomViewModel.LotteryViewModel.AnchorLotteryInfo.RequireText.Contains("关注主播") && !m_liveRoomViewModel.Attention) { - var result = await m_liveRoomViewModel.SendDanmu(m_liveRoomViewModel.LotteryViewModel.AnchorLotteryInfo.Danmu); - if (result) - { - Notify.ShowMessageToast("弹幕发送成功"); - FlyoutLottery.Hide(); - } + // 参与天选会自动关注, 无须手动关注 + m_liveRoomViewModel.Attention = true; + msg += ", 关注主播成功"; } + + Notify.ShowMessageToast(msg); } private async void BtnSendRedPocketLotteryDanmu_Click(object sender, RoutedEventArgs e) { - if (m_liveRoomViewModel.LotteryViewModel == null || - m_liveRoomViewModel.LotteryViewModel.RedPocketLotteryInfo == null || - string.IsNullOrEmpty(m_liveRoomViewModel.LotteryViewModel.RedPocketLotteryInfo.Danmu)) return; - var msg = ""; - var result = await m_liveRoomViewModel.SendDanmu(m_liveRoomViewModel.LotteryViewModel.RedPocketLotteryInfo.Danmu); - if (result) - { - FlyoutRedPocketLottery.Hide(); - msg += "弹幕发送成功"; - } + if (!await m_liveRoomViewModel.JoinRedPocketLottery()) return; + FlyoutRedPocketLottery.Hide(); + var msg = ""; + msg += "弹幕发送成功"; if (!m_liveRoomViewModel.Attention) { - BtnAttention_Click(sender, e); + // 参与红包会自动关注, 无须手动关注 + m_liveRoomViewModel.Attention = true; msg += ", 关注主播成功"; } - Notify.ShowMessageToast(msg, 4); + Notify.ShowMessageToast(msg); } private void BottomBtnMiniWindows_Click(object sender, RoutedEventArgs e) @@ -1108,28 +1137,37 @@ private async void MiniWidnows(bool mini) MessageCenter.SetMiniWindow(mini); } - private void btnRemoveWords_Click(object sender, RoutedEventArgs e) + private void btnRemoveShieldWord_Click(object sender, RoutedEventArgs e) { var word = (sender as HyperlinkButton).DataContext as string; - settingVM.LiveWords.Remove(word); - SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveWords); - + DelShieldWord(word); } - private void DanmuSettingAddWord_Click(object sender, RoutedEventArgs e) + private void btnAddShieldWord_Click(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(DanmuSettingTxtWord.Text)) { Notify.ShowMessageToast("关键字不能为空"); return; } - if (!settingVM.LiveWords.Contains(DanmuSettingTxtWord.Text)) + AddShieldWord(DanmuSettingTxtWord.Text); + + DanmuSettingTxtWord.Text = ""; + } + + private void AddShieldWord(string word) + { + if (!settingVM.LiveShieldWords.Contains(word)) { - settingVM.LiveWords.Add(DanmuSettingTxtWord.Text); - SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveWords); + settingVM.LiveShieldWords.Add(word); + SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveShieldWords); } + } - DanmuSettingTxtWord.Text = ""; + private void DelShieldWord(string word) + { + settingVM.LiveShieldWords.Remove(word); + SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveShieldWords); } #region 播放器手势 @@ -1377,5 +1415,22 @@ private void LowDelaySwitch_Toggled(object sender, RoutedEventArgs e) } // 这里可以做个重启播放器的功能...就不需要用户手动重启了 } + + private void ChatList_PointerEntered(object sender, PointerRoutedEventArgs e) => isPointerInChatList = true; + + private void ChatList_PointerExited(object sender, PointerRoutedEventArgs e) + { + isPointerInChatList = false; + chatScrollTimer.Stop(); + chatScrollTimer.Start(); + } + + private void RootGrid_PointerEntered(object sender, PointerRoutedEventArgs e) + { + isPointerInThisPage = true; + ChatScrollToBottom(); + } + + private void RootGrid_PointerExited(object sender, PointerRoutedEventArgs e) => isPointerInThisPage = false; } } diff --git a/src/BiliLite.UWP/Pages/SettingPage.xaml.cs b/src/BiliLite.UWP/Pages/SettingPage.xaml.cs index 284395eb..dbe6a027 100644 --- a/src/BiliLite.UWP/Pages/SettingPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/SettingPage.xaml.cs @@ -749,7 +749,7 @@ private void LoadLiveDanmu() SettingService.SetValue(SettingConstants.Live.SHOW, LiveDanmuSettingState.IsOn ? Visibility.Visible : Visibility.Collapsed); }); //弹幕关键词 - LiveDanmuSettingListWords.ItemsSource = settingVM.LiveWords; + LiveDanmuSettingListWords.ItemsSource = settingVM.LiveShieldWords; } private void LoadDownlaod() { @@ -1100,21 +1100,21 @@ private void LiveDanmuSettingAddWord_Click(object sender, RoutedEventArgs e) Notify.ShowMessageToast("关键字不能为空"); return; } - if (!settingVM.LiveWords.Contains(LiveDanmuSettingTxtWord.Text)) + if (!settingVM.LiveShieldWords.Contains(LiveDanmuSettingTxtWord.Text)) { - settingVM.LiveWords.Add(LiveDanmuSettingTxtWord.Text); - SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveWords); + settingVM.LiveShieldWords.Add(LiveDanmuSettingTxtWord.Text); + SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveShieldWords); } DanmuSettingTxtWord.Text = ""; - SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveWords); + SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveShieldWords); } private void RemoveLiveDanmuWord_Click(object sender, RoutedEventArgs e) { var word = (sender as AppBarButton).DataContext as string; - settingVM.LiveWords.Remove(word); - SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveWords); + settingVM.LiveShieldWords.Remove(word); + SettingService.SetValue(SettingConstants.Live.SHIELD_WORD, settingVM.LiveShieldWords); } private async void btnCleanImageCache_Click(object sender, RoutedEventArgs e) diff --git a/src/BiliLite.UWP/Pages/UserInfoPage.xaml b/src/BiliLite.UWP/Pages/UserInfoPage.xaml index bd6548a2..7be3eb39 100644 --- a/src/BiliLite.UWP/Pages/UserInfoPage.xaml +++ b/src/BiliLite.UWP/Pages/UserInfoPage.xaml @@ -191,8 +191,8 @@ - - + + diff --git a/src/BiliLite.UWP/Pages/UserInfoPage.xaml.cs b/src/BiliLite.UWP/Pages/UserInfoPage.xaml.cs index db30b459..df5d2e82 100644 --- a/src/BiliLite.UWP/Pages/UserInfoPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/UserInfoPage.xaml.cs @@ -65,6 +65,7 @@ public UserInfoPage() followVM = new UserFollowVM(false); m_userDynamicViewModel.OpenCommentEvent += UserDynamicViewModelOpenCommentEvent; splitView.PaneClosed += SplitView_PaneClosed; + m_viewModel.LiveStreaming += (_, e) => btnLiveRoom.Label = "正在直播"; } private void SplitView_PaneClosed(SplitView sender, object args) { @@ -193,7 +194,7 @@ private async void SubmitVideo_ItemClick(object sender, ItemClickEventArgs e) private void btnLiveRoom_Click(object sender, RoutedEventArgs e) { - if (m_viewModel.UserInfo == null) return; + if (!m_viewModel.HaveLiveRoom) return; MessageCenter.NavigateToPage(this, new NavigationInfo() { icon = Symbol.Video, diff --git a/src/BiliLite.UWP/Services/FrostMasterDanmakuController.cs b/src/BiliLite.UWP/Services/FrostMasterDanmakuController.cs index 6cd36fac..5339ac4d 100644 --- a/src/BiliLite.UWP/Services/FrostMasterDanmakuController.cs +++ b/src/BiliLite.UWP/Services/FrostMasterDanmakuController.cs @@ -150,6 +150,7 @@ public override void Load(IEnumerable danmakuList) public override void Add(BiliDanmakuItem danmakuItem, bool owner) { var realDanmakuItem = m_mapper.Map(danmakuItem); + if (owner) realDanmakuItem.HasBorder = true; m_danmakuMaster.AddRealtimeDanmaku(realDanmakuItem, false); } diff --git a/src/BiliLite.UWP/Services/NsDanmakuController.cs b/src/BiliLite.UWP/Services/NsDanmakuController.cs index ee490e5b..615210e2 100644 --- a/src/BiliLite.UWP/Services/NsDanmakuController.cs +++ b/src/BiliLite.UWP/Services/NsDanmakuController.cs @@ -149,7 +149,7 @@ public override void Add(BiliDanmakuItem danmakuItem,bool owner) public override void AddLiveDanmaku(string text, bool owner, Color color) { - m_danmakuControl.AddLiveDanmu(text, false, color); + m_danmakuControl.AddLiveDanmu(text, owner, color); } public override void Pause() diff --git a/src/BiliLite.UWP/ViewModels/Live/LiveRoomLotteryViewModel.cs b/src/BiliLite.UWP/ViewModels/Live/LiveRoomLotteryViewModel.cs index 641b00dd..d37b75fc 100644 --- a/src/BiliLite.UWP/ViewModels/Live/LiveRoomLotteryViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/Live/LiveRoomLotteryViewModel.cs @@ -62,6 +62,8 @@ public LiveRoomLotteryViewModel() public event EventHandler AnchorLotteryStart; + public event EventHandler AddLotteryShieldWord; + #endregion #region Private Methods @@ -156,15 +158,16 @@ public async Task LoadLotteryInfo(int roomId) AnchorLotteryStart?.Invoke(this, AnchorLotteryInfo); AnchorLotteryShow = true; AnchorLotteryTimer.Start(); + AddLotteryShieldWord?.Invoke(AnchorLotteryInfo, AnchorLotteryInfo.Danmu); } if (obj.data["popularity_red_pocket"].ToArray().Length > 0) { var data = JsonConvert.DeserializeObject>(obj.data["popularity_red_pocket"].ToString()); RedPocketLotteryInfo = data?[0] ?? throw new CustomizedErrorException("RedPocket lottery data is null"); - RedPocketLotteryShow = true; RedPocketLotteryTimer.Start(); + AddLotteryShieldWord?.Invoke(RedPocketLotteryInfo, RedPocketLotteryInfo.Danmu); } } catch (Exception ex) diff --git a/src/BiliLite.UWP/ViewModels/Live/LiveRoomViewModel.cs b/src/BiliLite.UWP/ViewModels/Live/LiveRoomViewModel.cs index 006d9c67..eefb556e 100644 --- a/src/BiliLite.UWP/ViewModels/Live/LiveRoomViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/Live/LiveRoomViewModel.cs @@ -59,9 +59,12 @@ public LiveRoomViewModel() m_timer = new Timer(1000); m_timerBox = new Timer(1000); TimerAutoHideGift = new Timer(1000); - m_timer.Elapsed += Timer_Elapsed; + m_timer.Elapsed += Timer_LiveTime_Elapsed; + m_timer.Elapsed += Timer_SuperChats_Elapsed; m_timerBox.Elapsed += Timer_box_Elapsed; TimerAutoHideGift.Elapsed += Timer_auto_hide_gift_Elapsed; + LotteryViewModel.AddLotteryShieldWord += (_, e) => AddLotteryShieldWord?.Invoke(_, e); + LotteryViewModel.AnchorLotteryStart += (_, e) => AnchorLotteryStart?.Invoke(_, e); m_messageHandleActionsMap = InitLiveMessageHandleActionMap(); LoadMoreGuardCommand = new RelayCommand(LoadMoreGuardList); @@ -101,11 +104,23 @@ public LiveRoomViewModel() [DoNotNotify] public int RoomID { get; set; } + /// + /// 主播uid + /// + [DoNotNotify] + public long AnchorUid { get; set; } + /// /// 房间标题 /// public string RoomTitle { get; set; } + /// + /// buvid3 用于防止风控 + /// + [DoNotNotify] + public string Buvid3 { get; set; } + [DoNotNotify] public ObservableCollection Messages { get; set; } @@ -124,6 +139,9 @@ public LiveRoomViewModel() [DoNotNotify] public bool ReceiveGiftMsg { get; set; } = true; + [DoNotNotify] + public bool ShowLotteryDanmu { get; set; } = true; + public bool ShowGiftMessage { get; set; } /// @@ -173,7 +191,7 @@ public LiveRoomViewModel() public LiveAnchorProfile Profile { get; set; } - public bool Liveing { get; set; } + public bool Live { get; set; } public string LiveTime { get; set; } @@ -207,6 +225,14 @@ public LiveRoomViewModel() public string ManualPlayUrl { get; set; } = ""; + [DoNotNotify] + public Dictionary LotteryDanmu = new Dictionary { { "AnchorLottery", "" }, {"RedPocketLottery", ""} }; + + /// + /// 有的特殊直播间没有一些娱乐内容. 例如央视新闻直播间. + /// + public bool IsSpecialLiveRoom = false; + #endregion #region Events @@ -217,14 +243,18 @@ public LiveRoomViewModel() public event EventHandler AddNewDanmu; - public event EventHandler ChatScrollToEnd; - public event EventHandler RedPocketLotteryEnd; - public event EventHandler AnchorLotteryStart; + public event EventHandler AnchorLotteryStart; public event EventHandler SetManualPlayUrl; + public event EventHandler AddLotteryShieldWord; + + public event EventHandler DelShieldWord; + + public event EventHandler SpecialLiveRoomHideElements; + #endregion #region Private Methods @@ -244,6 +274,14 @@ private LiveMessageHandleActionsMap InitLiveMessageHandleActionMap() { RedPocketLotteryEnd?.Invoke(this, e); }; + actionMap.AddShieldWord += (_, e) => + { + AddLotteryShieldWord?.Invoke(this, e); + }; + actionMap.DelShieldWord += (_, e) => + { + DelShieldWord?.Invoke(this, e); + }; return actionMap; } @@ -270,7 +308,6 @@ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatch else { HideGiftFlag++; - ChatScrollToEnd?.Invoke(null, null); } }); } @@ -291,15 +328,15 @@ private async void MessageCenter_LoginedEvent(object sender, object e) //await GetFreeSilverTime(); } - private async void Timer_Elapsed(object sender, ElapsedEventArgs e) + private async void Timer_LiveTime_Elapsed(object sender, ElapsedEventArgs e) { try { - if (LiveInfo == null && !Liveing) + if (!Live) { await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { - LiveTime = ""; + LiveTime = "未开播"; }); return; } @@ -308,20 +345,26 @@ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatch await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { - for (var i = 0; i < SuperChats.Count; i++) + LiveTime = ts.ToString(@"hh\:mm\:ss"); + }); + } + catch (Exception ex) + { + _logger.Warn(ex.Message, ex); + } + } + + private async void Timer_SuperChats_Elapsed(object sender, ElapsedEventArgs e) + { + try + { + await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + { + for(int i = 0; i < SuperChats.Count; i++) { - var item = SuperChats[i]; - if (item.Time <= 0) - { - SuperChats.Remove(item); - } - else - { - item.Time -= 1; - } + if (SuperChats.ElementAt(i).Time <= 0) SuperChats.RemoveAt(i); + else SuperChats.ElementAt(i).Time -= 1; } - - LiveTime = ts.ToString(@"hh\:mm\:ss"); }); } catch (Exception ex) @@ -341,16 +384,12 @@ private async void ReceiveMessage(int roomId) } m_liveMessage ??= new LiveMessage(); - var buvidResults = await m_liveRoomApi.GetBuvid().Request(); - var buvidData = await buvidResults.GetJson>(); - var buvid = buvidData.data.B3; - var danmuResults = await m_liveRoomApi.GetDanmuInfo(roomId).Request(); var danmuData = await danmuResults.GetJson>(); var token = danmuData.data.Token; var host = danmuData.data.HostList[0].Host; - await m_liveMessage.Connect(roomId, uid, token, buvid, host, m_cancelSource.Token); + await m_liveMessage.Connect(roomId, uid, token, Buvid3, host, m_cancelSource.Token); } catch (TaskCanceledException) { @@ -465,6 +504,81 @@ private List GetFlvPlayUrls(LiveRoomPlayUrlModel liveRoomPlayUr return GetSpecialPlayUrls(liveRoomPlayUrlModel, "http_stream"); } + private async Task JoinRedPocketLotteryRequest(long uid, int room_id, long ruid, int lot_id) + { + if (!Logined && !await Notify.ShowLoginDialog()) + { + Notify.ShowMessageToast("请先登录"); + return false; + } + try + { + var result = await m_liveRoomApi.JoinRedPocketLottery(uid, room_id, ruid, lot_id).Request(); + if (!result.status) + { + throw new CustomizedErrorException(result.message); + } + + var data = await result.GetData(); + if (!data.success) + { + throw new CustomizedErrorException(data.message); + } + + if (data.data?["join_status"].ToString().Length > 0 && data.data?["join_status"].ToInt32() == 1) return true; + else throw new CustomizedErrorException("未能成功加入红包抽奖"); + } + catch (CustomizedErrorException ex) + { + Notify.ShowMessageToast(ex.Message); + _logger.Error(ex.Message, ex); + return false; + } + catch (Exception ex) + { + _logger.Log("参加红包抽奖出现错误", LogType.Error, ex); + Notify.ShowMessageToast("参加红包抽奖出现错误"); + return false; + } + } + + private async Task JoinAnchorLotteryRequest(int lottery_id, int gift_id, int gift_num) + { + if (!Logined && !await Notify.ShowLoginDialog()) + { + Notify.ShowMessageToast("请先登录"); + return false; + } + try + { + var result = await m_liveRoomApi.JoinAnchorLottery(RoomID, lottery_id, Buvid3, gift_id, gift_num).Request(); + if (!result.status) + { + throw new CustomizedErrorException(result.message); + } + + var data = await result.GetData(); + if (!data.success) + { + throw new CustomizedErrorException(data.message); + } + + return true; + } + catch (CustomizedErrorException ex) + { + Notify.ShowMessageToast(ex.Message); + _logger.Error(ex.Message, ex); + return false; + } + catch (Exception ex) + { + _logger.Log("参加天选抽奖出现错误", LogType.Error, ex); + Notify.ShowMessageToast("参加天选抽奖出现错误"); + return false; + } + } + #endregion #region Public Methods @@ -568,37 +682,43 @@ public async Task LoadLiveRoomDetail(string id) throw new CustomizedErrorException("加载直播间失败:" + data.message); } + if (data.data.GuardInfo == null) + { + IsSpecialLiveRoom = true; + SpecialLiveRoomHideElements?.Invoke(this, null); + }; + RoomID = data.data.RoomInfo.RoomId; RoomTitle = data.data.RoomInfo.Title; - Liveing = data.data.RoomInfo.LiveStatus == 1; - GuardNum = data.data.GuardInfo.Count; + Live = data.data.RoomInfo.LiveStatus == 1; + GuardNum = !IsSpecialLiveRoom ? data.data.GuardInfo.Count : 0; + AnchorUid = data.data.RoomInfo.Uid; LiveInfo = data.data; if (Ranks == null) { Ranks = new List() { - new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "高能用户贡献榜", "contribution-rank"), - new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "粉丝榜", "fans"), + new LiveRoomRankViewModel(RoomID, AnchorUid, "高能用户贡献榜", "contribution-rank"), + new LiveRoomRankViewModel(RoomID, AnchorUid, "粉丝榜", "fans"), }; SelectRank = Ranks[0]; } await LoadAnchorProfile(); - if (Liveing) + m_timer.Start(); + if (Live) { - m_timer.Start(); - await GetPlayUrls(RoomID, - SettingService.GetValue(SettingConstants.Live.DEFAULT_QUALITY, 10000)); - //GetFreeSilverTime(); - await LoadSuperChat(); - if (ReceiveLotteryMsg) - { - // 抽奖 - LotteryViewModel.LoadLotteryInfo(RoomID).RunWithoutAwait(); - RedPocketSendDanmuBtnText = Attention ? "一键发送弹幕" : "一键关注并发送弹幕"; - } + await GetPlayUrls(RoomID, SettingService.GetValue(SettingConstants.Live.DEFAULT_QUALITY, 10000)); + } + //GetFreeSilverTime(); + await LoadSuperChat(); + if (ReceiveLotteryMsg) + { + // 天选抽奖和红包抽奖 + LotteryViewModel.LoadLotteryInfo(RoomID).RunWithoutAwait(); + RedPocketSendDanmuBtnText = Attention ? "一键发送弹幕" : "一键关注并发送弹幕"; } await GetRoomGiftList(); @@ -609,6 +729,10 @@ await GetPlayUrls(RoomID, await GetTitles(); } + var buvidResults = await m_liveRoomApi.GetBuvid().Request(); + var buvidData = await buvidResults.GetJson>(); + Buvid3 = buvidData.data.B3; + EntryRoom(); ReceiveMessage(data.data.RoomInfo.RoomId); } @@ -668,7 +792,9 @@ public async Task LoadSuperChat() Price = item.Price, StartTime = item.StartTime, Time = item.Time, - Username = item.UserInfo.Uname + Username = item.UserInfo.Uname, + GuardLevel = item.UserInfo.GuardLevel, + Uid = item.Uid }); } } @@ -880,6 +1006,7 @@ public async Task GetRoomGiftList() /// public async Task GetGuardList() { + if (IsSpecialLiveRoom) return; try { LoadingGuard = true; @@ -888,8 +1015,7 @@ public async Task GetGuardList() if (!result.status) return; var data = await result.GetData(); if (!data.success) return; - var guardNum = data.data["info"]["num"].ToInt32(); - GuardNum = guardNum; // 更新显示数字 + GuardNum = data.data["info"]["num"].ToInt32(); //更新舰长数 var top3 = JsonConvert.DeserializeObject>(data.data["top3"].ToString()); if (Guards.Count == 0 && top3 != null && top3.Count != 0 && Guards.Count < GuardNum) @@ -1084,7 +1210,7 @@ public async Task SendBagGift(LiveGiftItem liveGiftItem) public async Task SendDanmu(string text) { - if (!SettingService.Account.Logined && !await Notify.ShowLoginDialog()) + if (!Logined && !await Notify.ShowLoginDialog()) { Notify.ShowMessageToast("请先登录"); return false; @@ -1119,8 +1245,82 @@ public async Task SendDanmu(string text) } } + public async Task JoinAnchorLottery() + { + try + { + if (!Logined) + { + throw new CustomizedErrorException("未登录"); + } + + if (LotteryViewModel == null || + LotteryViewModel.AnchorLotteryInfo == null || + string.IsNullOrEmpty(LotteryViewModel.AnchorLotteryInfo.Danmu)) + { + return false; + } + + return await JoinAnchorLotteryRequest(LotteryViewModel.AnchorLotteryInfo.Id, LotteryViewModel.AnchorLotteryInfo.GiftId, LotteryViewModel.AnchorLotteryInfo.GiftNum); + } + catch (CustomizedErrorException ex) + { + Notify.ShowMessageToast(ex.Message); + _logger.Error(ex.Message, ex); + return false; + } + catch (Exception ex) + { + _logger.Log("参与天选抽奖出现错误", LogType.Error, ex); + Notify.ShowMessageToast("参与天选抽奖出现错误"); + return false; + } + + } + + public async Task JoinRedPocketLottery() + { + try + { + if (!Logined) + { + throw new CustomizedErrorException("未登录"); + } + + if (LotteryViewModel == null || + LotteryViewModel.RedPocketLotteryInfo == null || + string.IsNullOrEmpty(LotteryViewModel.RedPocketLotteryInfo.Danmu)) + { + return false; + } + // 参与红包抽奖会自动发送弹幕, 不用自己发 + return await JoinRedPocketLotteryRequest(SettingService.Account.UserID, + RoomID, + AnchorUid, + LotteryViewModel.RedPocketLotteryInfo.LotteryId.ToInt32()); + } + catch (CustomizedErrorException ex) + { + Notify.ShowMessageToast(ex.Message); + _logger.Error(ex.Message, ex); + return false; + } + catch (Exception ex) + { + _logger.Log("参与红包抽奖出现错误", LogType.Error, ex); + Notify.ShowMessageToast("参与红包抽奖出现错误"); + return false; + } + + } + public void Dispose() { + foreach (var item in LotteryDanmu) + { + DelShieldWord?.Invoke(this, item.Value); + } + m_cancelSource?.Cancel(); m_liveMessage?.Dispose(); diff --git a/src/BiliLite.UWP/ViewModels/Live/SuperChatMsgViewModel.cs b/src/BiliLite.UWP/ViewModels/Live/SuperChatMsgViewModel.cs index 7b7167f9..376d5a85 100644 --- a/src/BiliLite.UWP/ViewModels/Live/SuperChatMsgViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/Live/SuperChatMsgViewModel.cs @@ -1,4 +1,5 @@ -using BiliLite.ViewModels.Common; +using BiliLite.Models.Common; +using BiliLite.ViewModels.Common; using Newtonsoft.Json; using PropertyChanged; @@ -58,5 +59,25 @@ public class SuperChatMsgViewModel : BaseViewModel [DoNotNotify] [JsonProperty("font_color")] public string FontColor { get; set; } + + [DoNotNotify] + [JsonProperty("guard_level")] + public UserCaptainType GuardLevel { get; set; } + + [DoNotNotify] + [JsonProperty("uid")] + public long Uid { get; set; } + + [DoNotNotify] + [JsonProperty("medal_name")] + public string MedalName { get; set; } + + [DoNotNotify] + [JsonProperty("medal_level")] + public int MedalLevel { get; set; } + + [DoNotNotify] + [JsonProperty("medal_color")] + public string MedalColor { get; set; } } } \ No newline at end of file diff --git a/src/BiliLite.UWP/ViewModels/User/UserDetailViewModel.cs b/src/BiliLite.UWP/ViewModels/User/UserDetailViewModel.cs index 57c0a89c..8e9fe750 100644 --- a/src/BiliLite.UWP/ViewModels/User/UserDetailViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/User/UserDetailViewModel.cs @@ -49,6 +49,10 @@ public UserDetailViewModel(IMapper mapper) public UserCenterInfoViewModel UserInfo { get; set; } + public bool HaveLiveRoom => UserInfo != null && UserInfo.LiveRoom != null; + + public EventHandler LiveStreaming; + #endregion #region Private Methods @@ -56,14 +60,17 @@ public UserDetailViewModel(IMapper mapper) private async Task GetUserInfoCore() { var api = await m_userDetailApi.UserInfo(Mid); - var result = await api.Request(); + var result = await api.Request(); if (!result.status) throw new CustomizedErrorException(result.message); var data = await result.GetData(); if (!data.success) throw new CustomizedErrorException(data.message); + data.data.Stat = await GetSpaceStat(); UserInfo = m_mapper.Map(data.data); + + if (HaveLiveRoom && UserInfo.LiveRoom.LiveStatus == 1) LiveStreaming?.Invoke(this, null); } private async Task GetStatCore()