From 5de62af8ca71118fd7a0767b44417ed284cba3d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B1=B3?= Date: Sat, 7 Sep 2024 16:50:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=AB=E7=85=A7=E5=88=87?= =?UTF-8?q?=E7=89=87=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/RuntimeObject/Danmu.cs | 10 +-- Core/RuntimeObject/Download/Basics.cs | 10 ++- Core/RuntimeObject/Download/FLV.cs | 2 +- Core/RuntimeObject/Download/HLS.cs | 2 +- Core/RuntimeObject/Download/Snapshot.cs | 42 ++++++++++++ Core/RuntimeObject/RoomInfo.cs | 82 +++++++++++------------ Desktop/NetWork/Post.cs | 3 + Desktop/Views/Control/CardControl.xaml | 3 + Desktop/Views/Control/CardControl.xaml.cs | 28 ++++++++ Server/WebAppServices/Api/rec_task.cs | 21 ++++++ 10 files changed, 153 insertions(+), 50 deletions(-) create mode 100644 Core/RuntimeObject/Download/Snapshot.cs diff --git a/Core/RuntimeObject/Danmu.cs b/Core/RuntimeObject/Danmu.cs index cc65cd711..9f87f8dbf 100644 --- a/Core/RuntimeObject/Danmu.cs +++ b/Core/RuntimeObject/Danmu.cs @@ -202,11 +202,11 @@ public static void SevaDanmu(LiveChat.LiveChatListener liveChatListener, bool De /// /// 储存原始弹幕信息到xml文件 /// - /// - /// - /// - /// - private static FileInfo SevaDanmu(List danmuInfo, string FileName, string Name, long roomId) + /// 弹幕信息对象 + /// 保存路径 + /// 昵称 + /// 房间号 + public static FileInfo SevaDanmu(List danmuInfo, string FileName, string Name, long roomId) { string XML = string.Empty; XML = "" + diff --git a/Core/RuntimeObject/Download/Basics.cs b/Core/RuntimeObject/Download/Basics.cs index a7edee4f6..6d0281706 100644 --- a/Core/RuntimeObject/Download/Basics.cs +++ b/Core/RuntimeObject/Download/Basics.cs @@ -230,7 +230,12 @@ internal static bool GetHlsHost_avc(RoomCardClass roomCard, ref HostClass hostCl } - + /// + /// 识别M3U8文件格式适配二级M3U8 + /// + /// + /// + /// internal static string Senior_M3U8_Analysis(string M3U8, ref HostClass hostClass) { if (string.IsNullOrEmpty(M3U8) || !M3U8.Contains("index.m3u8")) @@ -252,6 +257,8 @@ internal static string Senior_M3U8_Analysis(string M3U8, ref HostClass hostClass } return M3U8; } + + /// /// 刷新HlsHost信息 /// @@ -410,7 +417,6 @@ public static List GetOptionalClarity(long RoomId, string protocol_name, s } - public static void LiveChatListener_MessageReceived(object? sender, Core.LiveChat.MessageEventArgs e) { LiveChatListener liveChatListener = (LiveChatListener)sender; diff --git a/Core/RuntimeObject/Download/FLV.cs b/Core/RuntimeObject/Download/FLV.cs index b1cf5db95..281ba9fd5 100644 --- a/Core/RuntimeObject/Download/FLV.cs +++ b/Core/RuntimeObject/Download/FLV.cs @@ -95,7 +95,7 @@ await Task.Run(async () => HostClass hostClass = GetFlvHost_avc(card); string DlwnloadURL = $"{hostClass.host}{hostClass.base_url}{hostClass.uri_name}{hostClass.extra}"; //把当前写入文件写入记录 - string F_S = Config.Core_RunConfig._RecordingStorageDirectory + "/" + File.Replace(Config.Core_RunConfig._RecFileDirectory, "").Replace("\\", "/"); + string F_S = Config.Core_RunConfig._RecFileDirectory + "/" + File.Replace(Config.Core_RunConfig._RecFileDirectory, "").Replace("\\", "/"); card.DownInfo.DownloadFileList.CurrentOperationVideoFile = F_S; //下载提示 LogDownloadStart(card, "FLV"); diff --git a/Core/RuntimeObject/Download/HLS.cs b/Core/RuntimeObject/Download/HLS.cs index b97ee507f..9d802c519 100644 --- a/Core/RuntimeObject/Download/HLS.cs +++ b/Core/RuntimeObject/Download/HLS.cs @@ -167,7 +167,7 @@ await Task.Run(() => if (InitialRequest) { //把当前写入文件写入记录 - string F_S = Config.Core_RunConfig._RecordingStorageDirectory + "/" + fs.Name.Replace(new DirectoryInfo(Config.Core_RunConfig._RecFileDirectory).FullName, "").Replace("\\", "/"); + string F_S = Config.Core_RunConfig._RecFileDirectory + "/" + fs.Name.Replace(new DirectoryInfo(Config.Core_RunConfig._RecFileDirectory).FullName, "").Replace("\\", "/"); card.DownInfo.DownloadFileList.CurrentOperationVideoFile = F_S; Log.Debug("test",card.DownInfo.DownloadFileList.CurrentOperationVideoFile); //正式开始下载提示 diff --git a/Core/RuntimeObject/Download/Snapshot.cs b/Core/RuntimeObject/Download/Snapshot.cs new file mode 100644 index 000000000..efe34b18c --- /dev/null +++ b/Core/RuntimeObject/Download/Snapshot.cs @@ -0,0 +1,42 @@ +using Core.LogModule; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Core.RuntimeObject.Download +{ + public class Snapshot + { + /// + /// 生成录制中的直播间快照用于切片 + /// + /// + /// + public static (bool state,string message) CreateRecordingSnapshot(long Uid) + { + if (Uid == 0) return (false,"UID不能为空"); + + RoomCardClass Card = new(); + if (!_Room.GetCardForUID(Uid, ref Card) || Card == null || !Card.DownInfo.IsDownload) return (false,"当前房间未开播或未录制"); + + string videoFile = Card.DownInfo.DownloadFileList.CurrentOperationVideoFile; + if (!File.Exists(videoFile)) return (false,"当前直播间还未开始录制流"); + + string tempFile = $"{Core.Config.Core_RunConfig._TemporaryFileDirectory}{Path.GetFileName(videoFile)}"; + File.Copy(videoFile, tempFile, true); + + var listener = Card.DownInfo.LiveChatListener; + if (listener != null) + { + Danmu.SevaDanmu(listener.DanmuMessage.Danmu, tempFile, Card.Name, Card.RoomId); + Danmu.SevaGift(listener.DanmuMessage.Gift, tempFile); + Danmu.SevaGuardBuy(listener.DanmuMessage.GuardBuy, tempFile); + Danmu.SevaSuperChat(listener.DanmuMessage.SuperChat, tempFile); + } + return (true,tempFile); + } + } +} \ No newline at end of file diff --git a/Core/RuntimeObject/RoomInfo.cs b/Core/RuntimeObject/RoomInfo.cs index 1dec2cf53..72a72f7ba 100644 --- a/Core/RuntimeObject/RoomInfo.cs +++ b/Core/RuntimeObject/RoomInfo.cs @@ -725,28 +725,28 @@ internal static void BatchUpdateRoomStatusForLiveStream(List? UIDList = nu /// internal static void _BatchUpdateRoomStatusForLiveStream(List UIDList) { - - UidsInfo_Class uidsInfo_Class = GetRoomList(UIDList); - UIDList = null; - if (uidsInfo_Class != null && uidsInfo_Class.data != null && uidsInfo_Class.data.Count > 0) + + UidsInfo_Class uidsInfo_Class = GetRoomList(UIDList); + UIDList = null; + if (uidsInfo_Class != null && uidsInfo_Class.data != null && uidsInfo_Class.data.Count > 0) + { + foreach (var item in uidsInfo_Class.data) { - foreach (var item in uidsInfo_Class.data) + long.TryParse(item.Key, out long uid); + if (uid > 0) { - long.TryParse(item.Key, out long uid); - if (uid > 0) + RoomCardClass roomCard = new(); + if (_Room.GetCardForUID(uid, ref roomCard)) { - RoomCardClass roomCard = new(); - if (_Room.GetCardForUID(uid, ref roomCard)) - { - _Room.SetRoomCardByUid(uid, ToRoomCard(item.Value, roomCard)); - } - roomCard = null; + _Room.SetRoomCardByUid(uid, ToRoomCard(item.Value, roomCard)); } + roomCard = null; } } - uidsInfo_Class = null; - - + } + uidsInfo_Class = null; + + } #endregion @@ -756,7 +756,7 @@ internal static void _BatchUpdateRoomStatusForLiveStream(List UIDList) private static long _GetLiveTime(long Uid) { - RoomCardClass roomCard = new(); + RoomCardClass roomCard = new(); if (!_Room.GetCardForUID(Uid, ref roomCard)) { roomCard = ToRoomCard(GetRoomInfo(GetRoomId(Uid)), roomCard); @@ -903,7 +903,7 @@ private static RoomCardClass ToRoomCard(UidsInfo_Class.Data data, RoomCardClass RoomCardClass card = new RoomCardClass() { UID = data.uid, - Title = new() { Value = data.title.Replace("/","-").Replace("\\","-"), ExpirationTime = DateTime.Now.AddSeconds(30) }, + Title = new() { Value = data.title.Replace("/", "-").Replace("\\", "-"), ExpirationTime = DateTime.Now.AddSeconds(30) }, RoomId = data.room_id, live_time = new() { Value = data.live_time, ExpirationTime = DateTime.Now.AddSeconds(30) }, live_status = new() { Value = data.live_status, ExpirationTime = DateTime.Now.AddSeconds(3) }, @@ -934,7 +934,7 @@ private static RoomCardClass ToRoomCard(UidsInfo_Class.Data data, RoomCardClass else { OldCard.UID = data.uid; - OldCard.Title = new() { Value = data.title.Replace("/","-").Replace("\\","-"), ExpirationTime = DateTime.Now.AddSeconds(30) }; + OldCard.Title = new() { Value = data.title.Replace("/", "-").Replace("\\", "-"), ExpirationTime = DateTime.Now.AddSeconds(30) }; OldCard.RoomId = data.room_id; OldCard.live_time = new() { Value = data.live_time, ExpirationTime = DateTime.Now.AddSeconds(30) }; @@ -976,8 +976,8 @@ private static RoomCardClass ToRoomCard(UidsInfo_Class.Data data, RoomCardClass } catch (Exception ex) { - string A = data != null ? JsonSerializer.Serialize(data, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)}) : "内容为空"; - string B = OldCard != null ? JsonSerializer.Serialize(OldCard, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)}) : "内容为空"; + string A = data != null ? JsonSerializer.Serialize(data, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }) : "内容为空"; + string B = OldCard != null ? JsonSerializer.Serialize(OldCard, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }) : "内容为空"; Log.Error(nameof(ToRoomCard), $"在UidsInfo_Class.Data的TRC操作中出现意料外的错误,错误堆栈:[UidsInfo_Class.Data:{A}];[RoomCard:{B}]", ex, true); return null; } @@ -1051,8 +1051,8 @@ private static RoomCardClass ToRoomCard(RoomInfo_Class roomInfo, RoomCardClass O } catch (Exception ex) { - string A = roomInfo != null ? JsonSerializer.Serialize(roomInfo, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)}) : "内容为空"; - string B = OldCard != null ? JsonSerializer.Serialize(OldCard, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)}) : "内容为空"; + string A = roomInfo != null ? JsonSerializer.Serialize(roomInfo, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }) : "内容为空"; + string B = OldCard != null ? JsonSerializer.Serialize(OldCard, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }) : "内容为空"; Log.Error(nameof(ToRoomCard), $"在RoomInfo_Class的TRC操作中出现意料外的错误,错误堆栈:[RoomInfo_Class:{A}];[RoomCard:{B}]", ex, true); return null; } @@ -1073,7 +1073,7 @@ private static RoomCardClass ToRoomCard(UserInfo userInfo, RoomCardClass OldCard Name = userInfo.data.name, url = new() { Value = $"https://live.bilibili.com/{userInfo.data.live_room.roomid}", ExpirationTime = DateTime.MaxValue }, roomStatus = new() { Value = userInfo.data.live_room.liveStatus, ExpirationTime = DateTime.Now.AddSeconds(3) }, - Title = new() { Value = userInfo.data.live_room.title.Replace("/","-").Replace("\\","-"), ExpirationTime = DateTime.Now.AddSeconds(30) }, + Title = new() { Value = userInfo.data.live_room.title.Replace("/", "-").Replace("\\", "-"), ExpirationTime = DateTime.Now.AddSeconds(30) }, cover_from_user = new() { Value = userInfo.data.live_room.cover, ExpirationTime = DateTime.Now.AddMinutes(10) }, face = new() { Value = userInfo.data.face, ExpirationTime = DateTime.MaxValue }, sex = new() { Value = userInfo.data.sex, ExpirationTime = DateTime.MaxValue }, @@ -1089,7 +1089,7 @@ private static RoomCardClass ToRoomCard(UserInfo userInfo, RoomCardClass OldCard OldCard.Name = userInfo.data.name; OldCard.url = new() { Value = $"https://live.bilibili.com/{userInfo.data.live_room.roomid}", ExpirationTime = DateTime.MaxValue }; OldCard.roomStatus = new() { Value = userInfo.data.live_room.liveStatus, ExpirationTime = DateTime.Now.AddSeconds(3) }; - OldCard.Title = new() { Value = userInfo.data.live_room.title.Replace("/","-").Replace("\\","-"), ExpirationTime = DateTime.Now.AddSeconds(30) }; + OldCard.Title = new() { Value = userInfo.data.live_room.title.Replace("/", "-").Replace("\\", "-"), ExpirationTime = DateTime.Now.AddSeconds(30) }; OldCard.cover_from_user = new() { Value = userInfo.data.live_room.cover, ExpirationTime = DateTime.Now.AddMinutes(10) }; OldCard.face = new() { Value = userInfo.data.face, ExpirationTime = DateTime.MaxValue }; OldCard.sex = new() { Value = userInfo.data.sex, ExpirationTime = DateTime.MaxValue }; @@ -1106,8 +1106,8 @@ private static RoomCardClass ToRoomCard(UserInfo userInfo, RoomCardClass OldCard catch (Exception ex) { - string userInfoContent = userInfo != null ? JsonSerializer.Serialize(userInfo, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)}) : "内容为空"; - string oldCardContent = OldCard != null ? JsonSerializer.Serialize(OldCard, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)}) : "内容为空"; + string userInfoContent = userInfo != null ? JsonSerializer.Serialize(userInfo, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }) : "内容为空"; + string oldCardContent = OldCard != null ? JsonSerializer.Serialize(OldCard, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }) : "内容为空"; Log.Error(nameof(ToRoomCard), $"在UserInfo的TRC操作中出现意料外的错误,错误原始数据:[userInfo:{userInfoContent}];[RoomCard:{oldCardContent}];堆栈:{ex.ToString}", ex, true); return null; } @@ -1340,22 +1340,22 @@ public class RoomCardClass /// 轮播状态(0:未轮播 1:轮播) /// public ExpansionType roundStatus = new() { ExpirationTime = DateTime.UnixEpoch, Value = -1 }; - [JsonIgnore] + [JsonIgnore] /// /// 直播间网页url /// public ExpansionType url = new() { ExpirationTime = DateTime.UnixEpoch, Value = string.Empty }; - [JsonIgnore] + [JsonIgnore] /// /// 用户等级 /// public ExpansionType level = new() { ExpirationTime = DateTime.UnixEpoch, Value = -1 }; - [JsonIgnore] + [JsonIgnore] /// /// 主播性别 /// public ExpansionType sex = new() { ExpirationTime = DateTime.UnixEpoch, Value = string.Empty }; - [JsonIgnore] + [JsonIgnore] /// /// 主播简介 /// @@ -1378,35 +1378,35 @@ public class DownloadInfo /// /// 当前是否在下载 /// - public bool IsDownload = false; + public bool IsDownload { set; get; } = false; /// /// 是否触发瞎几把剪 /// - public bool IsCut = false; + public bool IsCut { set; get; } = false; /// /// 任务类型 /// - public TaskType taskType = TaskType.NewTask; + public TaskType taskType { set; get; } = TaskType.NewTask; /// /// 当前房间下载任务总大小 /// - public long DownloadSize = 0; + public long DownloadSize { set; get; } = 0; /// /// 实时下载速度 /// - public double RealTimeDownloadSpe = 0; + public double RealTimeDownloadSpe { set; get; } = 0; /// /// 任务状态 /// - public DownloadStatus Status = DownloadStatus.NewTask; + public DownloadStatus Status { set; get; } = DownloadStatus.NewTask; /// /// 任务开始时间 /// - public DateTime StartTime = DateTime.UnixEpoch; + public DateTime StartTime { set; get; } = DateTime.UnixEpoch; /// /// 任务结束时间 /// - public DateTime EndTime = DateTime.UnixEpoch; + public DateTime EndTime { set; get; } = DateTime.UnixEpoch; /// /// 弹幕对象 /// @@ -1416,11 +1416,11 @@ public class DownloadInfo /// /// 取消录制标记 /// - public bool Unmark = false; + public bool Unmark { set; get; } = false; /// /// 该录制任务的文件名缓存 /// - public DownloadFile DownloadFileList = new(); + public DownloadFile DownloadFileList { set; get; } = new(); public class DownloadFile { diff --git a/Desktop/NetWork/Post.cs b/Desktop/NetWork/Post.cs index f23bf11b1..87fbed162 100644 --- a/Desktop/NetWork/Post.cs +++ b/Desktop/NetWork/Post.cs @@ -28,10 +28,12 @@ public class Post /// 请求返回体 public static async Task PostBody(string url, Dictionary _dic = null) { + if (!string.IsNullOrEmpty(url) && url.Length > 5 && url.Substring(0, 4) != "http") { url = "http://" + url; } + try { Dictionary dic = new Dictionary @@ -58,6 +60,7 @@ public static async Task PostBody(string url, Dictionary _ var response = await client.PostAsync(url, content); var responseString = response.Content.ReadAsStringAsync().Result; OperationQueue.pack A = JsonConvert.DeserializeObject>(responseString); + return A.data; } } diff --git a/Desktop/Views/Control/CardControl.xaml b/Desktop/Views/Control/CardControl.xaml index 526d9fa15..35be82f59 100644 --- a/Desktop/Views/Control/CardControl.xaml +++ b/Desktop/Views/Control/CardControl.xaml @@ -18,6 +18,9 @@ + + diff --git a/Desktop/Views/Control/CardControl.xaml.cs b/Desktop/Views/Control/CardControl.xaml.cs index 14f90a490..fc9e317d6 100644 --- a/Desktop/Views/Control/CardControl.xaml.cs +++ b/Desktop/Views/Control/CardControl.xaml.cs @@ -198,5 +198,33 @@ private void MenuItem_OpenLiveUlr_Click(object sender, RoutedEventArgs e) }; Process.Start(psi); } + + private void Snapshot_Task_Click(object sender, RoutedEventArgs e) + { + Models.DataCard dataCard = GetDataCard(sender); + Dictionary dic = new Dictionary + { + {"uid", dataCard.Uid.ToString() } + }; + Task.Run(() => + { + var message = NetWork.Post.PostBody<(bool state, string message)>($"{Config.Core_RunConfig._DesktopIP}:{Config.Core_RunConfig._DesktopPort}/api/rec_task/generate_snapshot", dic).Result; + if (!message.state) + { + Log.Info(nameof(Snapshot_Task_Click), $"生成直播间录制快照失败,原因:{message.message}"); + Dispatcher.Invoke(() => + { + MainWindow.SnackbarService.Show("快照失败", $"生成直播间录制快照失败,原因:{message.message}", ControlAppearance.Danger, new SymbolIcon(SymbolRegular.ErrorCircle20), TimeSpan.FromSeconds(5)); + }); + } + else + { + Dispatcher.Invoke(() => + { + MainWindow.SnackbarService.Show("快照完成", $"生成直播间录制快照完成,已输出到DDTV临时文件夹中({message.message})", ControlAppearance.Success, new SymbolIcon(SymbolRegular.Checkmark20), TimeSpan.FromSeconds(10)); + }); + } + }); + } } } diff --git a/Server/WebAppServices/Api/rec_task.cs b/Server/WebAppServices/Api/rec_task.cs index 744b1f003..37e00f2f3 100644 --- a/Server/WebAppServices/Api/rec_task.cs +++ b/Server/WebAppServices/Api/rec_task.cs @@ -76,4 +76,25 @@ public ActionResult Post(PostCommonParameters commonParameters, [FromForm] long return Content(MessageBase.MssagePack(nameof(cut_task), TaskInfo.State, $"{TaskInfo.Message}"), "application/json"); } } + + [Produces(MediaTypeNames.Application.Json)] + [ApiController] + [Route("api/rec_task/[controller]")] + [Login] + [Tags("rec_task")] + public class generate_snapshot : ControllerBase + { + /// + /// 创建直播间快照用于快速切片 + /// + /// + /// + /// + [HttpPost(Name = "generate_snapshot")] + public ActionResult Post(PostCommonParameters commonParameters, [FromForm] long uid = 0) + { + var message = Core.RuntimeObject.Download.Snapshot.CreateRecordingSnapshot(uid); + return Content(MessageBase.MssagePack(nameof(generate_snapshot),message,message.state?"创建快照成功":"创建快照失败", message.state?MessageCode.code.ok:MessageCode.code.OperationFailed), "application/json"); + } + } }