From 6ced93d8762d6a884c9266f5c9348ac3b94c05b1 Mon Sep 17 00:00:00 2001 From: GD-Slime <82302542+GD-Slime@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:06:51 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81=E7=99=BB=E5=BD=95=E4=B8=8B=E6=97=A0=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E7=9A=84=E9=97=AE=E9=A2=98=20(#420)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复二维码登录下无播放列表的问题 --- .../Common/Video/VideoUgcSeasonSectionEpisode.cs | 14 ++++++++++++-- src/BiliLite.UWP/Pages/VideoDetailPage.xaml | 9 +++------ .../ViewModels/Video/VideoDetailPageViewModel.cs | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/BiliLite.UWP/Models/Common/Video/VideoUgcSeasonSectionEpisode.cs b/src/BiliLite.UWP/Models/Common/Video/VideoUgcSeasonSectionEpisode.cs index f8d128d44..265ccb946 100644 --- a/src/BiliLite.UWP/Models/Common/Video/VideoUgcSeasonSectionEpisode.cs +++ b/src/BiliLite.UWP/Models/Common/Video/VideoUgcSeasonSectionEpisode.cs @@ -1,9 +1,12 @@ -using Newtonsoft.Json; +using BiliLite.Models.Common.Video.Detail; +using Newtonsoft.Json; namespace BiliLite.Models.Common.Video { public class VideoUgcSeasonSectionEpisode { + private string m_cover; + public long Id { get; set; } public string Aid { get; set; } @@ -12,7 +15,11 @@ public class VideoUgcSeasonSectionEpisode public string Title { get; set; } - public string Cover { get; set; } + public string Cover + { + get => m_cover ?? Arc.Pic; + set => m_cover = value; + } [JsonProperty("cover_right_text")] public string CovverRightText { get; set; } @@ -34,5 +41,8 @@ public class VideoUgcSeasonSectionEpisode [JsonProperty("first_frame")] public string FirstFrame { get; set; } + + [JsonProperty("arc")] + public VideoDetailModel Arc { get; set; } // 简单复用一下 } } diff --git a/src/BiliLite.UWP/Pages/VideoDetailPage.xaml b/src/BiliLite.UWP/Pages/VideoDetailPage.xaml index 4b5089276..f54b6d3d0 100644 --- a/src/BiliLite.UWP/Pages/VideoDetailPage.xaml +++ b/src/BiliLite.UWP/Pages/VideoDetailPage.xaml @@ -38,11 +38,9 @@ - + - - - + @@ -53,8 +51,7 @@ diff --git a/src/BiliLite.UWP/ViewModels/Video/VideoDetailPageViewModel.cs b/src/BiliLite.UWP/ViewModels/Video/VideoDetailPageViewModel.cs index 65bc9414a..46963aa99 100644 --- a/src/BiliLite.UWP/ViewModels/Video/VideoDetailPageViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/Video/VideoDetailPageViewModel.cs @@ -224,6 +224,7 @@ public async Task LoadVideoDetail(string id, bool isbvid = false) var data = await results.GetJson>(); + // 通过web获取, 作为后备使用 if (!data.success) { // 通过web获取视频详情 @@ -252,6 +253,21 @@ public async Task LoadVideoDetail(string id, bool isbvid = false) throw new CustomizedErrorException(data.message); } + var webResults = await videoAPI.DetailWebInterface(id, isbvid).Request(); + if (!webResults.status) + { + throw new CustomizedErrorException(webResults.message); + } + var webData = await webResults.GetJson>(); + if (!webData.success) + { + throw new CustomizedErrorException(webData.message); + } + if (data.data.UgcSeason == null && webData.data.UgcSeason != null) + { + data.data.UgcSeason = webData.data.UgcSeason; + } + var videoInfoViewModel = m_mapper.Map(data.data); VideoInfo = videoInfoViewModel; if (needGetUserReq) From ed655be4213414e1bfd302831cf5f9e5615b5377 Mon Sep 17 00:00:00 2001 From: GD-Slime <82302542+GD-Slime@users.noreply.github.com> Date: Sat, 2 Dec 2023 18:20:18 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=94=B9=EF=BC=8C=E6=96=B0=E5=A2=9E=E7=9B=B4=E6=92=AD=E9=97=B4?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=86=85=E5=AE=B9=20(#410)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复: - 修复仅有黄豆表情时错位 - 修复送礼物功能 - 修复右侧栏收到礼物时右侧栏停止自动滚动的问题 - 修复进场通知 - 修复人气值(改为新的“看过”) - 修复在加载舰长列表时的无限加载最后几位的问题 - 修复了同一时间接收弹幕量过高时视频上的弹幕堆积的问题 - 修复榜单(更改为现在的高能用户贡献榜单) - 修正一些typo 更改: - 互动区自动清理现在为队列式(queue) - 丰富了用户进入舰队时的显示内容 - 更改了右侧栏内容的字重与颜色 - 更改了大航海用户的名字颜色 - 用户名现在可以被选中和复制 - 更改了天选抽奖的布局并使体验更好 - 为大航海下的列表加入了头像显示 新增: - 新增用户被直播间禁言提示 - 新增直播间被警告提示 - 新增直播间被切断提示 - ToRichText方法现在可以更改对象的颜色和字重(有默认值) - 新增直播间开始直播提示,并重新会加载播放Url - 新增人气红包功能 --- src/BiliLite.UWP/Assets/Live/RedPocket.png | Bin 0 -> 21902 bytes src/BiliLite.UWP/BiliLite.UWP.csproj | 8 +- .../Extensions/StringExtensions.cs | 16 +- src/BiliLite.UWP/Models/Common/Enumerates.cs | 49 +++-- .../Models/Common/Live/DanmuMsgModel.cs | 20 +- .../Models/Common/Live/InteractWordModel.cs | 21 ++ .../Live/LiveMessageHandleActionsMap.cs | 152 +++++++++---- .../Live/LiveRoomAnchorLotteryInfoModel.cs | 52 +++++ .../Live/LiveRoomEndAnchorLotteryInfoModel.cs | 57 ++++- .../LiveRoomEndRedPocketLotteryInfoModel.cs | 117 ++++++++++ .../Common/Live/LiveRoomRankItemModel.cs | 15 +- .../Live/LiveRoomRedPocketLotteryInfoModel.cs | 193 +++++++++++++++++ .../Models/Common/Live/RoomBlockMsgModel.cs | 9 + .../Common/Live/WarningOrCutOffMsgModel.cs | 9 + .../Models/Common/Live/WelcomeMsgModel.cs | 11 - .../Models/Requests/Api/Live/LiveRoomAPI.cs | 34 +-- src/BiliLite.UWP/Modules/Live/LiveMessage.cs | 152 +++++++++---- src/BiliLite.UWP/Pages/LiveDetailPage.xaml | 201 ++++++++++++++++-- src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs | 102 +++++++-- .../Live/LiveRoomAnchorLotteryViewModel.cs | 116 ---------- .../Live/LiveRoomLotteryViewModel.cs | 191 +++++++++++++++++ .../ViewModels/Live/LiveRoomRankViewModel.cs | 32 +-- .../ViewModels/Live/LiveRoomViewModel.cs | 98 +++++---- 23 files changed, 1317 insertions(+), 338 deletions(-) create mode 100644 src/BiliLite.UWP/Assets/Live/RedPocket.png create mode 100644 src/BiliLite.UWP/Models/Common/Live/InteractWordModel.cs create mode 100644 src/BiliLite.UWP/Models/Common/Live/LiveRoomEndRedPocketLotteryInfoModel.cs create mode 100644 src/BiliLite.UWP/Models/Common/Live/LiveRoomRedPocketLotteryInfoModel.cs create mode 100644 src/BiliLite.UWP/Models/Common/Live/RoomBlockMsgModel.cs create mode 100644 src/BiliLite.UWP/Models/Common/Live/WarningOrCutOffMsgModel.cs delete mode 100644 src/BiliLite.UWP/Models/Common/Live/WelcomeMsgModel.cs delete mode 100644 src/BiliLite.UWP/ViewModels/Live/LiveRoomAnchorLotteryViewModel.cs create mode 100644 src/BiliLite.UWP/ViewModels/Live/LiveRoomLotteryViewModel.cs diff --git a/src/BiliLite.UWP/Assets/Live/RedPocket.png b/src/BiliLite.UWP/Assets/Live/RedPocket.png new file mode 100644 index 0000000000000000000000000000000000000000..f698eb6bedd08c6d1aaa212a1db947d8c635d0a1 GIT binary patch literal 21902 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 literal 0 HcmV?d00001 diff --git a/src/BiliLite.UWP/BiliLite.UWP.csproj b/src/BiliLite.UWP/BiliLite.UWP.csproj index 90f5d19d8..d3c850557 100644 --- a/src/BiliLite.UWP/BiliLite.UWP.csproj +++ b/src/BiliLite.UWP/BiliLite.UWP.csproj @@ -137,7 +137,12 @@ + + + + + @@ -218,13 +223,12 @@ + - - diff --git a/src/BiliLite.UWP/Extensions/StringExtensions.cs b/src/BiliLite.UWP/Extensions/StringExtensions.cs index 42ca7152d..c16da0ede 100644 --- a/src/BiliLite.UWP/Extensions/StringExtensions.cs +++ b/src/BiliLite.UWP/Extensions/StringExtensions.cs @@ -79,7 +79,7 @@ public static string TraditionalToSimplified(this string input) /// /// /// - public static RichTextBlock ToRichTextBlock(this string txt, JObject emote, bool isLive = false) + public static RichTextBlock ToRichTextBlock(this string txt, JObject emote, bool isLive = false, string color = null, string fontWeight = "Normal") { var input = txt; try @@ -105,11 +105,14 @@ public static RichTextBlock ToRichTextBlock(this string txt, JObject emote, bool if (!isLive) { input = HandelVideoID(input); } //生成xaml - var xaml = string.Format(@" + xmlns:mc = ""http://schemas.openxmlformats.org/markup-compatibility/2006"" LineHeight=""{1}"" {2} {3}> {0} - ", input, isLive ? 22 : 20); + ", input, + isLive ? 22 : 20, + color == null ? "" : $"Foreground=\"{color}\"", + $"FontWeight=\"{fontWeight}\""); var p = (RichTextBlock)XamlReader.Load(xaml); return p; } @@ -340,6 +343,9 @@ private static string HandelEmoji(string input, JObject emote) return input; } + /// + /// 处理直播黄豆表情 + /// private static string HandleLiveEmoji(string input, JObject emotes) { if (emotes == null) return input; @@ -349,7 +355,7 @@ private static string HandleLiveEmoji(string input, JObject emotes) if (!emotes.TryGetValue(emojiCode, out var emote)) continue; var replacement = string.Format( - @"", + @"", emote["url"], emote["width"], emote["height"]); input = input.Replace(emojiCode, replacement); diff --git a/src/BiliLite.UWP/Models/Common/Enumerates.cs b/src/BiliLite.UWP/Models/Common/Enumerates.cs index 024c476b9..1a5abb626 100644 --- a/src/BiliLite.UWP/Models/Common/Enumerates.cs +++ b/src/BiliLite.UWP/Models/Common/Enumerates.cs @@ -306,10 +306,10 @@ public enum MessageType /// ConnectSuccess, - /// - /// 在线人数 - /// - Online, + ///// + ///// 在线人数(即人气值, 已弃用) + ///// + //Online, /// /// 弹幕 @@ -324,12 +324,12 @@ public enum MessageType /// /// 欢迎信息 /// - Welcome, + InteractWord, /// - /// 系统消息 + /// 系统消息 (未做实现), 先注释 /// - SystemMsg, + //SystemMsg, /// /// 醒目留言 @@ -346,12 +346,7 @@ public enum MessageType /// AnchorLotteryStart, - /// - /// 抽奖结束 - /// - AnchorLotteryEnd, - /// /// 抽奖结果 /// AnchorLotteryAward, @@ -370,5 +365,35 @@ public enum MessageType /// 房间信息更新 /// RoomChange, + + /// + /// 指定观众禁言 + /// + RoomBlock, + + /// + /// 超管警告或切断 + /// + WaringOrCutOff, + + /// + /// 开始直播 + /// + StartLive, + + /// + /// 看过直播的人数变化(代替人气值) + /// + WatchedChange, + + /// + /// 红包抽奖开始 + /// + RedPocketLotteryStart, + + /// + /// 红包抽奖赢家 + /// + RedPocketLotteryWinner, } } \ 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 36b6222e0..fa4ac5fa4 100644 --- a/src/BiliLite.UWP/Models/Common/Live/DanmuMsgModel.cs +++ b/src/BiliLite.UWP/Models/Common/Live/DanmuMsgModel.cs @@ -29,10 +29,15 @@ public class DanmuMsgModel public string UserName { get; set; } /// - /// 用户名颜色,默认灰色 + /// 用户名颜色, 默认灰色 /// public string UserNameColor { get; set; } = "#FF808080"; + /// + /// 用户名字重, 默认Normal + /// + public string UserNameFontWeight { get; set; } = "Normal"; + ///// ///// 等级 ///// @@ -81,12 +86,21 @@ public class DanmuMsgModel /// /// 用户上的舰的图片 /// - public string UserCaptainImage { get; set; } + public string UserCaptainImage + { + get => UserCaptain switch + { + "舰长" => "/Assets/Live/ic_live_guard_3.png", + "提督" => "/Assets/Live/ic_live_guard_2.png", + "总督" => "/Assets/Live/ic_live_guard_1.png", + _ => null, + }; + } /// /// 黄豆表情 /// - public JContainer Emoji { get; set; } + public JObject Emoji { get; set; } /// /// 各类大表情 diff --git a/src/BiliLite.UWP/Models/Common/Live/InteractWordModel.cs b/src/BiliLite.UWP/Models/Common/Live/InteractWordModel.cs new file mode 100644 index 000000000..5a46eba77 --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/Live/InteractWordModel.cs @@ -0,0 +1,21 @@ +using Windows.UI.Xaml; + +namespace BiliLite.Models.Common.Live +{ + public class InteractWordModel + { + public string UserName { get; set; } + + public string UserID { get; set; } + + public int MsgType { get; set; } + + public string MedalName { get; set; } + + public string MedalLevel { get; set; } + + public string MedalColor { get; set; } + + public Visibility ShowMedal { get; set; } = Visibility.Collapsed; + } +} \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Common/Live/LiveMessageHandleActionsMap.cs b/src/BiliLite.UWP/Models/Common/Live/LiveMessageHandleActionsMap.cs index 4208c55a3..b765667dc 100644 --- a/src/BiliLite.UWP/Models/Common/Live/LiveMessageHandleActionsMap.cs +++ b/src/BiliLite.UWP/Models/Common/Live/LiveMessageHandleActionsMap.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using BiliLite.Modules.Live; using Windows.UI.Xaml; using BiliLite.ViewModels.Live; using Newtonsoft.Json; +using BiliLite.Extensions; +using BiliLite.Services; namespace BiliLite.Models.Common.Live { @@ -15,19 +16,23 @@ public LiveMessageHandleActionsMap() Map = new Dictionary> { { MessageType.ConnectSuccess, ConnectSuccess }, - { MessageType.Online, Online }, + //{ MessageType.Online, Online }, { MessageType.Danmu, Danmu }, { MessageType.Gift, Gift }, - { MessageType.Welcome, Welcome }, - { MessageType.WelcomeGuard, WelcomeGuard }, - { MessageType.SystemMsg, SystemMsg }, + { MessageType.InteractWord, InteractWord }, + //{ MessageType.SystemMsg, SystemMsg }, { MessageType.SuperChat, SuperChat }, { MessageType.SuperChatJpn, SuperChat }, { MessageType.AnchorLotteryStart, AnchorLotteryStart }, - { MessageType.AnchorLotteryEnd, AnchorLotteryEnd }, { MessageType.AnchorLotteryAward, AnchorLotteryAward }, { MessageType.GuardBuy, GuardBuy }, { MessageType.RoomChange, RoomChange }, + { MessageType.RoomBlock, RoomBlock }, + { MessageType.WaringOrCutOff, WaringOrCutOff }, + { MessageType.StartLive, StartLive }, + { MessageType.WatchedChange, WatchedChange }, + { MessageType.RedPocketLotteryStart, RedPocketLotteryStart}, + { MessageType.RedPocketLotteryWinner, RedPocketLotteryWinner}, }; } @@ -35,7 +40,11 @@ public LiveMessageHandleActionsMap() public event EventHandler AddNewDanmu; - public event EventHandler LotteryEnd; + public event EventHandler AnchorLotteryEnd; + + public event EventHandler RedPocketLotteryEnd; + + public event EventHandler AnchorInfoLiveInfo; private void ConnectSuccess(LiveRoomViewModel viewModel, object message) { @@ -45,18 +54,17 @@ private void ConnectSuccess(LiveRoomViewModel viewModel, object message) }); } - private void Online(LiveRoomViewModel viewModel, object message) + private void WatchedChange(LiveRoomViewModel viewModel, object message) { - viewModel.Online = (int)message; + viewModel.WatchedNum = (string)message; } private void Danmu(LiveRoomViewModel viewModel, object message) { var m = message as DanmuMsgModel; - m.ShowUserLevel = Visibility.Visible; if (viewModel.Messages.Count >= viewModel.CleanCount) { - viewModel.Messages.Clear(); + viewModel.Messages.RemoveAt(0); } viewModel.Messages.Add(m); AddNewDanmu?.Invoke(null, m); @@ -83,34 +91,45 @@ private void Gift(LiveRoomViewModel viewModel, object message) } } - private void Welcome(LiveRoomViewModel viewModel, object message) + private void InteractWord(LiveRoomViewModel viewModel, object message) { - var info = message as WelcomeMsgModel; + var info = message as InteractWordModel; if (!viewModel.ReceiveWelcomeMsg) return; - viewModel.Messages.Add(new DanmuMsgModel() + var msg = new DanmuMsgModel() { UserName = info.UserName, - UserNameColor = "#FFFF69B4",//Colors.HotPink - Text = " 进入直播间" - }); - } + // UserNameColor = "#FFFF69B4",//Colors.HotPink + RichText = info.MsgType == 1 ? "进入直播间".ToRichTextBlock(null, color: "Gray") : "关注了主播".ToRichTextBlock(null, color: "Gray") + }; - private void WelcomeGuard(LiveRoomViewModel viewModel, object message) - { - var info = message as WelcomeMsgModel; - if (!viewModel.ReceiveWelcomeMsg) return; - viewModel.Messages.Add(new DanmuMsgModel() + if (info.ShowMedal == Visibility.Visible) { - UserName = info.UserName, - UserNameColor = "#FFFF69B4",//Colors.HotPink - Text = " (舰长)进入直播间" - }); - } + msg.MedalColor = info.MedalColor; + msg.MedalName = info.MedalName; + msg.MedalLevel = info.MedalLevel; + msg.ShowMedal = info.ShowMedal; + } - private void SystemMsg(LiveRoomViewModel viewModel, object message) - { + viewModel.Messages.Add(msg); } + // 已被b站弃用 + //private void WelcomeGuard(LiveRoomViewModel viewModel, object message) + //{ + // var info = message as InteractWordModel; + // if (!viewModel.ReceiveWelcomeMsg) return; + // viewModel.Messages.Add(new DanmuMsgModel() + // { + // UserName = info.UserName, + // UserNameColor = "#FFFF69B4",//Colors.HotPink + // RichText = " (舰长)进入直播间".ToRichTextBlock(null) + // }); + //} + + //private void SystemMsg(LiveRoomViewModel viewModel, object message) + //{ + //} + private void SuperChat(LiveRoomViewModel viewModel, object message) { viewModel.SuperChats.Add(message as SuperChatMsgViewModel); @@ -120,29 +139,48 @@ private void AnchorLotteryStart(LiveRoomViewModel viewModel, object message) { if (!viewModel.ReceiveLotteryMsg) return; var info = message.ToString(); - viewModel.AnchorLotteryViewModel.SetLotteryInfo(JsonConvert.DeserializeObject(info)); + viewModel.LotteryViewModel.SetAnchorLotteryInfo(JsonConvert.DeserializeObject(info)); + } - private void AnchorLotteryEnd(LiveRoomViewModel viewModel, object message) + private void RedPocketLotteryStart(LiveRoomViewModel viewModel, object message) { + if (!viewModel.ReceiveLotteryMsg) return; + var info = message.ToString(); + viewModel.LotteryViewModel.SetRedPocketLotteryInfo(JsonConvert.DeserializeObject(info)); + + viewModel.ShowRedPocketLotteryWinnerList = false; + viewModel.RedPocketSendDanmuBtnText = viewModel.Attention ? "一键发送弹幕" : "一键关注并发送弹幕"; + } + + private void RedPocketLotteryWinner(LiveRoomViewModel viewModel, object message) + { + if (!viewModel.ReceiveLotteryMsg) return; + var info = JsonConvert.DeserializeObject(message.ToString()); + RedPocketLotteryEnd?.Invoke(this, info); } private void AnchorLotteryAward(LiveRoomViewModel viewModel, object message) { if (!viewModel.ReceiveLotteryMsg) return; var info = JsonConvert.DeserializeObject(message.ToString()); - LotteryEnd?.Invoke(this, info); + AnchorLotteryEnd?.Invoke(this, info); } private void GuardBuy(LiveRoomViewModel viewModel, object message) { var info = message as GuardBuyMsgModel; - viewModel.Messages.Add(new DanmuMsgModel() + var msg = new DanmuMsgModel { UserName = info.UserName, UserNameColor = "#FFFF69B4",//Colors.HotPink - Text = $"成为了{info.GiftName}" - }); + RichText = $"成为了主播的{info.GiftName}🎉".ToRichTextBlock(null, fontWeight: "Medium"), + UserCaptain = info.GiftName, + ShowCaptain = Visibility.Visible, + UserNameFontWeight = "SemiBold", // 字重调大, 防止与进场弹幕混淆 + }; + + viewModel.Messages.Add(msg); // 刷新舰队列表 _ = viewModel.GetGuardList(); } @@ -152,5 +190,47 @@ private void RoomChange(LiveRoomViewModel viewModel, object message) var info = message as RoomChangeMsgModel; viewModel.RoomTitle = info.Title; } + + private void RoomBlock(LiveRoomViewModel viewModel, object message) + { + var info = message as RoomBlockMsgModel; + var msg = new DanmuMsgModel() + { + UserName = info.UserName, + RichText = "被直播间禁言🚫".ToRichTextBlock(null, fontWeight: "Medium"), // 字重调大, 防止与进场弹幕混淆) + UserNameFontWeight = "SemiBold", + }; + + viewModel.Messages.Add(msg); + } + + private void WaringOrCutOff(LiveRoomViewModel viewModel, object message) + { + var info = message as WarningOrCutOffMsgModel; + var msg = new DanmuMsgModel() + { + UserName = info.Command switch + { + "WARNING" => "⛔直播间警告", + "CUT_OFF" => "⛔直播间切断", + _ => null, + }, + UserNameColor = "FFFF0000", + RichText = info.Message.ToRichTextBlock(null, color: "Red", fontWeight: "Medium"), // 字重调大, 防止与进场弹幕混淆 + UserNameFontWeight = "SemiBold", + }; + + viewModel.Messages.Add(msg); + } + + private async void StartLive(LiveRoomViewModel viewModel, object room_Id) + { + await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(3)); // 挂起三秒再获取, 否则很大可能一直卡加载而不缓冲 + viewModel.GetPlayUrls(room_Id.ToInt32(), SettingService.GetValue(SettingConstants.Live.DEFAULT_QUALITY, 10000)).RunWithoutAwait(); + viewModel.Messages.Add(new DanmuMsgModel() + { + UserName = $"{room_Id} 直播间开始直播", + }); + } } } diff --git a/src/BiliLite.UWP/Models/Common/Live/LiveRoomAnchorLotteryInfoModel.cs b/src/BiliLite.UWP/Models/Common/Live/LiveRoomAnchorLotteryInfoModel.cs index a6810a8de..8881fe2b0 100644 --- a/src/BiliLite.UWP/Models/Common/Live/LiveRoomAnchorLotteryInfoModel.cs +++ b/src/BiliLite.UWP/Models/Common/Live/LiveRoomAnchorLotteryInfoModel.cs @@ -1,4 +1,6 @@ using Newtonsoft.Json; +using System.Collections.ObjectModel; +using Windows.UI.Xaml.Controls; namespace BiliLite.Models.Common.Live { @@ -7,15 +9,35 @@ public class LiveRoomAnchorLotteryInfoModel [JsonProperty("asset_icon")] public string AssetIcon { get; set; } + /// + /// 奖品的图片(因为有可能只是b站礼物) + /// [JsonProperty("award_image")] public string AwardImage { get; set; } + /// + /// 奖品名称 + /// [JsonProperty("award_name")] public string AwardName { get; set; } + /// + /// 奖品个数 + /// [JsonProperty("award_num")] public int AwardNum { get; set; } + /// + /// 中奖的用户信息 + /// + [JsonProperty("award_users")] + public ObservableCollection AwardUsers { get; set; } + + public StackPanel WinnerList => AwardUsers == null ? new StackPanel() : new LiveRoomEndAnchorLotteryInfoModel().GenerateWinnerList(AwardUsers); + + /// + /// 未知... + /// [JsonProperty("cur_gift_num")] public int CurGiftNum { get; set; } @@ -24,21 +46,36 @@ public class LiveRoomAnchorLotteryInfoModel public string Danmu { get; set; } + /// + /// 次级礼物信息? 因为有可能是b站礼物所以有id + /// [JsonProperty("gift_id")] public int GiftId { get; set; } [JsonProperty("show_gift")] 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; } + /// + /// 待调查... 可能用于控制抽奖结束后按钮的关闭时间? + /// [JsonProperty("goaway_time")] public int GoawayTime { get; set; } @@ -47,18 +84,30 @@ public class LiveRoomAnchorLotteryInfoModel [JsonProperty("join_type")] public int JoinType { get; set; } + /// + /// 抽奖的状态, 1为正在倒计时, 2为已开奖 + /// [JsonProperty("lot_status")] public int LotStatus { get; set; } [JsonProperty("max_time")] public int MaxTime { get; set; } + /// + /// 参与抽奖的需求 + /// [JsonProperty("require_text")] public string RequireText { get; set; } + /// + /// 参与抽奖的需求类型? + /// [JsonProperty("require_type")] public int RequireType { get; set; } + /// + /// 参与抽奖的需求等级? 可能和粉丝牌等级有关? 待调查 + /// [JsonProperty("require_value")] public int RequireValue { get; set; } @@ -73,6 +122,9 @@ public class LiveRoomAnchorLotteryInfoModel public int Status { get; set; } + /// + /// 剩余时间 + /// public int Time { get; set; } public string Url { get; set; } diff --git a/src/BiliLite.UWP/Models/Common/Live/LiveRoomEndAnchorLotteryInfoModel.cs b/src/BiliLite.UWP/Models/Common/Live/LiveRoomEndAnchorLotteryInfoModel.cs index 4dfda51ea..dbb76ec31 100644 --- a/src/BiliLite.UWP/Models/Common/Live/LiveRoomEndAnchorLotteryInfoModel.cs +++ b/src/BiliLite.UWP/Models/Common/Live/LiveRoomEndAnchorLotteryInfoModel.cs @@ -1,5 +1,12 @@ -using System.Collections.Generic; +using System; using Newtonsoft.Json; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media.Imaging; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Shapes; +using Windows.UI.Xaml; +using System.Collections.ObjectModel; +using System.Linq; namespace BiliLite.Models.Common.Live { @@ -25,6 +32,52 @@ public class LiveRoomEndAnchorLotteryInfoModel public string WebUrl { get; set; } [JsonProperty("award_users")] - public List AwardUsers { get; set; } + public ObservableCollection AwardUsers { get; set; } + + public StackPanel WinnerList => AwardUsers == null ? new StackPanel() : GenerateWinnerList(AwardUsers); + + public StackPanel GenerateWinnerList(ObservableCollection awardUsers) + { + var result = new StackPanel() + { + // + Orientation = Orientation.Vertical, + }; + if (awardUsers == null || awardUsers.ToArray().Length <= 0) return result; + foreach (var awardUser in awardUsers) + { + var awardUserItemPanel = new StackPanel() + { + // + Orientation = Orientation.Horizontal, + HorizontalAlignment = HorizontalAlignment.Center, + Margin = new Thickness(0, 4, 0, 4), + }; + var userFaceImage = new Ellipse() + { + Width = 30, + Height = 30, + Fill = new ImageBrush() + { + // + ImageSource = new BitmapImage(new Uri(awardUser.Face + "@30h")), + Stretch = Stretch.UniformToFill, + } + }; + var userNameText = new TextBlock() + { + // + Margin = new Thickness(4, 0, 4, 0), + TextWrapping = TextWrapping.Wrap, + Text = awardUser.Uname, + VerticalAlignment = VerticalAlignment.Center, + }; + + awardUserItemPanel.Children.Add(userFaceImage); + awardUserItemPanel.Children.Add(userNameText); + result.Children.Add(awardUserItemPanel); + } + return result; + } } } \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Common/Live/LiveRoomEndRedPocketLotteryInfoModel.cs b/src/BiliLite.UWP/Models/Common/Live/LiveRoomEndRedPocketLotteryInfoModel.cs new file mode 100644 index 000000000..c047567d5 --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/Live/LiveRoomEndRedPocketLotteryInfoModel.cs @@ -0,0 +1,117 @@ +using BiliLite.Extensions; +using BiliLite.Models.Exceptions; +using BiliLite.Services; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Documents; +using Windows.UI.Xaml.Markup; + +namespace BiliLite.Models.Common.Live +{ + public class LiveRoomEndRedPocketLotteryInfoModel + { + /// + /// 抽奖的id号 + /// + [JsonProperty("lot_id")] + public string LotId { get; set; } + + /// + /// 总中奖人数 + /// + [JsonProperty("total_num")] + public int TotalNumber { get; set; } + + /// + /// 中奖者详细信息 + /// + [JsonProperty("winner_info")] + public ObservableCollection> Winners { get; set; } + + public RichTextBlock WinnersList => WinnerToRichTextBlock(); + + /// + /// 中奖的礼物信息 + /// + [JsonProperty("awards")] + public IDictionary Awards { get; set; } + + /// + /// 版本号? 待调查, 可能用于区别不同版本的Json反馈 + /// + [JsonProperty("version")] + public int Version { get; set; } + + private static readonly ILogger _logger = GlobalLogger.FromCurrentType(); + + private RichTextBlock WinnerToRichTextBlock() + { + try + { + //var result = (RichTextBlock)XamlReader.Load(""); + var result = new RichTextBlock() { LineHeight = 28 }; + if (Winners == null) throw new CustomizedErrorException("红包中奖名单为空"); + foreach (var winner in Winners) + { + var name = winner[1]; + var giftId = winner[3]; + var p = $@" + {winner[1]} 获得 + + + + {Awards[giftId].AwardName} + "; + + var paragraph = (Paragraph)XamlReader.Load(p); + result.Blocks.Add(paragraph); + } + return result; + } + catch (Exception ex) + { + Notify.ShowMessageToast("红包中奖名单富文本转换失败"); + _logger.Error("红包中奖名单富文本转换失败", ex); + var text = new RichTextBlock(); + var paragraph = new Paragraph(); + var run = new Run(); + paragraph.Inlines.Add(run); + text.Blocks.Add(paragraph); + return text; + } + } + } + + public class LiveRoomEndRedPocketLotteryInfoAwardModel + { + /// + /// 奖品类型? 待调查 + /// + [JsonProperty("award_type")] + public int AwardType { get; set; } + + /// + /// 奖品名字 + /// + [JsonProperty("award_name")] + public string AwardName { get; set; } + + /// + /// 奖品图片 + /// + [JsonProperty("award_pic")] + public string AwardPic { get; set; } + + /// + /// 奖品价格(以金瓜子计算) + /// + [JsonProperty("award_price")] + public int AwardPrice { get; set; } + } +} diff --git a/src/BiliLite.UWP/Models/Common/Live/LiveRoomRankItemModel.cs b/src/BiliLite.UWP/Models/Common/Live/LiveRoomRankItemModel.cs index b502c4715..f18269a19 100644 --- a/src/BiliLite.UWP/Models/Common/Live/LiveRoomRankItemModel.cs +++ b/src/BiliLite.UWP/Models/Common/Live/LiveRoomRankItemModel.cs @@ -4,11 +4,24 @@ namespace BiliLite.Models.Common.Live { public class LiveRoomRankItemModel { + private string m_name; + public int Rank { get; set; } public long Uid { get; set; } - public string Uname { get; set; } + public string Uname + { + get => m_name; + set => m_name = value; + } + + [JsonProperty("name")] + public string Name + { + get => m_name; + set => m_name = value; + } public string Face { get; set; } diff --git a/src/BiliLite.UWP/Models/Common/Live/LiveRoomRedPocketLotteryInfoModel.cs b/src/BiliLite.UWP/Models/Common/Live/LiveRoomRedPocketLotteryInfoModel.cs new file mode 100644 index 000000000..212aa31ee --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/Live/LiveRoomRedPocketLotteryInfoModel.cs @@ -0,0 +1,193 @@ +using BiliLite.Extensions; +using BiliLite.Models.Exceptions; +using BiliLite.Services; +using Newtonsoft.Json; +using System; +using System.Collections.ObjectModel; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Documents; +using Windows.UI.Xaml.Markup; + +namespace BiliLite.Models.Common.Live +{ + public class LiveRoomRedPocketLotteryInfoModel + { + /// + /// 红包id + /// + [JsonProperty("lot_id")] + public string LotteryId { get; set; } + + /// + /// 发送者名称 + /// + [JsonProperty("sender_name")] + public string SenderName { get; set; } + + /// + /// 发送者UID + /// + [JsonProperty("sender_uid")] + public string SenderUid { get; set; } + + /// + /// 发送者头像 + /// + [JsonProperty("sender_face")] + public string SenderFace { get; set; } + + /// + /// 抽取条件? 未知 + /// + [JsonProperty("join_requirement")] + public int JoinRequirement { get; set; } + + /// + /// 抽取红包所发送的弹幕 + /// + [JsonProperty("danmu")] + public string Danmu { get; set; } + + /// + /// 礼物列表 + /// + [JsonProperty("awards")] + public ObservableCollection Awards { get; set; } + + public RichTextBlock AwardsList => AwardsToRichTextBlock(); + + /// + /// 红包开始时间 + /// + [JsonProperty("start_time")] + public int StartTime { get; set; } + + /// + /// 红包结束时间 + /// + [JsonProperty("end_time")] + public int EndTime { get; set; } + + /// + /// 红包持续时间(即EndTime - StartTime) + /// + [JsonProperty("last_time")] + public int LastTime { get; set; } + + /// + /// 红包按钮移除时间 + /// + [JsonProperty("remove_time")] + public int RemoveTime { get; set; } + + /// + /// ?待研究 + /// + [JsonProperty("replace_time")] + public int ReplaceTime { get; set; } + + /// + /// 目前时间 + /// + [JsonProperty("current_time")] + public int CurrentTime { get; set; } + + /// + /// 抽奖状态 1为正在倒计时 2为已经开奖 + /// + [JsonProperty("lot_status")] + public int LotteryStatus { get; set; } + + /// + /// 用户状态(可能是是否可以参与)? 待研究 + /// + [JsonProperty("user_status")] + public int UserStatus { get; set; } + + /// + /// 红包总共金额 + /// + [JsonProperty("total_price")] + public int TotalPrice { get; set; } + + /// + /// 可能是用于多个红包排队? 待研究 + /// + [JsonProperty("wait_num")] + public int WaitNumber { get; set; } + + private static readonly ILogger _logger = GlobalLogger.FromCurrentType(); + + private RichTextBlock AwardsToRichTextBlock() + { + try + { + //var result = (RichTextBlock)XamlReader.Load(""); + var result = new RichTextBlock() { LineHeight = 28 }; + if (Awards != null) + { + foreach (var item in Awards) + { + var p = string.Format( + @" + + + + + ", + item.GiftPicture, 24, item.GiftName, item.GiftNumber); + + var paragraph = (Paragraph)XamlReader.Load(p); + result.Blocks.Add(paragraph); + } + return result; + } + else + { + throw new CustomizedErrorException("红包奖品为空"); + } + } + catch (Exception ex) + { + Notify.ShowMessageToast("红包奖品富文本转换失败"); + _logger.Error("红包奖品富文本转换失败", ex); + var tx = new RichTextBlock(); + Paragraph paragraph = new Paragraph(); + Run run = new Run(); + paragraph.Inlines.Add(run); + tx.Blocks.Add(paragraph); + return tx; + } + } + } + + public class LiveRoomRedPocketLotteryAwardInfoModel + { + /// + /// 礼物ID + /// + [JsonProperty("gift_id")] + public string GiftId { get; set; } + + /// + /// 礼物名字 + /// + [JsonProperty("gift_name")] + public string GiftName { get; set; } + + /// + /// 礼物图片 + /// + [JsonProperty("gift_pic")] + public string GiftPicture { get; set; } + + /// + /// 礼物数量 + /// + [JsonProperty("num")] + public int GiftNumber { get; set; } + } +} diff --git a/src/BiliLite.UWP/Models/Common/Live/RoomBlockMsgModel.cs b/src/BiliLite.UWP/Models/Common/Live/RoomBlockMsgModel.cs new file mode 100644 index 000000000..5fd5e4ee5 --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/Live/RoomBlockMsgModel.cs @@ -0,0 +1,9 @@ +namespace BiliLite.Models.Common.Live +{ + public class RoomBlockMsgModel + { + public string UserID { get; set; } + + public string UserName { get; set; } + } +} diff --git a/src/BiliLite.UWP/Models/Common/Live/WarningOrCutOffMsgModel.cs b/src/BiliLite.UWP/Models/Common/Live/WarningOrCutOffMsgModel.cs new file mode 100644 index 000000000..2907881d1 --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/Live/WarningOrCutOffMsgModel.cs @@ -0,0 +1,9 @@ +namespace BiliLite.Models.Common.Live +{ + public class WarningOrCutOffMsgModel + { + public string Message { get; set; } + + public string Command { get; set; } + } +} diff --git a/src/BiliLite.UWP/Models/Common/Live/WelcomeMsgModel.cs b/src/BiliLite.UWP/Models/Common/Live/WelcomeMsgModel.cs deleted file mode 100644 index 11c61ba2a..000000000 --- a/src/BiliLite.UWP/Models/Common/Live/WelcomeMsgModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BiliLite.Models.Common.Live -{ - public class WelcomeMsgModel - { - public string UserName { get; set; } - - public string IsAdmin { get; set; } - - public string UserID { get; set; } - } -} \ No newline at end of file diff --git a/src/BiliLite.UWP/Models/Requests/Api/Live/LiveRoomAPI.cs b/src/BiliLite.UWP/Models/Requests/Api/Live/LiveRoomAPI.cs index f0c69531d..29a287c13 100644 --- a/src/BiliLite.UWP/Models/Requests/Api/Live/LiveRoomAPI.cs +++ b/src/BiliLite.UWP/Models/Requests/Api/Live/LiveRoomAPI.cs @@ -137,16 +137,17 @@ public ApiModel GetFreeSilver() /// 赠送背包礼物 /// /// - public ApiModel SendBagGift(long ruid, int gift_id, int num, int bag_id, int roomId) + public ApiModel SendBagGift(long ruid, int giftId, int num, int bagId, int roomId) { var api = new ApiModel() { method = RestSharp.Method.Post, - baseUrl = $"https://api.live.bilibili.com/gift/v2/live/bag_send", - parameter = ApiHelper.MustParameter(AppKey, true) + $"&actionKey=appkey", - body = $"uid={SettingService.Account.UserID}&ruid={ruid}&send_ruid=0&gift_id={gift_id}&gift_num={num}&bag_id={bag_id}&biz_id={roomId}&rnd={new Random().Next(1000, 999999).ToString("000000")}&biz_code=live&data_behavior_id=&data_source_id=" + baseUrl = $"https://api.live.bilibili.com/xlive/revenue/v1/gift/sendBag", + body = ApiHelper.MustParameter(AppKey, true) + $"&actionKey=appkey", + need_cookie = true, }; - api.parameter += ApiHelper.GetSign(api.parameter, AppKey); + api.body += $"&biz_code=live&biz_id={roomId}&gift_id={giftId}&gift_num={num}&price=0&bag_id={bagId}&rnd={TimeExtensions.GetTimestampMS()}&ruid={ruid}&uid={SettingService.Account.UserID}"; + api.body += ApiHelper.GetSign(api.body, AppKey); return api; } @@ -154,16 +155,18 @@ public ApiModel SendBagGift(long ruid, int gift_id, int num, int bag_id, int roo /// 赠送礼物 /// /// - public ApiModel SendGift(long ruid, int gift_id, int num, int roomId, string coin_type, int price) + public ApiModel SendGift(long ruid, int giftId, string coinType, int num, int roomId, int price) { var api = new ApiModel() { method = RestSharp.Method.Post, - baseUrl = $"https://api.live.bilibili.com/gift/v2/live/send", + baseUrl = $"https://api.live.bilibili.com/xlive/revenue/v1/gift/send{char.ToUpper(coinType[0]) + coinType.Substring(1)}", body = ApiHelper.MustParameter(AppKey, true) + $"&actionKey=appkey", + need_cookie = true, }; - api.body += $"&biz_code=live&biz_id={roomId}&coin_type={coin_type}&gift_id={gift_id}&gift_num={num}&mobi_app=android&platform=android&price={price}&rnd={TimeExtensions.GetTimestampMS()}&ruid={ruid}&uid={SettingService.Account.UserID}"; + api.body += $"&biz_code=live&biz_id={roomId}&gift_id={giftId}&gift_num={num}&price={price}&rnd={TimeExtensions.GetTimestampMS()}&ruid={ruid}&uid={SettingService.Account.UserID}"; api.body += ApiHelper.GetSign(api.body, AppKey); + return api; } @@ -245,16 +248,16 @@ public ApiModel FansList(long ruid, int roomId, int page) /// /// 主播ID /// 房间号 - /// - /// gold-rank=金瓜子排行,today-rank=今日礼物排行,seven-rank=7日礼物排行 + /// 目前仅发现为online_rank + /// 目前仅发现为contribution_rank /// - public ApiModel RoomRankList(long ruid, int roomId, string rank_type, int next_offset = 0) + public ApiModel RoomRankList(long ruid, int roomId, string rank_type, string switch_type) { var api = new ApiModel() { method = RestSharp.Method.Get, - baseUrl = $"https://api.live.bilibili.com/rankdb/v1/RoomRank/tabRanks", - parameter = ApiHelper.MustParameter(AppKey, true) + $"&actionKey=appkey&next_offset={next_offset}&room_id={roomId}&ruid={ruid}&rank_type={rank_type}", + baseUrl = $"https://api.live.bilibili.com/xlive/general-interface/v1/rank/queryContributionRank", + parameter = ApiHelper.MustParameter(AppKey, true) + $"&actionKey=appkey&room_id={roomId}&ruid={ruid}&type={rank_type}&switch={switch_type}", }; api.parameter += ApiHelper.GetSign(api.parameter, AppKey); return api; @@ -272,13 +275,14 @@ public ApiModel RoomLotteryInfo(int roomId) method = RestSharp.Method.Get, baseUrl = $"https://api.live.bilibili.com/xlive/lottery-interface/v1/lottery/getLotteryInfo", parameter = ApiHelper.MustParameter(AppKey, true) + $"&actionKey=appkey&roomid={roomId}", + need_cookie = true, }; api.parameter += ApiHelper.GetSign(api.parameter, AppKey); return api; } /// - /// 直播间抽奖信息 + /// 直播间超级留言(SuperChat)信息 /// /// /// @@ -310,7 +314,7 @@ public ApiModel RoomEntryAction(int roomId) return api; } - public ApiModel GetDanmukuInfo(int roomId) + public ApiModel GetDanmuInfo(int roomId) { var api = new ApiModel() { diff --git a/src/BiliLite.UWP/Modules/Live/LiveMessage.cs b/src/BiliLite.UWP/Modules/Live/LiveMessage.cs index dd6c01d8e..afe98119d 100644 --- a/src/BiliLite.UWP/Modules/Live/LiveMessage.cs +++ b/src/BiliLite.UWP/Modules/Live/LiveMessage.cs @@ -67,7 +67,7 @@ public async Task Connect(int roomID, int uid, string token, string buvid, strin var receivedData = new byte[ms.Length]; ms.Read(receivedData, 0, receivedData.Length); - ParseData(receivedData); + await ParseData(receivedData); } catch (Exception ex) { @@ -121,7 +121,7 @@ private async Task SendHeartBeatAsync() /// 解析内容 /// /// - private void ParseData(byte[] data) + private async Task ParseData(byte[] data) { //协议版本。 //0为JSON,可以直接解析; @@ -147,9 +147,13 @@ private void ParseData(byte[] data) var text = Encoding.UTF8.GetString(body); //可能有多条数据,做个分割 var textLines = Regex.Split(text, "[\x00-\x1f]+").Where(x => x.Length > 2 && x[0] == '{').ToArray(); + + var delay = textLines.Length > 30 ? 0.1 : 0.05; // 大概3秒来一波 + delay = textLines.Length > 60 ? 0.2 : delay; foreach (var item in textLines) { ParseMessage(item); + await Task.Delay(TimeSpan.FromSeconds(delay)); } } } @@ -160,7 +164,7 @@ private void ParseMessage(string jsonMessage) { var obj = JObject.Parse(jsonMessage); var cmd = obj["cmd"].ToString(); - if (cmd.Contains("DANMU_MSG")) + if (cmd == ("DANMU_MSG")) { var msg = new DanmuMsgModel(); if (obj["info"] != null && obj["info"].ToArray().Length != 0) @@ -171,7 +175,7 @@ private void ParseMessage(string jsonMessage) var extra = JObject.Parse(obj["info"][0][15]["extra"].ToString()); if (extra["emots"].ToArray().Length != 0) { - msg.Emoji = (JContainer)extra["emots"]; + msg.Emoji = (JObject)extra["emots"]; } } @@ -183,17 +187,19 @@ private void ParseMessage(string jsonMessage) msg.BigSticker = new DanmuMsgModel.BigStickerInfo { Url = (string)obj["info"][0][13]["url"], - Height = (int)obj["info"][0][13]["height"], - Width = (int)obj["info"][0][13]["width"], + Height = (int)obj["info"][0][13]["height"], + Width = (int)obj["info"][0][13]["width"], }; //有的表情特别大 :( - msg.BigSticker.Height = (msg.BigSticker.Height * 60 / msg.BigSticker.Width).ToInt32(); + msg.BigSticker.Height = (msg.BigSticker.Height * 60 / msg.BigSticker.Width).ToInt32(); msg.BigSticker.Width = 60; } // 弹幕内容 msg.Text = obj["info"][1].ToString(); - msg.RichText = StringExtensions.ToRichTextBlock(msg.Text, (JObject)msg.Emoji, true); + // 字重调大, 防止与进场弹幕混淆 + msg.UserNameFontWeight = "SemiBold"; + msg.RichText = msg.Text.ToRichTextBlock(msg.Emoji, true, fontWeight: "Medium"); // 弹幕颜色 var color = obj["info"][0][3].ToInt32(); @@ -216,18 +222,19 @@ private void ParseMessage(string jsonMessage) // 是否为舰长 if (obj["info"][3][10] != null && Convert.ToInt32(obj["info"][3][10].ToString()) != 0) { - switch (Convert.ToInt32(obj["info"][3][10].ToString())){ + switch (Convert.ToInt32(obj["info"][3][10].ToString())) + { case 3: msg.UserCaptain = "舰长"; - msg.UserCaptainImage = "/Assets/Live/ic_live_guard_3.png"; + msg.UserNameColor = "#FF23709E"; break; case 2: msg.UserCaptain = "提督"; - msg.UserCaptainImage = "/Assets/Live/ic_live_guard_2.png"; + msg.UserNameColor = "#FF7B166F"; break; case 1: msg.UserCaptain = "总督"; - msg.UserCaptainImage = "/Assets/Live/ic_live_guard_1.png"; + msg.UserNameColor = "#FFC01039"; break; } msg.ShowCaptain = Visibility.Visible; @@ -241,7 +248,6 @@ private void ParseMessage(string jsonMessage) msg.MedalColor = obj["info"][3][4].ToString(); msg.ShowMedal = Visibility.Visible; } - // 用户直播等级(已经被b站弃用) //if (obj["info"][4] != null && obj["info"][4].ToArray().Length != 0) //{ @@ -260,7 +266,7 @@ private void ParseMessage(string jsonMessage) return; } } - if (cmd == "SEND_GIFT") + else if (cmd == "SEND_GIFT" || cmd == "POPULARITY_RED_POCKET_NEW") { var msg = new GiftMsgModel(); if (obj["data"] != null) @@ -275,7 +281,7 @@ private void ParseMessage(string jsonMessage) } return; } - if (cmd == "COMBO_SEND") + else if (cmd == "COMBO_SEND") { var msg = new GiftMsgModel(); if (obj["data"] != null) @@ -290,63 +296,86 @@ private void ParseMessage(string jsonMessage) } return; } - if (cmd == "WELCOME") + else if (cmd == "INTERACT_WORD") { - var w = new WelcomeMsgModel(); + var w = new InteractWordModel(); if (obj["data"] != null) { w.UserName = obj["data"]["uname"].ToString(); w.UserID = obj["data"]["uid"].ToString(); + w.MsgType = obj["data"]["msg_type"].ToInt32(); - NewMessage?.Invoke(MessageType.Welcome, w); - } + 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.MedalColor = obj["data"]["fans_medal"]["medal_color"].ToString(); + w.ShowMedal = Visibility.Visible; + } + NewMessage?.Invoke(MessageType.InteractWord, w); + } return; } - if (cmd == "SYS_MSG") + // 没写相关实现, 先注释掉 + //if (cmd == "SYS_MSG") + //{ + // NewMessage?.Invoke(MessageType.SystemMsg, obj["msg"].ToString()); + // return; + //} + else if (cmd == "ANCHOR_LOT_START") { - NewMessage?.Invoke(MessageType.SystemMsg, obj["msg"].ToString()); + if (obj["data"] != null) + { + NewMessage?.Invoke(MessageType.AnchorLotteryStart, obj["data"].ToString()); + } return; } - if (cmd == "ANCHOR_LOT_START") + else if (cmd == "ANCHOR_LOT_AWARD") { if (obj["data"] != null) { - NewMessage?.Invoke(MessageType.AnchorLotteryStart, obj["data"].ToString()); + NewMessage?.Invoke(MessageType.AnchorLotteryAward, obj["data"].ToString()); } return; } - if (cmd == "ANCHOR_LOT_AWARD") + else if (cmd == "POPULARITY_RED_POCKET_START") { if (obj["data"] != null) { - NewMessage?.Invoke(MessageType.AnchorLotteryAward, obj["data"].ToString()); + NewMessage?.Invoke(MessageType.RedPocketLotteryStart, obj["data"].ToString()); } return; } - if (cmd == "SUPER_CHAT_MESSAGE") + else if (cmd == "POPULARITY_RED_POCKET_WINNER_LIST") { - SuperChatMsgViewModel msgView = new SuperChatMsgViewModel(); if (obj["data"] != null) { - msgView.BackgroundBottomColor = obj["data"]["background_bottom_color"].ToString(); - msgView.BackgroundColor = obj["data"]["background_color"].ToString(); - msgView.BackgroundImage = obj["data"]["background_image"].ToString(); - msgView.EndTime = obj["data"]["end_time"].ToInt32(); - msgView.StartTime = obj["data"]["start_time"].ToInt32(); - msgView.Time = obj["data"]["time"].ToInt32(); - msgView.MaxTime = msgView.EndTime - msgView.StartTime; - msgView.Face = obj["data"]["user_info"]["face"].ToString(); - msgView.FaceFrame = obj["data"]["user_info"]["face_frame"].ToString(); - msgView.FontColor = obj["data"]["message_font_color"].ToString(); - msgView.Message = obj["data"]["message"].ToString(); - msgView.Price = obj["data"]["price"].ToInt32(); - msgView.Username = obj["data"]["user_info"]["uname"].ToString(); - NewMessage?.Invoke(MessageType.SuperChat, msgView); + NewMessage?.Invoke(MessageType.RedPocketLotteryWinner, obj["data"].ToString()); } return; } - if (cmd == "ROOM_CHANGE") + else if (cmd == "SUPER_CHAT_MESSAGE") + { + var msgView = new SuperChatMsgViewModel(); + if (obj["data"] == null) return; + msgView.BackgroundBottomColor = obj["data"]["background_bottom_color"].ToString(); + msgView.BackgroundColor = obj["data"]["background_color"].ToString(); + msgView.BackgroundImage = obj["data"]["background_image"].ToString(); + msgView.EndTime = obj["data"]["end_time"].ToInt32(); + msgView.StartTime = obj["data"]["start_time"].ToInt32(); + msgView.Time = obj["data"]["time"].ToInt32(); + msgView.MaxTime = msgView.EndTime - msgView.StartTime; + msgView.Face = obj["data"]["user_info"]["face"].ToString(); + msgView.FaceFrame = obj["data"]["user_info"]["face_frame"].ToString(); + msgView.FontColor = obj["data"]["message_font_color"].ToString(); + msgView.Message = obj["data"]["message"].ToString(); + msgView.Price = obj["data"]["price"].ToInt32(); + msgView.Username = obj["data"]["user_info"]["uname"].ToString(); + NewMessage?.Invoke(MessageType.SuperChat, msgView); + return; + } + else if (cmd == "ROOM_CHANGE") { if (obj["data"] != null) { @@ -361,7 +390,7 @@ private void ParseMessage(string jsonMessage) } return; } - if (cmd == "GUARD_BUY") + else if (cmd == "GUARD_BUY") { if (obj["data"] != null) { @@ -378,6 +407,42 @@ private void ParseMessage(string jsonMessage) } return; } + else if (cmd == "ROOM_BLOCK_MSG") + { + if (obj["data"] != null) + { + NewMessage?.Invoke(MessageType.RoomBlock, new RoomBlockMsgModel() + { + UserID = obj["data"]["uid"].ToString(), + UserName = obj["data"]["uname"].ToString(), + }); + } + } + else if (cmd == "WARNING" || cmd == "CUT_OFF") + { + if (obj["msg"] != null) + { + NewMessage?.Invoke(MessageType.WaringOrCutOff, new WarningOrCutOffMsgModel() + { + Message = obj["msg"].ToString(), + Command = cmd, + }); + } + } + else if (cmd == "LIVE" || cmd == "REENTER_LIVE_ROOM") + { + if (obj["roomid"] != null) + { + NewMessage?.Invoke(MessageType.StartLive, obj["roomid"].ToString()); + } + } + else if (cmd == "WATCHED_CHANGE") + { + if (obj["data"] != null) + { + NewMessage?.Invoke(MessageType.WatchedChange, obj["data"]["text_large"].ToString()); + } + } } catch (Exception ex) { @@ -386,7 +451,6 @@ private void ParseMessage(string jsonMessage) logger.Error("直播解析JSON包出错", ex); } } - } /// diff --git a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml index d27eb71cf..c0edd2ab5 100644 --- a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml +++ b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml @@ -316,7 +316,7 @@ --> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -859,7 +1014,7 @@ - 人气: 粉丝: + 粉丝: @@ -1041,7 +1196,14 @@ - + + @@ -1065,7 +1227,7 @@ - + @@ -1108,8 +1270,13 @@ - - + + + + + + + diff --git a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs index 80f585555..4b6e26dcf 100644 --- a/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/LiveDetailPage.xaml.cs @@ -35,6 +35,7 @@ using BiliLite.Player.States.PlayStates; using BiliLite.Player.States.ScreenStates; using BiliLite.ViewModels.Live; +using Windows.UI.Xaml.Documents; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -94,7 +95,10 @@ public LiveDetailPage() m_liveRoomViewModel = new LiveRoomViewModel(); m_liveRoomViewModel.ChangedPlayUrl += LiveRoomViewModelChangedPlayUrl; m_liveRoomViewModel.AddNewDanmu += LiveRoomViewModelAddNewDanmu; - m_liveRoomViewModel.LotteryEnd += LiveRoomViewModelLotteryEnd; + m_liveRoomViewModel.AnchorLotteryEnd += LiveRoomViewModelAnchorLotteryEnd; + m_liveRoomViewModel.RedPocketLotteryEnd += LiveRoomViewModelRedPocketLotteryEnd; + m_liveRoomViewModel.ChatScrollToEnd += LiveRoomViewModelChatScrollToEnd; + m_liveRoomViewModel.LotteryViewModel.AnchorLotteryStart += LiveRoomViewModelAnchorLotteryStart; this.Loaded += LiveDetailPage_Loaded; this.Unloaded += LiveDetailPage_Unloaded; } @@ -125,12 +129,46 @@ private void Timer_focus_Tick(object sender, object e) } } - private void LiveRoomViewModelLotteryEnd(object sender, LiveRoomEndAnchorLotteryInfoModel e) + private void LiveRoomViewModelAnchorLotteryStart(object sender, LiveRoomAnchorLotteryInfoModel e) + { + AnchorLotteryWinnerList.Content = e.WinnerList; + if (e.WinnerList.Children.Count != 0) + { + m_liveRoomViewModel.ShowAnchorLotteryWinnerList = true; + } + } + + private void LiveRoomViewModelAnchorLotteryEnd(object sender, LiveRoomEndAnchorLotteryInfoModel e) { 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}, 恭喜欧皇~"; + } + } + Notify.ShowMessageToast(msg, new List(), 10); + AnchorLotteryWinnerList.Content = e.WinnerList; + m_liveRoomViewModel.ShowAnchorLotteryWinnerList = true; + m_liveRoomViewModel.LoadBag().RunWithoutAwait(); + } - Notify.ShowMessageToast($"开奖信息:\r\n奖品:{e.AwardName}\r\n中奖用户:{str}", new List() { }, 10); + private void LiveRoomViewModelRedPocketLotteryEnd(object sender, LiveRoomEndRedPocketLotteryInfoModel e) + { + var winners = e.Winners; + var awards = e.Awards; + redPocketWinnerList.Content = e.WinnersList; + m_liveRoomViewModel.ShowRedPocketLotteryWinnerList = true; + foreach (var winner in winners) + { + if (winner[0] == (SettingService.Account.UserID).ToString()) { + Notify.ShowMessageToast($"你已在人气红包抽中 {awards[winner[3]].AwardName} , 赶快查看吧~"); + } + } + m_liveRoomViewModel.LoadBag().RunWithoutAwait(); } private void LiveRoomViewModelAddNewDanmu(object sender, DanmuMsgModel e) @@ -151,6 +189,11 @@ 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) @@ -465,8 +508,8 @@ private void PreLoadSetting() }; // 播放器优先模式 m_viewModel.LivePlayerMode = (LivePlayerMode)SettingService.GetValue( - SettingConstants.Player.DEFAULT_LIVE_PLAYER_MODE, - (int)DefaultPlayerModeOptions.DEFAULT_LIVE_PLAYER_MODE); + SettingConstants.Player.DEFAULT_LIVE_PLAYER_MODE, + (int)DefaultPlayerModeOptions.DEFAULT_LIVE_PLAYER_MODE); m_playerConfig.PlayMode = m_viewModel.LivePlayerMode; } @@ -707,10 +750,20 @@ private async void BottomBtnRefresh_Click(object sender, RoutedEventArgs e) await m_liveRoomViewModel.LoadLiveRoomDetail(roomid); } - private void btnSendGift_Click(object sender, RoutedEventArgs e) + private async void btnSendGift_Click(object sender, RoutedEventArgs e) + { + if (sender is Button { DataContext: LiveGiftItem giftInfo }) + { + await m_liveRoomViewModel.SendGift(giftInfo); + } + } + + private async void btnSendBagGift_Click(object sender, RoutedEventArgs e) { - var giftInfo = (sender as Button).DataContext as LiveGiftItem; - m_liveRoomViewModel.SendGift(giftInfo).RunWithoutAwait(); + if (sender is Button { DataContext: LiveGiftItem giftInfo }) + { + await m_liveRoomViewModel.SendBagGift(giftInfo); + } } private async void TopBtnScreenshot_Click(object sender, RoutedEventArgs e) @@ -871,16 +924,39 @@ private void ListView_ItemClick(object sender, ItemClickEventArgs e) private async void BtnSendLotteryDanmu_Click(object sender, RoutedEventArgs e) { - if (m_liveRoomViewModel.AnchorLotteryViewModel != null && m_liveRoomViewModel.AnchorLotteryViewModel.LotteryInfo != null && !string.IsNullOrEmpty(m_liveRoomViewModel.AnchorLotteryViewModel.LotteryInfo.Danmu)) + if (m_liveRoomViewModel.LotteryViewModel != null && + m_liveRoomViewModel.LotteryViewModel.AnchorLotteryInfo != null && + !string.IsNullOrEmpty(m_liveRoomViewModel.LotteryViewModel.AnchorLotteryInfo.Danmu)) { - var result = await m_liveRoomViewModel.SendDanmu(m_liveRoomViewModel.AnchorLotteryViewModel.LotteryInfo.Danmu); + var result = await m_liveRoomViewModel.SendDanmu(m_liveRoomViewModel.LotteryViewModel.AnchorLotteryInfo.Danmu); if (result) { Notify.ShowMessageToast("弹幕发送成功"); FlyoutLottery.Hide(); } + } + } + 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 (!m_liveRoomViewModel.Attention) + { + BtnAttention_Click(sender, e); + msg += ", 关注主播成功"; + } + + Notify.ShowMessageToast(msg, 4); } private void BottomBtnMiniWindows_Click(object sender, RoutedEventArgs e) @@ -1092,12 +1168,6 @@ private void Grid_ManipulationCompleted(object sender, ManipulationCompletedRout } #endregion - private async void btnSendBagGift_Click(object sender, RoutedEventArgs e) - { - var giftInfo = (sender as Button).DataContext as LiveGiftItem; - await Task.Run(() => m_liveRoomViewModel.SendBagGift(giftInfo)).ConfigureAwait(false); - } - private void DataTransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args) { DataRequest request = args.Request; diff --git a/src/BiliLite.UWP/ViewModels/Live/LiveRoomAnchorLotteryViewModel.cs b/src/BiliLite.UWP/ViewModels/Live/LiveRoomAnchorLotteryViewModel.cs deleted file mode 100644 index 183e5c4e1..000000000 --- a/src/BiliLite.UWP/ViewModels/Live/LiveRoomAnchorLotteryViewModel.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Timers; -using BiliLite.Extensions; -using BiliLite.Models.Common; -using BiliLite.Models.Common.Live; -using BiliLite.Models.Exceptions; -using BiliLite.Models.Requests.Api.Live; -using BiliLite.Services; -using BiliLite.ViewModels.Common; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PropertyChanged; - -namespace BiliLite.ViewModels.Live -{ - public class LiveRoomAnchorLotteryViewModel : BaseViewModel - { - #region Fields - - private static readonly ILogger _logger = GlobalLogger.FromCurrentType(); - private readonly LiveRoomAPI m_liveRoomApi; - - #endregion - - #region Constructors - - public LiveRoomAnchorLotteryViewModel() - { - m_liveRoomApi = new LiveRoomAPI(); - Timer = new Timer(1000); - Timer.Elapsed += Timer_Elapsed; - } - - #endregion - - #region Properties - - [DoNotNotify] - public Timer Timer { get; set; } - - public LiveRoomAnchorLotteryInfoModel LotteryInfo { get; set; } - - public string DownTime { get; set; } = "--:--"; - - public bool End { get; set; } - - public bool Show { get; set; } - - #endregion - - #region Private Methods - - private async void Timer_Elapsed(object sender, ElapsedEventArgs e) - { - await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => - { - if (LotteryInfo == null) return; - if (LotteryInfo.Time <= 0) - { - End = false; - Timer.Stop(); - DownTime = "已开奖"; - Show = false; - LotteryInfo = null; - return; - } - var time = TimeSpan.FromSeconds(LotteryInfo.Time); - DownTime = time.ToString(@"mm\:ss"); - LotteryInfo.Time--; - }); - } - - #endregion - - #region Public Methods - - public async Task LoadLotteryInfo(int roomId) - { - try - { - var result = await m_liveRoomApi.RoomLotteryInfo(roomId).Request(); - if (!result.status) - { - throw new CustomizedErrorException(result.message); - } - - var obj = await result.GetData(); - if (!obj.success) - { - throw new CustomizedErrorException(obj.message); - } - - var data = JsonConvert.DeserializeObject(obj.data["anchor"] - .ToString()); - - LotteryInfo = data ?? throw new CustomizedErrorException("result data is null"); - Show = true; - Timer.Start(); - } - catch (Exception ex) - { - _logger.Log("加载主播抽奖信息失败", LogType.Error, ex); - } - } - - public void SetLotteryInfo(LiveRoomAnchorLotteryInfoModel info) - { - LotteryInfo = info; - Show = true; - Timer.Start(); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/BiliLite.UWP/ViewModels/Live/LiveRoomLotteryViewModel.cs b/src/BiliLite.UWP/ViewModels/Live/LiveRoomLotteryViewModel.cs new file mode 100644 index 000000000..0d3e9150e --- /dev/null +++ b/src/BiliLite.UWP/ViewModels/Live/LiveRoomLotteryViewModel.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using System.Timers; +using BiliLite.Extensions; +using BiliLite.Models.Common; +using BiliLite.Models.Common.Live; +using BiliLite.Models.Exceptions; +using BiliLite.Models.Requests.Api.Live; +using BiliLite.Services; +using BiliLite.ViewModels.Common; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PropertyChanged; + +namespace BiliLite.ViewModels.Live +{ + public class LiveRoomLotteryViewModel : BaseViewModel + { + #region Fields + + private static readonly ILogger _logger = GlobalLogger.FromCurrentType(); + private readonly LiveRoomAPI m_liveRoomApi; + + #endregion + + #region Constructors + + public LiveRoomLotteryViewModel() + { + m_liveRoomApi = new LiveRoomAPI(); + AnchorLotteryTimer = new Timer(1000); + AnchorLotteryTimer.Elapsed += AnchorLottery_Timer_Elapsed; + RedPocketLotteryTimer = new Timer(1000); + RedPocketLotteryTimer.Elapsed += RedPocket_Timer_Elapsed; + } + + #endregion + + #region Properties + + // 天选时刻相关 + [DoNotNotify] + public Timer AnchorLotteryTimer { get; set; } + + public LiveRoomAnchorLotteryInfoModel AnchorLotteryInfo { get; set; } + + public string AnchorLotteryDownTime { get; set; } = "--:--"; + + public bool AnchorLotteryShow { get; set; } + + // 人气红包相关 + [DoNotNotify] + public Timer RedPocketLotteryTimer { get; set; } + + public LiveRoomRedPocketLotteryInfoModel RedPocketLotteryInfo { get; set; } + + public string RedPocketLotteryDownTime { get; set; } = "--:--"; + + public bool RedPocketLotteryShow { get; set; } + + public event EventHandler AnchorLotteryStart; + + #endregion + + #region Private Methods + + private async void AnchorLottery_Timer_Elapsed(object sender, ElapsedEventArgs e) + { + await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + { + if (AnchorLotteryInfo == null) return; + + if (AnchorLotteryInfo.Time <= 0) + { + AnchorLotteryDownTime = "已开奖"; + AnchorLotteryInfo.GoawayTime--; + } + + if (AnchorLotteryInfo.GoawayTime <= 0) + { + AnchorLotteryTimer.Stop(); + AnchorLotteryShow = false; + AnchorLotteryInfo = null; + return; + } + + if (AnchorLotteryInfo.Time > 0) + { + AnchorLotteryDownTime = TimeSpan.FromSeconds(AnchorLotteryInfo.Time).ToString(@"mm\:ss"); + } + + AnchorLotteryInfo.Time--; + }); + } + + private async void RedPocket_Timer_Elapsed(object sender, ElapsedEventArgs e) + { + await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + { + if (RedPocketLotteryInfo == null) return; + + var nowTime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); + var runningTime = RedPocketLotteryInfo.CurrentTime - RedPocketLotteryInfo.StartTime; + + if (RedPocketLotteryInfo.LotteryStatus == 2 || + runningTime >= RedPocketLotteryInfo.LastTime || + nowTime >= RedPocketLotteryInfo.EndTime) + { + RedPocketLotteryDownTime = "已开奖"; + } + + if (nowTime >= RedPocketLotteryInfo.RemoveTime) + { + RedPocketLotteryInfo = null; + RedPocketLotteryTimer.Stop(); + RedPocketLotteryShow = false; + return; + } + + if (runningTime < RedPocketLotteryInfo.LastTime && + nowTime < RedPocketLotteryInfo.EndTime) + { + RedPocketLotteryDownTime = TimeSpan.FromSeconds(RedPocketLotteryInfo.EndTime - nowTime).ToString(@"mm\:ss"); + } + }); + } + + #endregion + + #region Public Methods + + public async Task LoadLotteryInfo(int roomId) + { + try + { + var result = await m_liveRoomApi.RoomLotteryInfo(roomId).Request(); + if (!result.status) + { + throw new CustomizedErrorException(result.message); + } + + var obj = await result.GetData(); + if (!obj.success) + { + throw new CustomizedErrorException(obj.message); + } + + if (obj.data["anchor"].ToArray().Length > 0) + { + var data = JsonConvert.DeserializeObject(obj.data["anchor"].ToString()); + AnchorLotteryInfo = data ?? throw new CustomizedErrorException("Anchor lottery data is null"); + AnchorLotteryStart?.Invoke(this, AnchorLotteryInfo); + AnchorLotteryShow = true; + AnchorLotteryTimer.Start(); + } + + 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(); + } + } + catch (Exception ex) + { + Notify.ShowMessageToast("加载主播抽奖信息失败"); + _logger.Log("加载主播抽奖信息失败", LogType.Error, ex); + } + } + + public void SetAnchorLotteryInfo(LiveRoomAnchorLotteryInfoModel info) + { + AnchorLotteryInfo = info; + AnchorLotteryShow = true; + AnchorLotteryTimer.Start(); + } + + public void SetRedPocketLotteryInfo(LiveRoomRedPocketLotteryInfoModel info) + { + RedPocketLotteryInfo = info; + RedPocketLotteryShow = true; + RedPocketLotteryTimer.Start(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/BiliLite.UWP/ViewModels/Live/LiveRoomRankViewModel.cs b/src/BiliLite.UWP/ViewModels/Live/LiveRoomRankViewModel.cs index 72ffcc086..c8a58dbc3 100644 --- a/src/BiliLite.UWP/ViewModels/Live/LiveRoomRankViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/Live/LiveRoomRankViewModel.cs @@ -68,8 +68,8 @@ public LiveRoomRankViewModel(int roomId, long uid, string title, string type) private void LoadDataCore(JObject data) { - var list = JsonConvert.DeserializeObject>(data["data"]["list"] - .ToString()); + var position = RankType == "fans" ? data["data"]["list"] : data["data"]["item"]; + var list = JsonConvert.DeserializeObject>(position.ToString()); if (list != null) { foreach (var item in list) @@ -77,21 +77,11 @@ private void LoadDataCore(JObject data) Items.Add(item); } } - - if (RankType != "fans") - { - Next = data["data"]["next_offset"].ToInt32(); - CanLoadMore = Next != 0; - } - else - { - var total = data["data"]["total_page"].ToInt32(); - if (Page < total) - { - CanLoadMore = true; - Page++; - } - } + if (RankType != "fans") return; + var total = data["data"]["total_page"].ToInt32(); + if (Page >= total) return; + CanLoadMore = true; + Page++; } #endregion @@ -113,11 +103,9 @@ public async Task LoadData() { Loading = true; CanLoadMore = false; - var api = m_liveRoomApi.FansList(Uid, RoomID, Page); - if (RankType != "fans") - { - api = m_liveRoomApi.RoomRankList(Uid, RoomID, RankType, Next); - } + var api = RankType == "fans" ? + m_liveRoomApi.FansList(Uid, RoomID, Page) : + m_liveRoomApi.RoomRankList(Uid, RoomID, "online_rank", "contribution_rank"); var result = await api.Request(); if (!result.status) diff --git a/src/BiliLite.UWP/ViewModels/Live/LiveRoomViewModel.cs b/src/BiliLite.UWP/ViewModels/Live/LiveRoomViewModel.cs index 45af7dc8f..63c28ed4c 100644 --- a/src/BiliLite.UWP/ViewModels/Live/LiveRoomViewModel.cs +++ b/src/BiliLite.UWP/ViewModels/Live/LiveRoomViewModel.cs @@ -46,7 +46,7 @@ public LiveRoomViewModel() m_liveRoomApi = new LiveRoomAPI(); m_playerApi = new PlayerAPI(); //liveMessage = new Live.LiveMessage(); - AnchorLotteryViewModel = new LiveRoomAnchorLotteryViewModel(); + LotteryViewModel = new LiveRoomLotteryViewModel(); MessageCenter.LoginedEvent += MessageCenter_LoginedEvent; MessageCenter.LogoutedEvent += MessageCenter_LogoutedEvent; Logined = SettingService.Account.Logined; @@ -87,7 +87,7 @@ public LiveRoomViewModel() [DoNotNotify] public Timer TimerAutoHideGift { get; private set; } - public LiveRoomAnchorLotteryViewModel AnchorLotteryViewModel { get; set; } + public LiveRoomLotteryViewModel LotteryViewModel { get; set; } [DoNotNotify] public static List Titles { get; set; } @@ -126,9 +126,9 @@ public LiveRoomViewModel() public bool ShowGiftMessage { get; set; } /// - /// 人气值 + /// 看过的人数(替代人气值) /// - public int Online { get; set; } + public string WatchedNum { get; set; } public bool Loading { get; set; } = true; @@ -193,16 +193,28 @@ public LiveRoomViewModel() [DoNotNotify] public bool AutoReceiveFreeSilver { get; set; } + public bool ShowRedPocketLotteryWinnerList { get; set; } = false; + + public bool ShowAnchorLotteryWinnerList { get; set; } = false; + + public string RedPocketSendDanmuBtnText { get; set; } = "一键关注并发送弹幕"; + #endregion #region Events public event EventHandler ChangedPlayUrl; - public event EventHandler LotteryEnd; + public event EventHandler AnchorLotteryEnd; public event EventHandler AddNewDanmu; + public event EventHandler ChatScrollToEnd; + + public event EventHandler RedPocketLotteryEnd; + + public event EventHandler AnchorLotteryStart; + #endregion #region Private Methods @@ -214,9 +226,13 @@ private LiveMessageHandleActionsMap InitLiveMessageHandleActionMap() { AddNewDanmu?.Invoke(this, e); }; - actionMap.LotteryEnd += (_, e) => + actionMap.AnchorLotteryEnd += (_, e) => + { + AnchorLotteryEnd?.Invoke(this, e); + }; + actionMap.RedPocketLotteryEnd += (_, e) => { - LotteryEnd?.Invoke(this, e); + RedPocketLotteryEnd?.Invoke(this, e); }; return actionMap; } @@ -227,7 +243,7 @@ private void LiveMessage_NewMessage(MessageType type, object message) var success = m_messageHandleActionsMap.Map.TryGetValue(type, out var handler); if (!success) return; - + handler(this, message); } @@ -244,6 +260,7 @@ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatch else { HideGiftFlag++; + ChatScrollToEnd?.Invoke(null, null); } }); } @@ -318,10 +335,10 @@ private async void ReceiveMessage(int roomId) var buvidData = await buvidResults.GetJson>(); var buvid = buvidData.data.B3; - var danmukuResults = await m_liveRoomApi.GetDanmukuInfo(roomId).Request(); - var danmukuData = await danmukuResults.GetJson>(); - var token = danmukuData.data.Token; - var host = danmukuData.data.HostList[0].Host; + 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); } @@ -533,16 +550,17 @@ public async Task LoadLiveRoomDetail(string id) RoomID = data.data.RoomInfo.RoomId; RoomTitle = data.data.RoomInfo.Title; - Online = data.data.RoomInfo.Online; + //WatchedNum = data.data.RoomInfo.Online; Liveing = data.data.RoomInfo.LiveStatus == 1; LiveInfo = data.data; if (Ranks == null) { Ranks = new List() { - new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "金瓜子榜", "gold-rank"), - new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "今日礼物榜", "today-rank"), - new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "七日礼物榜", "seven-rank"), + //new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "金瓜子榜", "gold-rank"), + //new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "今日礼物榜", "today-rank"), + //new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "七日礼物榜", "seven-rank"), + new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "高能用户贡献榜", "contribution-rank"), new LiveRoomRankViewModel(RoomID, data.data.RoomInfo.Uid, "粉丝榜", "fans"), }; SelectRank = Ranks[0]; @@ -559,7 +577,9 @@ await GetPlayUrls(RoomID, await LoadSuperChat(); if (ReceiveLotteryMsg) { - AnchorLotteryViewModel.LoadLotteryInfo(RoomID).RunWithoutAwait(); + // 抽奖 + LotteryViewModel.LoadLotteryInfo(RoomID).RunWithoutAwait(); + RedPocketSendDanmuBtnText = Attention ? "一键发送弹幕" : "一键关注并发送弹幕"; } } @@ -849,8 +869,11 @@ public async Task GetGuardList() if (!result.status) return; var data = await result.GetData(); if (!data.success) return; + var guardNum = data.data["info"]["num"].ToInt32(); + LiveInfo.GuardInfo.Count = guardNum; // 更新显示数字(似乎不生效...) + var top3 = JsonConvert.DeserializeObject>(data.data["top3"].ToString()); - if (Guards.Count == 0 && top3 != null && top3.Count != 0) + if (Guards.Count == 0 && top3 != null && top3.Count != 0 && Guards.Count < guardNum) { foreach (var item in top3) { @@ -859,7 +882,7 @@ public async Task GetGuardList() } var list = JsonConvert.DeserializeObject>(data.data["list"].ToString()); - if (list != null && list.Count != 0) + if (list != null && list.Count != 0 && Guards.Count < guardNum) { foreach (var item in list) { @@ -965,18 +988,19 @@ public async Task SendGift(LiveGiftItem liveGiftItem) } try { - var result = await m_liveRoomApi.SendGift(LiveInfo.RoomInfo.Uid, liveGiftItem.Id, liveGiftItem.Num, RoomID, liveGiftItem.CoinType, liveGiftItem.Price).Request(); + var result = await m_liveRoomApi.SendGift(LiveInfo.RoomInfo.Uid, liveGiftItem.Id, liveGiftItem.CoinType, liveGiftItem.Num, RoomID, liveGiftItem.Price).Request(); if (!result.status) { throw new CustomizedErrorException(result.message); } - var data = await result.GetData(); + var data = await result.GetData(); if (!data.success) { throw new CustomizedErrorException(data.message); } + Notify.ShowMessageToast(data.data?["send_tips"].ToString().Length > 0 ? data.data?["send_tips"].ToString() : "赠送成功"); // 鬼知道怎么有时候有返回提示有时候没有 await LoadWalletInfo(); } catch (CustomizedErrorException ex) @@ -1002,22 +1026,23 @@ public async Task SendBagGift(LiveGiftItem liveGiftItem) try { var result = await m_liveRoomApi.SendBagGift(LiveInfo.RoomInfo.Uid, liveGiftItem.Id, liveGiftItem.Num, liveGiftItem.BagId, RoomID).Request(); - if (result.status) + if (!result.status) { - var data = await result.GetData(); - if (data.success) - { - await LoadBag(); - } - else - { - Notify.ShowMessageToast(data.message); - } + throw new CustomizedErrorException(result.message); } - else + + var data = await result.GetData(); + if (!data.success) { - Notify.ShowMessageToast(result.message); + throw new CustomizedErrorException(data.message); } + Notify.ShowMessageToast(data.data?["send_tips"].ToString().Length > 0 ? data.data?["send_tips"].ToString() : "赠送成功"); + await LoadBag(); + } + catch (CustomizedErrorException ex) + { + Notify.ShowMessageToast(ex.Message); + _logger.Error(ex.Message, ex); } catch (Exception ex) { @@ -1072,10 +1097,11 @@ public void Dispose() m_timer?.Stop(); m_timerBox?.Stop(); TimerAutoHideGift?.Stop(); - if (AnchorLotteryViewModel != null) + if (LotteryViewModel != null) { - AnchorLotteryViewModel.Timer.Stop(); - AnchorLotteryViewModel = null; + LotteryViewModel.AnchorLotteryTimer.Stop(); + LotteryViewModel.RedPocketLotteryTimer.Stop(); + LotteryViewModel = null; } Messages?.Clear(); From e514fbaa6d93f463e8157f271b3058dcea84132d Mon Sep 17 00:00:00 2001 From: ywmoyue Date: Sun, 3 Dec 2023 16:36:37 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=9D=E5=AD=98wbi=E4=BB=A4=E7=89=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BiliLite.UWP/BiliLite.UWP.csproj | 2 + .../Models/Common/SettingConstants.cs | 20 +++++++ src/BiliLite.UWP/Models/Common/WbiKey.cs | 20 +++++++ src/BiliLite.UWP/Services/ApiHelper.cs | 16 +----- src/BiliLite.UWP/Services/WbiKeyService.cs | 57 +++++++++++++++++++ 5 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 src/BiliLite.UWP/Models/Common/WbiKey.cs create mode 100644 src/BiliLite.UWP/Services/WbiKeyService.cs diff --git a/src/BiliLite.UWP/BiliLite.UWP.csproj b/src/BiliLite.UWP/BiliLite.UWP.csproj index 90f5d19d8..4b1cb741f 100644 --- a/src/BiliLite.UWP/BiliLite.UWP.csproj +++ b/src/BiliLite.UWP/BiliLite.UWP.csproj @@ -176,6 +176,7 @@ + @@ -217,6 +218,7 @@ + diff --git a/src/BiliLite.UWP/Models/Common/SettingConstants.cs b/src/BiliLite.UWP/Models/Common/SettingConstants.cs index 2448e3bf8..d94eab02f 100644 --- a/src/BiliLite.UWP/Models/Common/SettingConstants.cs +++ b/src/BiliLite.UWP/Models/Common/SettingConstants.cs @@ -151,6 +151,26 @@ public class Account /// 默认登录用AppKey /// public static ApiKeyInfo DefaultLoginAppKeySecret = ApiHelper.AndroidKey; + + /// + /// Wbi令牌ImgKey参数 + /// + public const string WBI_IMG_KEY = "WbiImgKey"; + + /// + /// Wbi令牌SubKey参数 + /// + public const string WBI_SUB_KEY = "WbiSubKey"; + + /// + /// Wbi令牌参数获取时间(unix时间戳) + /// + public const string WBI_KEY_TIME = "WbiKeyTime"; + + /// + /// Wbi令牌参数刷新时间(单位分钟,暂定2小时) + /// + public const int WBI_KEY_REFRESH_TIME = 120; } public class VideoDanmaku diff --git a/src/BiliLite.UWP/Models/Common/WbiKey.cs b/src/BiliLite.UWP/Models/Common/WbiKey.cs new file mode 100644 index 000000000..b1d2ac14b --- /dev/null +++ b/src/BiliLite.UWP/Models/Common/WbiKey.cs @@ -0,0 +1,20 @@ +namespace BiliLite.Models.Common +{ + public class WbiKey + { + public WbiKey(string imgKey, string subKey) + { + ImgKey = imgKey; + SubKey = subKey; + } + + public WbiKey() + { + + } + + public string ImgKey { get; set; } + + public string SubKey { get; set; } + } +} diff --git a/src/BiliLite.UWP/Services/ApiHelper.cs b/src/BiliLite.UWP/Services/ApiHelper.cs index fd2eddcee..df0715836 100644 --- a/src/BiliLite.UWP/Services/ApiHelper.cs +++ b/src/BiliLite.UWP/Services/ApiHelper.cs @@ -59,18 +59,6 @@ public static class ApiHelper 36, 20, 34, 44, 52 }; - private static async Task<(string, string)> GetWbiKeys() - { - // 获取最新的 img_key 和 sub_key - var response = await new AccountApi().Nav().Request(); - var result = await response.GetData(); - var imgUrl = result.data.WbiImg.ImgUrl; - var subUrl = result.data.WbiImg.SubUrl; - var imgKey = imgUrl.Substring(imgUrl.LastIndexOf('/') + 1).Split('.')[0]; - var subKey = subUrl.Substring(subUrl.LastIndexOf('/') + 1).Split('.')[0]; - return (imgKey, subKey); - } - private static string GetMixinKey(string origin) { // 对 imgKey 和 subKey 进行字符顺序打乱编码 @@ -79,7 +67,9 @@ private static string GetMixinKey(string origin) public static async Task GetWbiSign(string url) { - var (imgKey, subKey) = await GetWbiKeys(); + var wbiKeys = await new WbiKeyService().GetWbiKeys(); + var imgKey = wbiKeys.ImgKey; + var subKey = wbiKeys.SubKey; // 为请求参数进行 wbi 签名 var mixinKey = GetMixinKey(imgKey + subKey); diff --git a/src/BiliLite.UWP/Services/WbiKeyService.cs b/src/BiliLite.UWP/Services/WbiKeyService.cs new file mode 100644 index 000000000..3e645ee9b --- /dev/null +++ b/src/BiliLite.UWP/Services/WbiKeyService.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using BiliLite.Extensions; +using BiliLite.Models.Common; +using BiliLite.Models.Common.Account; +using BiliLite.Models.Requests.Api; + +namespace BiliLite.Services +{ + public class WbiKeyService + { + private static async Task GetNewWbiKeys() + { + // 获取最新的 img_key 和 sub_key + var response = await new AccountApi().Nav().Request(); + var result = await response.GetData(); + var imgUrl = result.data.WbiImg.ImgUrl; + var subUrl = result.data.WbiImg.SubUrl; + var imgKey = imgUrl.Substring(imgUrl.LastIndexOf('/') + 1).Split('.')[0]; + var subKey = subUrl.Substring(subUrl.LastIndexOf('/') + 1).Split('.')[0]; + + var currentTime = DateTimeOffset.Now.ToUnixTimeSeconds(); + SettingService.SetValue(SettingConstants.Account.WBI_IMG_KEY, imgKey); + SettingService.SetValue(SettingConstants.Account.WBI_SUB_KEY, subKey); + SettingService.SetValue(SettingConstants.Account.WBI_KEY_TIME, currentTime); + + return new WbiKey(imgKey, subKey); + } + + private WbiKey GetCurrentWbiKeys() + { + var imgKey = SettingService.GetValue(SettingConstants.Account.WBI_IMG_KEY, string.Empty); + var subKey = SettingService.GetValue(SettingConstants.Account.WBI_SUB_KEY, string.Empty); + return new WbiKey(imgKey, subKey); + } + + public async Task GetWbiKeys() + { + var lastKeySaveTimestamp = SettingService.GetValue(SettingConstants.Account.WBI_KEY_TIME, 0); + if (lastKeySaveTimestamp <= 0) return await GetNewWbiKeys(); + var lastKeySaveTime = DateTimeOffset.FromUnixTimeSeconds(lastKeySaveTimestamp); + if ((DateTimeOffset.Now - lastKeySaveTime) >= + TimeSpan.FromMinutes(SettingConstants.Account.WBI_KEY_REFRESH_TIME)) + { + var newKeys = await GetNewWbiKeys(); + return newKeys; + } + else + { + var wbiKey = GetCurrentWbiKeys(); + if (wbiKey.ImgKey != string.Empty && wbiKey.SubKey != string.Empty) return wbiKey; + var newKeys = await GetNewWbiKeys(); + return newKeys; + } + } + } +} From 98995d86a0314ced8556f04b3c7ab08c2859fafd Mon Sep 17 00:00:00 2001 From: ywmoyue Date: Sun, 10 Dec 2023 11:18:50 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8DArm64=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E7=A0=81=E7=94=9F=E6=88=90=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BiliLite.UWP/BiliLite.UWP.csproj | 15 +++++--- .../Extensions/QrCodeExtensions.cs | 19 +++++++++++ src/BiliLite.UWP/Modules/User/LoginVM.cs | 15 ++++---- .../Pages/SeasonDetailPage.xaml.cs | 34 ++++++++----------- .../Pages/VideoDetailPage.xaml.cs | 20 ++++------- .../Services/Interfaces/IQrCodeService.cs | 10 ++++++ .../Services/QrCoderQrCodeService.cs | 30 ++++++++++++++++ .../Services/ZXingQrCodeService.cs | 25 ++++++++++++++ src/BiliLite.UWP/Startup.cs | 1 + 9 files changed, 124 insertions(+), 45 deletions(-) create mode 100644 src/BiliLite.UWP/Extensions/QrCodeExtensions.cs create mode 100644 src/BiliLite.UWP/Services/Interfaces/IQrCodeService.cs create mode 100644 src/BiliLite.UWP/Services/QrCoderQrCodeService.cs create mode 100644 src/BiliLite.UWP/Services/ZXingQrCodeService.cs diff --git a/src/BiliLite.UWP/BiliLite.UWP.csproj b/src/BiliLite.UWP/BiliLite.UWP.csproj index 5e108be5d..3034457ae 100644 --- a/src/BiliLite.UWP/BiliLite.UWP.csproj +++ b/src/BiliLite.UWP/BiliLite.UWP.csproj @@ -33,7 +33,7 @@ true bin\x86\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + TRACE;DEBUG;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;X86 ;2008 full x86 @@ -58,7 +58,7 @@ true bin\ARM\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + TRACE;DEBUG;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;ARM ;2008 full ARM @@ -83,7 +83,7 @@ true bin\ARM64\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + TRACE;DEBUG;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;ARM64 ;2008 full ARM64 @@ -109,7 +109,7 @@ true bin\x64\Debug\ - DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + TRACE;DEBUG;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS;X64 ;2008 full x64 @@ -136,6 +136,7 @@ PackageReference + @@ -223,7 +224,10 @@ + + + @@ -1228,6 +1232,9 @@ 4.1.0 + + 1.4.3 + 107.3.0 diff --git a/src/BiliLite.UWP/Extensions/QrCodeExtensions.cs b/src/BiliLite.UWP/Extensions/QrCodeExtensions.cs new file mode 100644 index 000000000..dab41dd3b --- /dev/null +++ b/src/BiliLite.UWP/Extensions/QrCodeExtensions.cs @@ -0,0 +1,19 @@ +using BiliLite.Services; +using BiliLite.Services.Interfaces; +using Microsoft.Extensions.DependencyInjection; + +namespace BiliLite.Extensions +{ + public static class QrCodeExtensions + { + public static IServiceCollection AddQrCodeService(this IServiceCollection services) + { +#if ARM64 + services.AddTransient(); +#else + services.AddTransient(); +#endif + return services; + } + } +} diff --git a/src/BiliLite.UWP/Modules/User/LoginVM.cs b/src/BiliLite.UWP/Modules/User/LoginVM.cs index 09602d351..005909c1e 100644 --- a/src/BiliLite.UWP/Modules/User/LoginVM.cs +++ b/src/BiliLite.UWP/Modules/User/LoginVM.cs @@ -13,6 +13,8 @@ using BiliLite.Models.Common; using BiliLite.Models.Requests.Api; using BiliLite.Services; +using BiliLite.Services.Interfaces; +using Microsoft.Extensions.DependencyInjection; namespace BiliLite.Modules.User { @@ -600,15 +602,10 @@ private async void GetQRAuthInfo() if (result.success) { qrAuthInfo = result.data; - ZXing.BarcodeWriter barcodeWriter = new ZXing.BarcodeWriter(); - barcodeWriter.Format = ZXing.BarcodeFormat.QR_CODE; - barcodeWriter.Options = new ZXing.Common.EncodingOptions() - { - Margin = 1, - Height = 200, - Width = 200 - }; - var img = barcodeWriter.Write(qrAuthInfo.url); + + var qrCodeService = App.ServiceProvider.GetRequiredService(); + var img = await qrCodeService.GenerateQrCode(qrAuthInfo.url); + QRImageSource = img; qrTimer = new Timer(); qrTimer.Interval = 3000; diff --git a/src/BiliLite.UWP/Pages/SeasonDetailPage.xaml.cs b/src/BiliLite.UWP/Pages/SeasonDetailPage.xaml.cs index 0f6361261..9b99fe050 100644 --- a/src/BiliLite.UWP/Pages/SeasonDetailPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/SeasonDetailPage.xaml.cs @@ -22,6 +22,8 @@ using BiliLite.Models.Common.Video; using BiliLite.Models.Download; using BiliLite.ViewModels.Season; +using BiliLite.Services.Interfaces; +using Microsoft.Extensions.DependencyInjection; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -134,7 +136,7 @@ private async Task InitSeasonDetail() seasonReviewVM.MediaID = m_viewModel.Detail.MediaId; InitializePlayInfo(); - CreateQR(); + await CreateQR(); } } @@ -298,7 +300,8 @@ private void PlayerControl_FullWindowEvent(object sender, bool e) } } bool changedFlag = false; - private void PlayerControl_ChangeEpisodeEvent(object sender, int e) + + private async void PlayerControl_ChangeEpisodeEvent(object sender, int e) { changedFlag = true; var aid = ""; @@ -315,7 +318,7 @@ private void PlayerControl_ChangeEpisodeEvent(object sender, int e) aid = m_viewModel.Episodes[e].Aid; } - CreateQR(); + await CreateQR(); comment.LoadComment(new LoadCommentInfo() { CommentMode = (int)CommentApi.CommentType.Video, @@ -325,7 +328,7 @@ private void PlayerControl_ChangeEpisodeEvent(object sender, int e) changedFlag = false; } - private void listEpisode_SelectionChanged(object sender, SelectionChangedEventArgs e) + private async void listEpisode_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (changedFlag || listEpisode.SelectedIndex == -1) { @@ -345,9 +348,10 @@ private void listEpisode_SelectionChanged(object sender, SelectionChangedEventAr CommentSort = CommentApi.CommentSort.Hot, Oid = m_viewModel.Episodes[listEpisode.SelectedIndex].Aid }); - CreateQR(); + await CreateQR(); } - private void listPreview_SelectionChanged(object sender, SelectionChangedEventArgs e) + + private async void listPreview_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (changedFlag || listPreview.SelectedIndex == -1) { @@ -367,24 +371,16 @@ private void listPreview_SelectionChanged(object sender, SelectionChangedEventAr CommentSort = CommentApi.CommentSort.Hot, Oid = m_viewModel.Episodes[listPreview.SelectedIndex].Aid }); - CreateQR(); + await CreateQR(); } - - private void CreateQR() + private async Task CreateQR() { try { - ZXing.BarcodeWriter barcodeWriter = new ZXing.BarcodeWriter(); - barcodeWriter.Format = ZXing.BarcodeFormat.QR_CODE; - barcodeWriter.Options = new ZXing.Common.EncodingOptions() - { - Margin = 1, - Height = 200, - Width = 200 - }; - var data = barcodeWriter.Write("https://b23.tv/ep" + ep_id); - imgQR.Source = data; + var qrCodeService = App.ServiceProvider.GetRequiredService(); + var img = await qrCodeService.GenerateQrCode("https://b23.tv/ep" + ep_id); + imgQR.Source = img; } catch (Exception ex) { diff --git a/src/BiliLite.UWP/Pages/VideoDetailPage.xaml.cs b/src/BiliLite.UWP/Pages/VideoDetailPage.xaml.cs index 047ded6f1..f7f8d05f7 100644 --- a/src/BiliLite.UWP/Pages/VideoDetailPage.xaml.cs +++ b/src/BiliLite.UWP/Pages/VideoDetailPage.xaml.cs @@ -22,6 +22,8 @@ using BiliLite.Models.Common.Video.Detail; using BiliLite.Models.Download; using BiliLite.ViewModels.Video; +using BiliLite.Services.Interfaces; +using Microsoft.Extensions.DependencyInjection; // https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板 @@ -182,7 +184,7 @@ private async Task InitializeVideo(string id) contentDesc.Content = desc; ChangeTitle(m_viewModel.VideoInfo.Title); - CreateQR(); + await CreateQR(); if (!string.IsNullOrEmpty(m_viewModel.VideoInfo.RedirectUrl)) { var result = await MessageCenter.HandelSeasonID(m_viewModel.VideoInfo.RedirectUrl); @@ -275,27 +277,19 @@ private void InitUgcSeason(string id) pivot.Items.Insert(0, element); } - private void CreateQR() + private async Task CreateQR() { try { - ZXing.BarcodeWriter barcodeWriter = new ZXing.BarcodeWriter(); - barcodeWriter.Format = ZXing.BarcodeFormat.QR_CODE; - barcodeWriter.Options = new ZXing.Common.EncodingOptions() - { - Margin = 1, - Height = 200, - Width = 200 - }; - var data = barcodeWriter.Write(m_viewModel.VideoInfo.ShortLink); - imgQR.Source = data; + var qrCodeService = App.ServiceProvider.GetRequiredService(); + var img = await qrCodeService.GenerateQrCode(m_viewModel.VideoInfo.ShortLink); + imgQR.Source = img; } catch (Exception ex) { logger.Log("创建二维码失败avid" + avid, LogType.Error, ex); Notify.ShowMessageToast("创建二维码失败"); } - } protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) diff --git a/src/BiliLite.UWP/Services/Interfaces/IQrCodeService.cs b/src/BiliLite.UWP/Services/Interfaces/IQrCodeService.cs new file mode 100644 index 000000000..ef6267806 --- /dev/null +++ b/src/BiliLite.UWP/Services/Interfaces/IQrCodeService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Windows.UI.Xaml.Media; + +namespace BiliLite.Services.Interfaces +{ + public interface IQrCodeService + { + public Task GenerateQrCode(string content, int size = 200); + } +} diff --git a/src/BiliLite.UWP/Services/QrCoderQrCodeService.cs b/src/BiliLite.UWP/Services/QrCoderQrCodeService.cs new file mode 100644 index 000000000..57c0ea371 --- /dev/null +++ b/src/BiliLite.UWP/Services/QrCoderQrCodeService.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using Windows.UI.Xaml.Media; +using BiliLite.Services.Interfaces; +using QRCoder; +using Windows.Storage.Streams; +using Windows.UI.Xaml.Media.Imaging; + +namespace BiliLite.Services +{ + public class QrCoderQrCodeService : IQrCodeService + { + public async Task GenerateQrCode(string content, int size = 200) + { + var qrGenerator = new QRCodeGenerator(); + var qrCodeData = qrGenerator.CreateQrCode(content, QRCodeGenerator.ECCLevel.Q); + + var qrCodeBmp = new BitmapByteQRCode(qrCodeData); + var qrCodeImageBmp = qrCodeBmp.GetGraphic(size); + + using var stream = new InMemoryRandomAccessStream(); + using var writer = new DataWriter(stream.GetOutputStreamAt(0)); + writer.WriteBytes(qrCodeImageBmp); + await writer.StoreAsync(); + var image = new BitmapImage(); + await image.SetSourceAsync(stream); + return image; + } + } +} diff --git a/src/BiliLite.UWP/Services/ZXingQrCodeService.cs b/src/BiliLite.UWP/Services/ZXingQrCodeService.cs new file mode 100644 index 000000000..1b3df3671 --- /dev/null +++ b/src/BiliLite.UWP/Services/ZXingQrCodeService.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Windows.UI.Xaml.Media; +using BiliLite.Services.Interfaces; + +namespace BiliLite.Services +{ + public class ZXingQrCodeService : IQrCodeService + { + public Task GenerateQrCode(string content,int size=200) + { + var barcodeWriter = new ZXing.BarcodeWriter + { + Format = ZXing.BarcodeFormat.QR_CODE, + Options = new ZXing.Common.EncodingOptions() + { + Margin = 1, + Height = size, + Width = size + } + }; + var img = barcodeWriter.Write(content); + return Task.FromResult(img); + } + } +} diff --git a/src/BiliLite.UWP/Startup.cs b/src/BiliLite.UWP/Startup.cs index 38619a6fc..7b9367c17 100644 --- a/src/BiliLite.UWP/Startup.cs +++ b/src/BiliLite.UWP/Startup.cs @@ -13,6 +13,7 @@ public void ConfigureServices(HostBuilderContext context, IServiceCollection ser services.AddViewModels(); services.AddSingleton(); + services.AddQrCodeService(); services.AddSingleton(); }