diff --git a/src/BiliLite.UWP/Assets/Live/RedPocket.png b/src/BiliLite.UWP/Assets/Live/RedPocket.png new file mode 100644 index 000000000..f698eb6be Binary files /dev/null and b/src/BiliLite.UWP/Assets/Live/RedPocket.png differ diff --git a/src/BiliLite.UWP/BiliLite.UWP.csproj b/src/BiliLite.UWP/BiliLite.UWP.csproj index 90f5d19d8..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,8 +136,14 @@ PackageReference + + + + + + @@ -176,6 +182,7 @@ + @@ -217,14 +224,17 @@ + + + + + - - @@ -1222,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/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/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/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/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/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/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/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/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 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/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/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/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/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; + } + } + } +} 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(); } 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(); 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)