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");
+ }
+ }
}