diff --git a/Learn.VideoAnalysis.API/Controllers/LJZK_Controller.cs b/Learn.VideoAnalysis.API/Controllers/LJZK_Controller.cs deleted file mode 100644 index 35c42f7..0000000 --- a/Learn.VideoAnalysis.API/Controllers/LJZK_Controller.cs +++ /dev/null @@ -1,196 +0,0 @@ - -using VideoAnalysisCore.Common; -using Microsoft.AspNetCore.Mvc; -using System.Reflection; -using MapsterMapper; -using Mapster; -using VideoAnalysisCore.AICore.SherpaOnnx; -using UserCenter.Model.Enum; -using VideoAnalysisCore.AICore.GPT.ChatGPT; -using VideoAnalysisCore.AICore.GPT; -using System.Text.Json; -using Microsoft.AspNetCore.Authorization; -using VideoAnalysisCore.Model.Enum; -using FFmpeg.NET.Services; -using Yitter.IdGenerator; -using VideoAnalysisCore.AICore.GPT.Dto; -using VideoAnalysisCore.Model; -using Learn.VideoAnalysis.API.Controllers.Dto; - -namespace Learn.VideoAnalysis.API.Controllers -{ - /// - /// 蓝鲸字库接口 - /// - [ApiController] - [Route("LJZK/[action]")] - public class LJZK_Controller : ControllerBase - { - private readonly ILogger _logger; - private readonly IMapper mp; - private readonly Repository nodesubscriptionDB; - private readonly Repository videoTaskDB; - private readonly Repository videoKonwPointDB; - private readonly Repository nodePackageInfoDB; - public LJZK_Controller(ILogger logger, - IMapper mp, Repository nodesubscriptionDB, - Repository videoTaskDB = null, Repository videoKonwPointDB = null - , Repository nodePackageInfoDB = null) - { - _logger = logger; - this.mp = mp; - this.nodesubscriptionDB = nodesubscriptionDB; - this.videoTaskDB = videoTaskDB; - this.videoKonwPointDB = videoKonwPointDB; - this.nodePackageInfoDB = nodePackageInfoDB; - } - - - /// - /// 蓝鲸智库_添加文件节点监控 - /// - /// 请求体 - /// - [HttpPost(Name = "NodeSubscription")] - public async Task NodeSubscription(NodeMonitoringReq req) - { - if (req is null || req.NodeId == 0) - return BadRequest("无效的提交数据"); - if (nodesubscriptionDB.IsAny(s => s.NodeId == req.NodeId)) - return BadRequest("重复添加了节点监控任务" + req.NodeId); - var res = await nodesubscriptionDB.InsertReturnEntityAsync(new NodeSubscription() - { - NodeId = req.NodeId, - TaskType = req.Type ?? default, - Subject = req.Subject ?? default, - - }); - return Ok(res); - } - - /// - /// 蓝鲸智库_文件包订阅 - /// - /// 请求体 - /// - [HttpPost(Name = "NodePackage")] - public async Task NodePackage(NodePackageReq req) - { - Console.WriteLine($"{DateTime.Now} 文件包订阅请求 req=" + JsonSerializer.Serialize(req)); - if (req.AnalyzeItems is null || req.AnalyzeItems.Count() == 0) - return BadRequest("无效视频列表数据"); - var videos = new List(req.AnalyzeItems.Count); - var nodePackages = new List(req.AnalyzeItems.Count); - var videoIdArr = videoTaskDB.AsQueryable().Select(v => v.TagId).Distinct().ToArray(); - foreach (var s in req.AnalyzeItems) - { - var np = new NodePackageInfo() - { - VideoCode = s.VideoCode, - AttachmentsInfoType = s.AttachmentsInfoType, - MaterialId = s.MaterialId, - StructurePageContentId = s.StructurePageContentId, - VideoName = s.VideoName, - NodeId = req.NodeId, - TaskType = req.TaskType, - SubjectType = req.SubjectType, - }; - nodePackages.Add(np); - if (videoIdArr.Contains(s.VideoCode)) - continue; - videos.Add(new VideoTask() - { - Id = YitIdHelper.NextId(), - ComeFrom = "127.0.0.1", - ApiToken = "", - Type = req.TaskType, - Subject = req.SubjectType, - TagId = s.VideoCode, - MediaUrl = string.Empty, - MediaName = s.VideoName - }); - } - await nodePackageInfoDB.InsertRangeAsync(nodePackages); - await videoTaskDB.InsertRangeAsync(videos); - if (videos is null || videos.Count == 0) - return Ok(); - var ids = videos.Select(s => s.Id).ToArray(); - RedisExpand.JoinQueue(ids); - return Ok(); - } - - /// - /// 获取任务类型 - /// - /// - [HttpGet(Name = "TaskTypList")] - public IActionResult TaskType() - { - Type type = typeof(TaskTypeEnum); - return Ok(Enum.GetValues(type).Cast() - .Select(s => new { Text = s.ToString(), Value = (int)s })); - } - /// - /// 获取学科类型 - /// - /// - [HttpGet(Name = "SubjectList")] - public IActionResult Subject() - { - Type type = typeof(SubjectEnum); - return Ok(Enum.GetValues(type).Cast() - .Select(s => new { Text = s.ToString(), Value = (int)s })); - } - - /// - /// 获取视频知识点片段taskId/tagId二选一 - /// - /// - /// 自定义id - /// - [HttpGet(Name = "TaskKnowInfo")] - public async Task TaskKnowInfo(long taskId, string? tagId) - { - if (taskId == 0 && string.IsNullOrEmpty(tagId)) - return BadRequest(); - var task = await videoTaskDB.AsQueryable() - .WhereIF(taskId != 0, s => s.Id == taskId) - .WhereIF(!string.IsNullOrEmpty(tagId), s => s.TagId == tagId) - .FirstAsync(); - if (task is null) - return BadRequest("无效任务"); - - var konwArr = await videoKonwPointDB.AsQueryable() - .Where(s => s.VideoTaskId == task.Id) - .ToArrayAsync(); - if (konwArr is null || konwArr.Length == 0) - return BadRequest("无效任务"); - return Ok(new TaskKnowRes() - { - TagId = task.TagId, - Status = task.LastEnum, - VideoTaskId = task.Id, - KnowBlockArr = konwArr - .GroupBy(s => s.StartTime) - .Select(s => new TaskKnowBlock() - { - Id = s.First().Id, - Content = s.First().Content, - StartTime = s.First().StartTime, - EndTime = s.First().EndTime, - Theme = s.First().Theme, - Know = s.Select(x => new TaskKnowInfo() - { - Id = x.Id, - KnowPoint = x.KnowPoint, - KnowPointId = x.KnowPointId - }).ToArray() - }).ToArray() - }); - } - - - - - } -} diff --git a/Learn.VideoAnalysis.API/Program.cs b/Learn.VideoAnalysis.API/Program.cs index 240fdb1..3573147 100644 --- a/Learn.VideoAnalysis.API/Program.cs +++ b/Learn.VideoAnalysis.API/Program.cs @@ -22,7 +22,9 @@ namespace Learn.VideoAnalysis.API builder.Services.AddSwaggerGen(c => { var file = Path.Combine(AppContext.BaseDirectory, "Learn.VideoAnalysis.API.xml"); // xml文档绝对路径 + var file1 = Path.Combine(AppContext.BaseDirectory, "VideoAnalysisCore.xml"); // xml文档绝对路径 c.IncludeXmlComments(file, true); // true : 显示控制器层注释 + c.IncludeXmlComments(file1, true); // true : 显示控制器层注释 c.OrderActionsBy(o => o.RelativePath); // 对action的名称进行排序,如果有多个,就可以看见效果了。 }); diff --git a/Learn.VideoAnalysis.sln b/Learn.VideoAnalysis.sln index 450a0c5..8f82961 100644 --- a/Learn.VideoAnalysis.sln +++ b/Learn.VideoAnalysis.sln @@ -7,7 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Learn.VideoAnalysis", "Vide EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VideoAnalysisCore", "VideoAnalysisCore\VideoAnalysisCore.csproj", "{69F4243A-B22E-431B-8F0B-ECD8729B8665}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Learn.VideoAnalysis.API", "Learn.VideoAnalysis.API\Learn.VideoAnalysis.API.csproj", "{D31BA4AB-73FC-47B1-A10A-34FD5E921F4A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Learn.VideoAnalysis.API", "Learn.VideoAnalysis.API\Learn.VideoAnalysis.API.csproj", "{D31BA4AB-73FC-47B1-A10A-34FD5E921F4A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/VideoAnalysis/Components/Layouts/BasicLayout.razor.cs b/VideoAnalysis/Components/Layouts/BasicLayout.razor.cs index 232e6f2..a9a4be5 100644 --- a/VideoAnalysis/Components/Layouts/BasicLayout.razor.cs +++ b/VideoAnalysis/Components/Layouts/BasicLayout.razor.cs @@ -2,6 +2,7 @@ using AntDesign.ProLayout; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; +using Microsoft.AspNetCore.Mvc; using Microsoft.Identity.Client.Extensions.Msal; using Microsoft.JSInterop; using System.Globalization; @@ -27,14 +28,14 @@ namespace VideoAnalysisRazor.Layouts protected override async Task OnAfterRenderAsync(bool firstRender) + { + } + protected override async Task OnInitializedAsync() { if (!await CheckLogin()) { NavigationManager.NavigateTo("/Login"); } - } - protected override async Task OnInitializedAsync() - { _menuData = [ new MenuDataItem { diff --git a/VideoAnalysis/Components/Pages/EvaluationProject.razor.cs b/VideoAnalysis/Components/Pages/EvaluationProject.razor.cs index 6ffdab0..2aef2b7 100644 --- a/VideoAnalysis/Components/Pages/EvaluationProject.razor.cs +++ b/VideoAnalysis/Components/Pages/EvaluationProject.razor.cs @@ -1,7 +1,6 @@ 锘縰sing AntDesign; using AntDesign.TableModels; using FreeRedis; -using Learn.VideoAnalysis.Controllers.Dto; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using SqlSugar; diff --git a/VideoAnalysis/Components/Pages/NodeSubscriptionPage.razor.cs b/VideoAnalysis/Components/Pages/NodeSubscriptionPage.razor.cs index bc2c7f9..302b79d 100644 --- a/VideoAnalysis/Components/Pages/NodeSubscriptionPage.razor.cs +++ b/VideoAnalysis/Components/Pages/NodeSubscriptionPage.razor.cs @@ -1,7 +1,6 @@ 锘縰sing AntDesign; using AntDesign.TableModels; using FreeRedis; -using Learn.VideoAnalysis.Controllers.Dto; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using SqlSugar; diff --git a/VideoAnalysis/Components/Pages/VideoTaskPage.razor b/VideoAnalysis/Components/Pages/VideoTaskPage.razor index 9917d06..da3b871 100644 --- a/VideoAnalysis/Components/Pages/VideoTaskPage.razor +++ b/VideoAnalysis/Components/Pages/VideoTaskPage.razor @@ -2,7 +2,7 @@ @using AntDesign @using AntDesign.TableModels @using System.ComponentModel.DataAnnotations -@using Learn.VideoAnalysis.Controllers.Dto +@using VideoAnalysisCore.Controllers.Dto @using SqlSugar @using VideoAnalysisCore.Model @using VideoAnalysisCore.Model.Dto diff --git a/VideoAnalysis/Components/Pages/VideoTaskPage.razor.cs b/VideoAnalysis/Components/Pages/VideoTaskPage.razor.cs index 077b3a6..3823d4e 100644 --- a/VideoAnalysis/Components/Pages/VideoTaskPage.razor.cs +++ b/VideoAnalysis/Components/Pages/VideoTaskPage.razor.cs @@ -2,7 +2,6 @@ using AntDesign.TableModels; using FFmpeg.NET.Services; using Learn.VideoAnalysis.API.Expand; -using Learn.VideoAnalysis.Controllers.Dto; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.DataProtection.KeyManagement; @@ -11,6 +10,7 @@ using System.Data.Common; using System.Linq.Expressions; using System.Threading.Tasks; using VideoAnalysisCore.Common; +using VideoAnalysisCore.Controllers.Dto; using VideoAnalysisCore.Model; using VideoAnalysisCore.Model.Dto; using VideoAnalysisCore.Model.Enum; @@ -127,7 +127,7 @@ namespace Learn.VideoAnalysis.Components.Pages return; var data = RedisExpand.Redis.HMGet(RedisExpandKey.Task(item.Id), "Progress", "LastEnum", "StartTime", "ErrorMessage"); - item.Progress = float.Parse(data[0]); + item.Progress = data[0]; item.LastEnum = data[1].ToEnum() ?? default; item.StartTimeDic = System.Text.Json.JsonSerializer.Deserialize>(data[2]) ?? null; item.ErrorMessage = data[3]; diff --git a/VideoAnalysis/Components/Pages/VideoTaskShow.razor.cs b/VideoAnalysis/Components/Pages/VideoTaskShow.razor.cs index 9696e24..65fe980 100644 --- a/VideoAnalysis/Components/Pages/VideoTaskShow.razor.cs +++ b/VideoAnalysis/Components/Pages/VideoTaskShow.razor.cs @@ -1,7 +1,6 @@ 锘縰sing AntDesign; using AntDesign.TableModels; using FFmpeg.NET.Services; -using Learn.VideoAnalysis.Controllers.Dto; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.DataProtection.KeyManagement; diff --git a/VideoAnalysis/Controllers/ApiController.cs b/VideoAnalysis/Controllers/ApiController.cs deleted file mode 100644 index d740eee..0000000 --- a/VideoAnalysis/Controllers/ApiController.cs +++ /dev/null @@ -1,296 +0,0 @@ - -using VideoAnalysisCore.Common; -using Microsoft.AspNetCore.Mvc; -using System.Reflection; -using MapsterMapper; -using Mapster; -using VideoAnalysisCore.AICore.SherpaOnnx; -using UserCenter.Model.Enum; -using VideoAnalysisCore.AICore.GPT.ChatGPT; -using VideoAnalysisCore.AICore.GPT; -using System.Text.Json; -using VideoAnalysisCore.Model.Enum; -using Yitter.IdGenerator; - -namespace Learn.VideoAnalysis.Controllers -{ - [ApiController] - [Route("[controller]/[action]")] - public class ApiController : ControllerBase - { - private readonly ILogger _logger; - private readonly IMapper mp; - private readonly Repository videoTaskDB; - private readonly Repository videoKonwDB; - private readonly IBserGPT chatGPT; - public ApiController(ILogger logger, Repository videoTaskDB, - IMapper mp, IBserGPT chatGPT, Repository videoKonwDB) - { - _logger = logger; - this.videoTaskDB = videoTaskDB; - this.mp = mp; - this.chatGPT = chatGPT; - this.videoKonwDB = videoKonwDB; - } - - private string GetClientIpAddress() - { - // 检查 X-Forwarded-For 请求头 - if (HttpContext.Request.Headers.ContainsKey("X-Forwarded-For") - && !string.IsNullOrEmpty(HttpContext.Request.Headers["X-Forwarded-For"])) - return HttpContext.Request.Headers["X-Forwarded-For"].ToString(); - if (HttpContext.Connection.RemoteIpAddress != null) - return HttpContext.Connection.RemoteIpAddress.ToString(); - throw new Exception("未能获取到客户端ip地址"); - } - - /// - /// 语音识别 - /// - /// 文件流 - /// - [HttpGet(Name = "AudioRecognitionUrl")] - public async Task AudioRecognitionUrl(string url) - { - try - { - using HttpClient client = new HttpClient(); - // 发送GET请求获取网络文件流 - using var networkStream = await client.GetStreamAsync(url); - var res = await SenseVoice.RunTask(networkStream); - return Ok(res); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } - } - /// - /// 语音识别 - /// - /// 文件流 - /// - [HttpPost(Name = "AudioRecognition")] - public async Task AudioRecognition(IFormFile file) - { - using var s = file.OpenReadStream(); - var res = await SenseVoice.RunTask(s); - return Ok(res); - } - - - /// - /// 获取FTS_Data str - /// - /// 路径 - /// - [HttpGet(Name = "fts_data")] - public async Task FTS_Data(string path = "itn_subject_sx.fst") - { - var hotwords = JsonSerializer - .Deserialize(System.IO.File.ReadAllText(Path.Combine(AppCommon.AIModelFile, "Hotwords.json"))); - var res = new List(100); - foreach (var element in hotwords.OrderByDescending(s => s.key.Count())) - foreach (var e in element.v) - res.Add($"""("{e}", "{element.key}")"""); - var pyFile = System.IO.File.ReadAllText(Path.Combine(AppCommon.AIModelFile, "sherpa-onnx-fst.py")); - var resStr = pyFile - .Replace("(fts_data)", "[" + string.Join(',', res) + "]") - .Replace("(path)", path); - return Ok(resStr); - } - - - /// - /// 重新开始执行GPT分析taskId/tagId二选一 - /// - /// - /// 自定义id - /// 切换任务所属学科 null忽略 - /// - [HttpGet(Name = "ReStart")] - public async Task ReStart(long taskId, string? tagId, SubjectEnum? subject) - { - var task = await videoTaskDB.AsQueryable() - .WhereIF(taskId != 0, s => s.Id == taskId) - .WhereIF(!string.IsNullOrEmpty(tagId), s => s.TagId == tagId) - .FirstAsync(); - if (task is null) - return BadRequest("未能找到对应任务"); - if (subject is not null) - { - task.Subject = subject; - await videoTaskDB.UpdateAsync(task); - } - //重新开始执行GPT分析 - RedisExpand.InsertChannel(RedisChannelEnum.ChatModelAnalysis - , task.Id); - return Ok(); - } - - - /// - /// 插入队列 - /// - /// - /// - /// - [HttpPost(Name = "TestInsertChannel")] - public IActionResult TestInsertChannel(int @enum = 1, string msg = "1") - { - RedisExpand.InsertChannel(@enum.ToEnum().Value - , msg); - return Ok(); - } - - /// - /// 视频处理 - /// - /// 请求体 - /// - [HttpPost(Name = "VideoAnalysis")] - public async Task VideoAnalysis(VideoAnalysisReq req) - { - if (!ModelState.IsValid) return BadRequest(ModelState); - - if (await videoTaskDB.IsAnyAsync(s => s.TagId == req.TagId)) - return BadRequest("重复添加"); - // 自动映射属性到哈希 - var task = new VideoTask() - { - Id=YitIdHelper.NextId(), - ComeFrom = GetClientIpAddress(), - MediaUrl = req.MediaUrl, - ApiToken = req.ApiToken, - Type = req.Type, - Subject = req.Subject, - Tag = req.Tag, - TagId = req.TagId, - MediaName = req.Name - }; - //入库 - var hashEntries = task.GetType() - .GetProperties(BindingFlags.Public | BindingFlags.Instance) - .ToDictionary(s => s.Name, s => s.GetValue(task)); - await videoTaskDB.InsertAsync(task); - RedisExpand.Redis.HMSet(RedisExpandKey.Task(task.Id), hashEntries); - RedisExpand.Redis.LPush(RedisExpandKey.ChannelKey, task.Id); - return Ok(task.Id); - } - - - - - - - ///// - ///// 获取视频知识点片段taskId/tagId二选一 - ///// - ///// - ///// 自定义id - ///// - //[HttpGet(Name = "TaskKnowInfo")] - //public async Task TaskKnowInfo(long taskId, string? tagId) - //{ - // if (taskId == 0 && string.IsNullOrEmpty(tagId)) - // return BadRequest(); - // var task = await videoTaskDB.AsQueryable() - // .WhereIF(taskId != 0, s => s.Id == taskId) - // .WhereIF(!string.IsNullOrEmpty(tagId), s => s.TagId == tagId) - // .FirstAsync(); - // if (task is null) - // return BadRequest("无效任务"); - - // var konwArr = await videoKonwDB.AsQueryable() - // .Where(s => s.VideoTaskId == task.Id) - // .ToArrayAsync(); - // if (konwArr is null || konwArr.Length == 0) - // return BadRequest("无效任务"); - // return Ok(new TaskKnowRes() - // { - // TagId = task.TagId, - // Status = task.LastEnum, - // VideoTaskId = task.Id, - // KnowBlockArr = konwArr - // .GroupBy(s => s.StartTime) - // .Select(s => new TaskKnowBlock() - // { - // Id = s.First().Id, - // Content = s.First().Content, - // StartTime = s.First().StartTime, - // EndTime = s.First().EndTime, - // Theme = s.First().Theme, - // Know = s.Select(x => new TaskKnowInfo() - // { - // Id = x.Id, - // KnowPoint = x.KnowPoint, - // KnowPointId = x.KnowPointId - // }).ToArray() - // }).ToArray() - // }); - //} - ///// - ///// 获取视频信息taskId/tagId二选一 - ///// - ///// - ///// 自定义id - ///// - //[HttpGet(Name = "TaskInfo")] - //public async Task TaskInfo(long taskId, string? tagId) - //{ - // if (taskId == 0 && string.IsNullOrEmpty(tagId)) - // return BadRequest(); - // var task = await videoTaskDB.AsQueryable() - // .WhereIF(taskId != 0, s => s.Id == taskId) - // .WhereIF(!string.IsNullOrEmpty(tagId), s => s.TagId == tagId) - // .FirstAsync(); - // if (task is null) - // return BadRequest(); - // var taskData = task.ChatAnalysis.Adapt(); - // if (taskData is null) - // return BadRequest(); - // taskData.Status = task.LastEnum; - // if (task.LastEnum != RedisChannelEnum.EndTask) - // return BadRequest(taskData); - // if (taskData != null && taskData.TimeBase != null) - // taskData.TimeBase = MergeTimeBases(taskData.TimeBase); - // return Ok(taskData); - //} - - - //[NonAction] - //private static List MergeTimeBases(IEnumerable timeBases) - //{ - // if (timeBases == null || timeBases.Count() == 0) - // { - // return new List(); - // } - // var mergedList = new List(); - // // 初始化合并段 - // var current = timeBases.First(); - // current.Content = string.Empty; - // foreach (var next in timeBases) - // { - // // 如果类型相同,则扩展时间段 - // if (current.TimeBaseType == next.TimeBaseType) - // current.End = Math.Max(current.End, next.End); - // else - // { - // // 类型不同,将当前时间段加入结果列表,并开始新时间段 - // mergedList.Add(current); - // current = next; - // current.Content = string.Empty; - // } - // } - // // 添加最后的时间段 - // mergedList.Add(current); - // return mergedList; - //} - - - - - - - } -} diff --git a/VideoAnalysis/Controllers/Dto/ApiDto.cs b/VideoAnalysis/Controllers/Dto/ApiDto.cs deleted file mode 100644 index e57524b..0000000 --- a/VideoAnalysis/Controllers/Dto/ApiDto.cs +++ /dev/null @@ -1,247 +0,0 @@ -锘縰sing AntDesign; -using SqlSugar; -using System.ComponentModel.DataAnnotations; -using UserCenter.Model.Enum; -using VideoAnalysisCore.AICore.GPT.Dto; -using VideoAnalysisCore.Model.Enum; - -namespace Learn.VideoAnalysis.Controllers.Dto -{ - /// - /// 瑙嗛鍒楄〃椤 - /// - public class StructurePageContentAnalyzeItem - { - /// - /// 褰曟挱鍐呭缂栧彿 - /// - public long StructurePageContentId { get; set; } - - /// - /// 绱犳潗ID - /// - public long MaterialId { get; set; } - - /// - /// 瑙嗛缂栫爜 - /// - public string VideoCode { get; set; } - - /// - /// 瑙嗛鏂囦欢鍚嶇О - /// - public string VideoName { get; set; } - /// - /// 鍐呭绫诲瀷 - /// - public AttachmentsInfoType AttachmentsInfoType { get; set; } - } - public class NodePackageReq - { - - /// - /// 褰曟挱缁撴瀯鐩綍鑺傜偣缂栧彿 - /// - [Required(ErrorMessage = "鐩綍鑺傜偣缂栧彿鏄繀濉」")] - public long NodeId { get; set; } - - /// - /// 绉戠洰绫诲瀷 - /// - [Required(ErrorMessage = "绉戠洰绫诲瀷鏄繀濉」")] - public SubjectEnum SubjectType { get; set; } - - /// - /// 浠诲姟绫诲瀷 - /// - [Required(ErrorMessage = "浠诲姟绫诲瀷鏄繀濉」")] - public TaskTypeEnum TaskType { get; set; } - - /// - /// 瑙嗛鍒楄〃 - /// - [Required(ErrorMessage = "鏂囦欢鏁伴噺鏄繀濉」")] - public List AnalyzeItems { get; set; } - - } - /// - /// 瑙嗛澶勭悊 璇锋眰 - /// - public class NodeMonitoringReq - { - /// - /// 濯掍綋璺緞 - /// - [Required(ErrorMessage = "鏂囦欢鑺傜偣ID鏄繀濉」")] - public long NodeId { get; set; } - /// - /// 浠诲姟绫诲瀷 - /// - public TaskTypeEnum? Type { get; set; } - /// - /// 瀛︾绫诲瀷 - /// - public SubjectEnum? Subject { get; set; } - - } - /// - /// 瑙嗛澶勭悊 璇锋眰 - /// - public class VideoAnalysisReq - { - /// - /// 濯掍綋璺緞 - /// - [Required(ErrorMessage = "璧勬簮URL鏄繀濉」")] - public string MediaUrl { get; set; } = string.Empty; - /// - /// 璧勬簮鍚嶇О - /// - [Required(ErrorMessage = "璧勬簮鍚嶇О鏄繀瑕佺殑")] - public string Name { get; set; } = string.Empty; - /// - /// ApiKey - /// - [Required(ErrorMessage = "鎺ュ彛Token鏄繀濉」")] - public string ApiToken { get; set; } = string.Empty; - /// - /// 鍐呭鎵灞炲绉 - /// - public SubjectEnum? Subject { get; set; } - /// - /// 浠诲姟绫诲瀷 - /// - public TaskTypeEnum? Type { get; set; } - /// - /// 鑷畾涔夊 浠诲姟瀹屾垚鍚庨檮甯﹂氱煡 - /// - public string Tag { get; set; } = string.Empty; - /// - /// 鑷畾涔塈d鍙敤浜庝换鍔″畬鎴愪箣鍚庣殑鏌ヨ - /// - public string? TagId { get; set; } - /// - ///鍥炶皟Api鍦板潃 - /// - //[Required(ErrorMessage = "鍥炶皟Api鍦板潃鏄繀濉」")] - //[Url(ErrorMessage = "璇疯緭鍏ユ湁鏁堢殑 URL")] - //public string CallBackUrl { get; set; } = string.Empty; - - } - public class TextValue - { - public TextValue(float v) - { - var s = TimeSpan.FromSeconds((double)v); - var td = new[] { s.Hours, s.Minutes, s.Seconds }; - Text = string.Join(':', td.Where(s => s > 0)); - Value = v; - } - public TextValue(string t,object v) - { - Text = t; - Value = v; - } - public TextValue() - { - - } - public string Text { get; set; } - public object Value { get; set; } - } - public class TaskKnowInfo - { - /// - ///瑙嗛鐗囨鐭ヨ瘑鐐 id - /// - public long Id { get; set; } - /// - /// 鐭ヨ瘑鐐 - /// - public string KnowPoint { get; set; } - /// - /// 鐭ヨ瘑鐐笽D - /// - public string KnowPointId { get; set; } - - } - public class TaskKnowBlock - { - public long Id { get; set; } - - /// - /// 寮濮嬫椂闂 - /// - public float? StartTime { get; set; } - /// - /// 缁撴潫鏃堕棿 - /// - public float? EndTime { get; set; } - /// - /// 鎸佺画鏃堕棿 - /// - [SugarColumn(IsIgnore = true)] - public float? KeepTime => (EndTime ?? 0) - StartTime ?? 0; - /// - /// 涓婚 - /// - public string? Theme { get; set; } - /// - /// 鍐呭鎬荤粨 - /// - public string? Content { get; set; } - /// - /// 鐭ヨ瘑鐐瑰垪琛 - /// - public TaskKnowInfo[] Know { get; set; } - } - /// - /// 瑙嗛鐗囨鐭ヨ瘑鐐圭粨鏋 - /// - public class TaskKnowRes - { - /// - /// 鑷畾涔塈d [浠诲姟瑙嗛鑷畾涔塱d] - /// - /// - public string? TagId { get; set; } - /// - /// 浠诲姟褰撳墠鎵ц鐘舵 - /// - public RedisChannelEnum Status { get; set; } - /// - /// 瑙嗛浠诲姟id - /// - public long VideoTaskId { get; set; } - /// - /// 瑙嗛鐭ヨ瘑蹇 - /// - public TaskKnowBlock[] KnowBlockArr { get; set; } - - } - public class TaskInfoRes: TaskRes - { - public TaskInfoRes() - { - - } - /// - /// 浠诲姟褰撳墠鎵ц鐘舵 - /// - public RedisChannelEnum Status { get; set; } - ///// - ///// 鏃堕棿杞寸姸鎬佹灇涓 - ///// - //public Dictionary TimeTypeEnum => - // Enum.GetValues(typeof(TimeBaseTypeEnum)) - // .Cast() - // .ToDictionary(x => (int)x, x => x.ToString()); - - ///// - ///// 鏃堕棿杞村悎璁 - ///// - //public Dictionary? TimeBaseTotal => - // TimeBase?.GroupBy(s => s.TimeBaseType??TimeBaseTypeEnum.鏁欏笀璁叉巿)? - // .ToDictionary(s => s.Key, s => new TextValue(s.Sum(x => x.End - x.Start))); - } -} diff --git a/VideoAnalysis/GlobalUsings.cs b/VideoAnalysis/GlobalUsings.cs index db8f5ee..1278b55 100644 --- a/VideoAnalysis/GlobalUsings.cs +++ b/VideoAnalysis/GlobalUsings.cs @@ -3,4 +3,3 @@ global using VideoAnalysisCore.Model; global using VideoAnalysisCore.Model.Dto; global using VideoAnalysisCore.Model.Enum; -global using Learn.VideoAnalysis.Controllers.Dto; \ No newline at end of file diff --git a/VideoAnalysis/Program.cs b/VideoAnalysis/Program.cs index acf33d7..38c041a 100644 --- a/VideoAnalysis/Program.cs +++ b/VideoAnalysis/Program.cs @@ -11,6 +11,10 @@ using VideoAnalysisCore.AICore.GPT.DeepSeek; using Microsoft.Extensions.DependencyInjection; using VideoAnalysisCore.Common.Expand; using Learn.VideoAnalysis.Expand; +using Microsoft.AspNetCore.Mvc.Formatters; +using System.Security.Cryptography; +using System.Diagnostics; +using VideoAnalysisCore.AICore.FFMPGE; @@ -20,6 +24,7 @@ namespace Learn.VideoAnalysis { public static void Main(string[] args) { + var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorComponents() @@ -46,7 +51,9 @@ namespace Learn.VideoAnalysis Description = "教学视频分析平台v1" }); var file = Path.Combine(AppContext.BaseDirectory, "Learn.VideoAnalysis.xml"); // xml文档绝对路径 + var file1 = Path.Combine(AppContext.BaseDirectory, "VideoAnalysisCore.xml"); // xml文档绝对路径 c.IncludeXmlComments(file, true); // true : 显示控制器层注释 + c.IncludeXmlComments(file1, true); // true : 显示控制器层注释 c.OrderActionsBy(o => o.RelativePath); // 对action的名称进行排序,如果有多个,就可以看见效果了。 }); @@ -95,7 +102,7 @@ namespace Learn.VideoAnalysis //builder.Services.AddSingleton(); //builder.Services.AddSingleton(); builder.Services.AddSingleton(); - + var app = builder.Build(); @@ -120,8 +127,8 @@ namespace Learn.VideoAnalysis app.MapRazorComponents() .AddInteractiveServerRenderMode(); - //.AddInteractiveWebAssemblyRenderMode() - //.AddAdditionalAssemblies(typeof(VideoAnalysisRazor._Imports).Assembly); + //.AddInteractiveWebAssemblyRenderMode() + //.AddAdditionalAssemblies(typeof(VideoAnalysisRazor._Imports).Assembly); app.MapControllers(); diff --git a/VideoAnalysisCore/AICore/FFMPGE/FFMPGEHandle.cs b/VideoAnalysisCore/AICore/FFMPGE/FFMPGEHandle.cs index cf92404..e673c20 100644 --- a/VideoAnalysisCore/AICore/FFMPGE/FFMPGEHandle.cs +++ b/VideoAnalysisCore/AICore/FFMPGE/FFMPGEHandle.cs @@ -9,6 +9,10 @@ using System.Runtime.InteropServices; using SqlSugar.IOC; using VideoAnalysisCore.Model; using VideoAnalysisCore.Model.Enum; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System.Text.Json; namespace VideoAnalysisCore.AICore.FFMPGE { @@ -24,7 +28,110 @@ namespace VideoAnalysisCore.AICore.FFMPGE ? $"/usr/bin/ffmpeg" : Path.Combine(AppCommon.AIModelFile, "ffmpeg.exe"); - public static string Task = string.Empty; + /// + /// 璇嗗埆瑙嗛鍏抽敭甯 + /// + /// 浠诲姟id + /// + public static async Task VideoKeyFrames(string task) + { + var taskID = long.Parse(task); + //闂撮殧绉 + var intervalSec = 5; + var threshold = 15.0; + var PPTVideoCode = await DbScoped.Sugar + .Queryable() + .Where(s => s.Id == long.Parse(task)) + .Select(s => s.PPTVideoCode).FirstAsync(); + if (string.IsNullOrEmpty(PPTVideoCode)) return; + //瑙嗛鍒囧抚 + var localPath = task.LocalPath(); + var filePath = Path.Combine(localPath, "ppt.mp4"); + if (!File.Exists(filePath)) + throw new Exception("瀛樺湪PPTCOde浣嗘湭鑳芥壘鍒板搴旇祫婧愭枃浠"); + var ffmpeg = new Engine(FFmpegPath); + var cToken = new CancellationToken(); + await ffmpeg.ExecuteAsync($"-i {filePath} -vf \"fps=1/{intervalSec},scale=320:180\" {localPath}/frame_%03d.jpg", cToken); + + //瑙嗛鍏抽敭甯у垎鏋 + var frameFiles = Directory.GetFiles(localPath, "*.jpg") + .OrderBy(f => f) + .ToList(); + + Image prevFrame = null; + string outputDir = "output"; + Directory.CreateDirectory(outputDir); + var keyFrames = new List(); + foreach (var frameFile in frameFiles) + { + using (var currFrame = Image.Load(frameFile)) + { + if (prevFrame != null) + { + double diff = CalculateFrameDifference(prevFrame, currFrame); + double timestamp = GetTimestampFromFileName(frameFile) * intervalSec; + + if (diff > threshold) + { + keyFrames.Add((int)timestamp); + //string outputPath = Path.Combine(outputDir, $"change_{timestamp:0000}.jpg"); + //currFrame.Save(outputPath); + //Console.WriteLine($"鍙樺寲甯: {timestamp}绉掞紝宸紓鍊: {diff:F2}"); + } + } + prevFrame?.Dispose(); + prevFrame = currFrame.Clone(); + } + } + //鍐欏叆鏁版嵁搴 + var keyFramStr = JsonSerializer.Serialize(keyFrames); + await DbScoped.Sugar + .Updateable() + .SetColumns(it => it.PPTKeyFrame == keyFramStr) + .Where(it => it.Id == taskID) + .ExecuteCommandAsync(); + + } + /// + /// 璁$畻甯у樊寮 + /// + /// + /// + /// + static double CalculateFrameDifference(Image img1, Image img2) + { + // 缁熶竴璋冩暣涓64x64 + var resized1 = img1.Clone(x => x.Grayscale()); + var resized2 = img2.Clone(x => x.Grayscale()); + + long diff = 0; + for (int y = 0; y < resized1.Height; y++) + { + for (int x = 0; x < resized1.Width; x++) + { + var pixel1 = resized1[x, y]; + var pixel2 = resized2[x, y]; + diff += Math.Abs(pixel1.R - pixel2.R); + } + } + return diff / (double)(resized1.Width * resized1.Height); + } + + static double GetTimestampFromFileName(string filePath) + { + string fileName = Path.GetFileNameWithoutExtension(filePath); + return double.Parse(fileName.Split('_')[1]); + } + /// + /// 鎵ц瑙嗛FFMPEG澶勭悊浠诲姟 + /// + /// + /// + public static async Task RunAsync(string task) + { + await VideoKeyFrames(task); + await Audio2WAV16KAsync(task); + } /// /// 闊抽杞爜涓 wav_16k /// @@ -32,7 +139,6 @@ namespace VideoAnalysisCore.AICore.FFMPGE /// public static async Task Audio2WAV16KAsync(string task) { - Task = task; var filePath = await DbScoped.Sugar .Queryable() .Where(s => s.Id == long.Parse(task)) @@ -45,9 +151,6 @@ namespace VideoAnalysisCore.AICore.FFMPGE var outputFile = new OutputFile(Path.Combine(task.LocalPath(), Path.GetFileNameWithoutExtension(filePath) + ".wav")); var ffmpeg = new Engine(FFmpegPath); - //ffmpeg.Progress += OnProgress; - //ffmpeg.Data += OnData; - ffmpeg.Complete += OnComplete; ffmpeg.Error += (sender, e) => { var ee = new Exception($"闊抽杞爜鍑虹幇寮傚父 \r\n[{e.Input.Name} => {e.Output.Name}]: 閿欒: {e.Exception.Message}"); @@ -62,31 +165,12 @@ namespace VideoAnalysisCore.AICore.FFMPGE //?string.Empty //: $"-f segment -reset_timestamps 1 -segment_time {AppCommon.AppSetting.FFmpeg.TimeSlice}") }; - var res = await ffmpeg.ConvertAsync(inputFile, outputFile, conversionOptions); - } - private static void OnProgress(object sender, ConversionProgressEventArgs e) - { - Console.WriteLine("[{0} => {1}]", e.Input.MetaData.FileInfo.Name, e.Output.Name); - Console.WriteLine("姣旂壒鐜: {0}", e.Bitrate); - Console.WriteLine("Fps: {0}", e.Fps); - Console.WriteLine("鍩烘湰妗嗘灦: {0}", e.Frame); - Console.WriteLine("澶勭悊鎸佺画鏃堕棿: {0}", e.ProcessedDuration); - Console.WriteLine("Size: {0} kb", e.SizeKb); - Console.WriteLine("鎬绘寔缁椂闂: {0}\n", e.TotalDuration); - } - - private static void OnData(object sender, ConversionDataEventArgs e) - { - Console.WriteLine(e.Data); - } - - private static void OnComplete(object sender, ConversionCompleteEventArgs e) - { - Console.WriteLine("杞崲瀹屾垚=>" + e.Output.Name); + Console.WriteLine($"{DateTime.Now}=>闊抽杞爜瀹屾垚"); //鍔犲叆涓嬩竴闃熷垪 - RedisExpand.InsertChannel(RedisChannelEnum.ParsingCaptions, Task); + RedisExpand.InsertChannel(RedisChannelEnum.ParsingCaptions, task); } + } } diff --git a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs index b99dd82..7986995 100644 --- a/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs +++ b/VideoAnalysisCore/AICore/GPT/DeepSeek/DeepSeek_GPT.cs @@ -46,7 +46,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek /// /// 浠诲姟id /// - public async Task GetKnow(string task) + public async Task GetKnow1(string task) { var taskId = long.Parse(task); var taskInfo = await videoTaskDB.AsQueryable() @@ -136,11 +136,14 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek var thems = JsonSerializer.Serialize(questionRes.Adapt());// string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme)); var checkResFormat1 = """[{"StartTime":寮濮嬬(number),"KnowPoint":鐭ヨ瘑鐐瑰悕绉(string),"KnowPointId":鐭ヨ瘑鐐笽d(string)}]"""; var knowMessages = - $"鎴戦拡瀵硅棰<{title}>鍒嗘瀽鍑轰簡涓浜涜棰戠殑鐭ヨ瘑鐗囨,鐜板湪闇瑕佷綘甯垜灏嗘瘡涓墖娈靛垎閰嶆伆褰撶殑鐭ヨ瘑鐐(鍗曚釜鐗囨鍏佽澶氫釜鐭ヨ瘑鐐圭敤閫楀彿','鍒嗗壊)銆" + + $"鎴戦拡瀵箋subject}璇惧爞鎺堣瑙嗛鍒嗘瀽鍑轰簡瑙嗛鐨勬巿璇鹃樁娈电墖娈点" + + $"鐜板湪闇瑕佷綘閫氳繃姣忎釜鐗囨鐨勫唴瀹规荤粨鏉ュ垎閰嶆纭殑鐭ヨ瘑鐐(鍗曚釜鐗囨鍏佽澶氫釜鐭ヨ瘑鐐圭敤閫楀彿','鍒嗗壊)銆" + $"杩欐槸鎴戠殑鍒嗘 {thems}銆" + - $"鎻愪緵鐨勭煡璇嗙偣鍚嶇О({knows})銆 鏍煎紡 (鏂规硶鐐笽d|鏂规硶鐐瑰悕绉) " + - $"鏈鍚庤纭繚鍒嗛厤鐨勭煡璇嗙偣鏄敤鎴锋彁渚涚殑,鍚﹀垯鐗囨鐭ヨ瘑鐐瑰肩暀绌!銆" + - $"杈撳嚭鍐呭鍙繑鍥瀓son鏍煎紡({checkResFormat1})"; + $"璇惧爞鍐呭涓巤fileNameInfoRes.鎺堣绔犺妭}绔犺妭鐩稿叧" + + $"鏈鍚庤纭繚鍒嗛厤鐨勭煡璇嗙偣鏄敤鎴锋彁渚涚殑,骞朵笖涓瀹氭纭悎鐞!" + + $"杈撳嚭鍐呭鍙繑鍥瀓son鏍煎紡({checkResFormat1})" + + $" 鏍煎紡 (鏂规硶鐐笽d|鏂规硶鐐瑰悕绉) " + + $"鎻愪緵鐨勭煡璇嗙偣鍚嶇О({knows})銆"; Console.WriteLine(DateTime.Now + "=>2.寮濮嬪垎鏋愯棰戝唴瀹圭煡璇嗙偣"); var konwRes = await ChatAsync(task, knowMessages, null); @@ -220,6 +223,428 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task); return gptRes; } + + + + + /// + /// 鑾峰彇鐭ヨ瘑鐐 + /// + /// 浠诲姟id + /// + public async Task GetKnow_v1(string task) + { + var taskId = long.Parse(task); + var taskInfo = await videoTaskDB.AsQueryable() + .Where(s => s.Id == taskId) + .FirstAsync(); + var subject = "鏁板"; + var Course_Id = 27; + switch (taskInfo.Type)//澶勭悊涓嶅悓浠诲姟绫诲瀷鐨勭煡璇嗙偣鏍 + { + case TaskTypeEnum.钃濋哺鏅哄簱_涓亴瑙嗛鍒嗘: + Course_Id = 51; + break; + case TaskTypeEnum.钃濋哺鏅哄簱_瑙嗛鍒嗘: + default: + Course_Id = 27; + break; + } + var xkwKnows = await knowledgeInfoDB.AsQueryable() + .Where(s => s.Course_Id == Course_Id + && (s.Depth == 3 + || s.Depth == 2)) + .Select(s => s.Name).ToArrayAsync(); + string title = taskInfo.MediaName; + var speakerArr = JsonSerializer.Deserialize(taskInfo.Speaker); + var captionsArr = JsonSerializer.Deserialize(taskInfo.Captions); + + var fileNameResFormat = "{鎺堣绔犺妭: string|null}"; + //var fileNamePostMessages = title + + // " 杩欐槸涓鍫傝鐨勬爣棰,璇蜂綘鍩轰簬鏍囬甯垜鍒嗘瀽鍑鸿繖鍫傝鎵璁叉巿鐨勫唴瀹逛笌鏈鎭板綋鐨勬巿璇剧珷鑺(鍏宠仈鏈璐村垏鐨勭珷鑺,淇濈暀涓涓珷鑺!)." + + // $"绔犺妭鑼冨洿闄愬畾鍦╗{string.Join(',', xkwKnows)}]鑼冨洿鍐." + + // $"杈撳嚭鏍煎紡 json瀛楃涓 瀵硅薄鏍煎紡{fileNameResFormat}"; + var rCaptionArr = string.Join(',', captionsArr + .Where((s, i) => i % 3 == 0) + .Take((int)(captionsArr?.Length ?? 0 / 2.2)) + .Select(s => s.Text)); + + var fileNamePostMessages = + "杩欐槸涓鍫傝鐨勯儴鍒嗘巿璇惧瓧骞,璇蜂綘鍩轰簬瀛楀箷鍐呭甯垜鍒嗘瀽鍑鸿繖鍫傝鎵璁叉巿鐨勫唴瀹逛笌鏈鎭板綋鐨勬巿璇剧珷鑺(鍏宠仈鏈璐村垏鐨勭珷鑺,淇濈暀涓涓珷鑺!)." + + $"绔犺妭鑼冨洿闄愬畾鍦╗{string.Join(',', xkwKnows)}]鑼冨洿鍐." + + $"浠ヤ笅鏄寘鍚椂闂寸殑瑙嗛瀛楀箷鏂囨湰銆" + + $"瀛楀箷鍒楄〃 {rCaptionArr}銆" + + $"杈撳嚭鏍煎紡 json瀛楃涓 瀵硅薄鏍煎紡{fileNameResFormat}"; + + var fileNameInfoRes = await ChatAsync + (task, fileNamePostMessages, null); +#if DEBUG + fileNameInfoRes = new FileNameInfo() { 鎺堣绔犺妭 = "涓鍏冧簩娆′笉绛夊紡" }; +#endif + var captions = ExpandFunction.GetSpeakerCaptions(captionsArr, speakerArr); + var maxVideoTime = captions?.TimeBase?.LastOrDefault()?.End ?? 0; + var criteriaBuilder = new StringBuilder(); + var know = await knowledgeInfoDB.GetFirstAsync(s => s.Course_Id == Course_Id && s.Name == fileNameInfoRes.鎺堣绔犺妭); + if (know is null) + throw new Exception("鏈兘鎵惧埌瀵瑰簲鐭ヨ瘑鐐=>" + fileNameInfoRes.鎺堣绔犺妭); + + await RedisExpand.Redis + .HMSetAsync(RedisExpandKey.Task(task), "瀛︾绔犺妭", fileNameInfoRes.鎺堣绔犺妭); + //鎻愬崌鍒扮埗绾 + var kInfo = await knowledgeInfoDB.GetByIdAsync(know.Parent_Id); + var knowledgeInfos = await knowledgeInfoDB.AsQueryable().ToChildListAsync(s => s.Parent_Id, kInfo.Parent_Id == 0 ? kInfo.Id : kInfo.Parent_Id); + var knows = string.Join(',', knowledgeInfos.Select(s => s.Id + "|" + s.Name)); + var knowDic = knowledgeInfos + .OrderBy(s => s.Id) + .GroupBy(s => s.Name) + .ToDictionary(s => s.First().Name, s => s.First().Id); + var questionRes = new List(); + while (true) + { + questionRes = new List(); + var lastTime = 0; + var endTime = 0; + var timeSpan = 700; + while (endTime + 60< maxVideoTime) + { + try + { + endTime = lastTime + timeSpan; + var nowCaptionStr = string.Join('|', captionsArr + .Where(s => s.Text != "銆") + .Where((s, i) => s.Start > lastTime && s.End < endTime) + .Select(s => s.Start + ":" + s.Text)); + var resFormat = """[{"StartTime":寮濮嬬(number),"EndTime":缁撴潫绉(number),"Stage":闃舵(string),"Theme":涓婚(string),"Content":鍐呭鎬荤粨(string)}]"""; + var postMessages = + $"浣犵殑浠诲姟鏄垎鏋愬嚭涓浗{subject}璇惧爞鏍囧噯娴佺▼鐨勬巿璇鹃樁娈垫櫤鑳藉垎娈" + + $"璇惧爞鍐呭涓巤fileNameInfoRes.鎺堣绔犺妭}绔犺妭鐩稿叧" + + $"璇惧爞鏍囧噯娴佺▼鍖呭惈浠ヤ笅7涓樁娈碉細璇剧▼寮曞叆/鏂扮煡璁茶В/渚嬮绮捐/璇惧爞缁冧範/浜掑姩璁ㄨ/璇剧▼鎬荤粨/浣滀笟甯冪疆銆" + + $"閫氳繃鏃堕棿娈电殑涓昏璁茶В鍐呭鍒嗘瀽鍑哄搴旂殑鏃堕棿娈靛唴瀹规荤粨銆" + + $"閫氳繃鐢熸垚鐨勫唴瀹规荤粨鍒嗘瀽鍑哄搴旂殑鏃堕棿娈典富棰樸 " + + $"鏈鍙垎鏋愪竴涓樁娈(浼樺厛鎸夌収闃舵椤哄簭 渚嬪 璇剧▼寮曞叆浼樺厛)銆 " + + $"鏈鍚庤妫鏌ユ煇浜涙椂闂存鐨勬椂甯 濡傛灉瓒呭嚭800绉掓垨鑰呬綆浜30绉掑垯涓嶇鍚堟潯浠!" + + $"杈撳嚭鍐呭鍙繑鍥瀓son鏍煎紡({resFormat})" + + $"瀛楀箷鏍煎紡(寮濮嬬:鍐呭|涓嬩竴娈靛瓧骞).浠ヤ笅鏄寘鍚椂闂寸殑閮ㄥ垎瑙嗛瀛楀箷鏂囨湰銆" + + $"瀛楀箷鍒楄〃 {nowCaptionStr} 瀛楀箷缁撴潫!"; + + Console.WriteLine(DateTime.Now + $"=>1.寮濮嬪垎鏋愯棰戝唴瀹 {lastTime}~{endTime}"); + questionRes.AddRange(await ChatAsync(task, postMessages, null)); + lastTime = (int)questionRes.Last().EndTime.Value - (lastTime==0?0: 30); + } + catch (Exception ex) + { + Console.WriteLine(DateTime.Now + $"=>鍒嗘瀽瑙嗛鍐呭澶辫触 {lastTime}~{endTime}"); + Console.WriteLine(DateTime.Now + ex.Message); + Console.WriteLine(DateTime.Now + ex.StackTrace); + endTime = lastTime - timeSpan; + + } + } + questionRes = questionRes.OrderBy(s => s.StartTime).ToList(); + + var thems = JsonSerializer.Serialize(questionRes.Adapt());// string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme)); + var checkResFormat1 = """[{"StartTime":寮濮嬬(number),"KnowPoint":鐭ヨ瘑鐐瑰悕绉(string),"KnowPointId":鐭ヨ瘑鐐笽d(string)}]"""; + var knowMessages = + $"鎴戦拡瀵箋subject}璇惧爞鎺堣瑙嗛鍒嗘瀽鍑轰簡瑙嗛鐨勬巿璇鹃樁娈电墖娈点" + + $"鐜板湪闇瑕佷綘閫氳繃姣忎釜鐗囨鐨勫唴瀹规荤粨鏉ュ垎閰嶆纭殑鐭ヨ瘑鐐(鍗曚釜鐗囨鍏佽澶氫釜鐭ヨ瘑鐐圭敤閫楀彿','鍒嗗壊)銆" + + $"杩欐槸鎴戠殑鍒嗘 {thems}銆" + + $"璇惧爞鍐呭涓巤fileNameInfoRes.鎺堣绔犺妭}绔犺妭鐩稿叧" + + $"鏈鍚庤纭繚鍒嗛厤鐨勭煡璇嗙偣鏄敤鎴锋彁渚涚殑,骞朵笖涓瀹氭纭悎鐞!" + + $"杈撳嚭鍐呭鍙繑鍥瀓son鏍煎紡({checkResFormat1})" + + $" 鏍煎紡 (鏂规硶鐐笽d|鏂规硶鐐瑰悕绉) " + + $"鎻愪緵鐨勭煡璇嗙偣鍚嶇О({knows})銆"; + Console.WriteLine(DateTime.Now + "=>2.寮濮嬪垎鏋愯棰戝唴瀹圭煡璇嗙偣"); + var konwRes = await ChatAsync(task, knowMessages, null); + + for (int i = 0; i < konwRes.Count(); i++) + questionRes[i].KnowPoint = konwRes[i].KnowPoint; + //for (int i = 0; i < questionRes.Length; i++) + //{ + // var item = questionRes[i]; + // if (i == questionRes.Length - 1) + // item.EndTime = maxVideoTime; + // else + // item.EndTime = (int)(questionRes[i + 1]?.StartTime ?? 0) - 1; + //} + thems = JsonSerializer.Serialize(questionRes.Adapt()); + var checkResFormat = """{"Score":鎵撳垎(number),"Evaluation":璇勪环(string)""";//,"Data":浼樺寲鍚庣殑鍒嗘(array)}"""; + var checkMessage = "鎴戜负瑙嗛鐨勮瑙e唴瀹瑰仛浜嗕竴浜涘垎娈,甯屾湜浣犺兘閫氳瀛楀箷鍐呭鍚庢鏌ヤ笅鐨勫垎娈垫槸鍚︾鍚堟垜鐨勮姹?" + + $"妫鏌ヨ繖浜涘垎娈电殑鏃堕棿鏄惁鍚堢悊 涓庣浉閭荤殑鏃堕棿娈甸棿闅旀槸鍚﹀浜庡悎鐞嗗尯闂30~900绉掍箣闂?" + + $"鍒嗘鐨勪富棰樺唴瀹,鐭ヨ瘑鐐瑰垎閰嶆槸鍚﹀悎鐞嗙鍚堝疄闄呭悧?" + + $"璇风粰鍑轰綘鐨勬墦鍒(0-100,70鍒嗗強鏍)浠ュ強鎵撳垎鍘熷洜銆" + + $"杩欐槸鎴戠殑鍒嗘 {thems}銆" + + $"鍚庣画鐨勫唴瀹规槸鍖呭惈鏃堕棿鎴崇殑瑙嗛瀛楀箷鐨勫浐瀹氭牸寮忔枃鏈" + + $"瀛楀箷鏍煎紡(璇磋瘽浜:寮濮嬬:缁撴潫绉:鍐呭|涓嬩竴娈靛瓧骞).浠ヤ笅鏄寘鍚椂闂寸殑瑙嗛瀛楀箷鏂囨湰銆傚瓧骞曞垪琛 {captions.Captions}銆" + + $"鏈鍚庤緭鍑烘牸寮忎负json({checkResFormat})"; + + Console.WriteLine(DateTime.Now + "=>3.寮濮嬫鏌ヨ棰戝垎娈电粨鏋"); + var checkRes = await ChatAsync(task, checkMessage, null); + if (checkRes != null && checkRes.Score >= 80) + { + break; + } + else + { + Console.WriteLine(DateTime.Now + $"=>{task} 寰楀垎杩囦綆/鍒嗘闀垮害涓嶅尮閰 寰楀垎{checkRes?.Score} "); + Console.WriteLine(checkRes.Evaluation); + Console.WriteLine(); + } + if (questionRes.Any(s => s.KeepTime < 30)) + { + Console.WriteLine(DateTime.Now + "=>瑙嗛鍒嗘杩囩煭!! 閲嶆柊杩涜AI鍒嗘瀽"); + continue; + } + + } + + //todo 鏈寘鍚殑鐭ヨ瘑鐐圭墖娈 濡備綍澶勭悊 + var insertData = questionRes + .Where(s => !string.IsNullOrEmpty(s.KnowPoint)) + .SelectMany( + s => + { + var ks = s.KnowPoint.Split(",").Distinct(); + return ks.Where(x => knowDic.ContainsKey(x)) + .Select(x => new VideoKonwPoint() + { + Content = s.Content, + Theme = s.Theme, + StartTime = s.StartTime, + EndTime = s.EndTime, + KnowPoint = x, + KnowPointId = knowDic[x].ToString(), + TagId = taskInfo.TagId, + VideoTaskId = taskInfo.Id, + }); + }).ToList(); + await videoKonwPointDB.DeleteAsync(s => s.VideoTaskId == taskId); + await videoKonwPointDB.InsertRangeAsync(insertData); + + + + + await RedisExpand.Redis + .HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes); + + var gptRes = new TaskRes(captions); + await RedisExpand.Redis + .HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes); + RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task); + return gptRes; + } + + + /// + /// 鑾峰彇鐭ヨ瘑鐐 + /// + /// 浠诲姟id + /// + public async Task GetKnow(string task) + { + var taskId = long.Parse(task); + var taskInfo = await videoTaskDB.AsQueryable() + .Where(s => s.Id == taskId) + .FirstAsync(); + var subject = "鏁板"; + var Course_Id = 27; + switch (taskInfo.Type)//澶勭悊涓嶅悓浠诲姟绫诲瀷鐨勭煡璇嗙偣鏍 + { + case TaskTypeEnum.钃濋哺鏅哄簱_涓亴瑙嗛鍒嗘: + Course_Id = 51; + break; + case TaskTypeEnum.钃濋哺鏅哄簱_瑙嗛鍒嗘: + default: + Course_Id = 27; + break; + } + var xkwKnows = await knowledgeInfoDB.AsQueryable() + .Where(s => s.Course_Id == Course_Id + && (s.Depth == 3 + || s.Depth == 2)) + .Select(s => s.Name).ToArrayAsync(); + string title = taskInfo.MediaName; + var speakerArr = JsonSerializer.Deserialize(taskInfo.Speaker); + var captionsArr = JsonSerializer.Deserialize(taskInfo.Captions); + + var fileNameResFormat = "{鎺堣绔犺妭: string|null}"; + var rCaptionArr = string.Join(',', captionsArr + .Where((s, i) => i % 3 == 0) + .Take((int)(captionsArr?.Length ?? 0 / 2.2)) + .Select(s => s.Text)); + + var fileNamePostMessages = + "杩欐槸涓鍫傝鐨勯儴鍒嗘巿璇惧瓧骞,璇蜂綘鍩轰簬瀛楀箷鍐呭甯垜鍒嗘瀽鍑鸿繖鍫傝鎵璁叉巿鐨勫唴瀹逛笌鏈鎭板綋鐨勬巿璇剧珷鑺(鍏宠仈鏈璐村垏鐨勭珷鑺,淇濈暀涓涓珷鑺!)." + + $"绔犺妭鑼冨洿闄愬畾鍦╗{string.Join(',', xkwKnows)}]鑼冨洿鍐." + + $"浠ヤ笅鏄寘鍚椂闂寸殑瑙嗛瀛楀箷鏂囨湰銆" + + $"瀛楀箷鍒楄〃 {rCaptionArr}銆" + + $"杈撳嚭鏍煎紡 json瀛楃涓 瀵硅薄鏍煎紡{fileNameResFormat}"; + + var fileNameInfoRes = await ChatAsync + (task, fileNamePostMessages, null); +#if DEBUG + fileNameInfoRes = new FileNameInfo() { 鎺堣绔犺妭 = "涓鍏冧簩娆′笉绛夊紡" }; +#endif + var captions = ExpandFunction.GetSpeakerCaptions(captionsArr, speakerArr); + var maxVideoTime = captions?.TimeBase?.LastOrDefault()?.End ?? 0; + var criteriaBuilder = new StringBuilder(); + var know = await knowledgeInfoDB.GetFirstAsync(s => s.Course_Id == Course_Id && s.Name == fileNameInfoRes.鎺堣绔犺妭); + if (know is null) + throw new Exception("鏈兘鎵惧埌瀵瑰簲鐭ヨ瘑鐐=>" + fileNameInfoRes.鎺堣绔犺妭); + + await RedisExpand.Redis + .HMSetAsync(RedisExpandKey.Task(task), "瀛︾绔犺妭", fileNameInfoRes.鎺堣绔犺妭); + //鎻愬崌鍒扮埗绾 + var kInfo = await knowledgeInfoDB.GetByIdAsync(know.Parent_Id); + var knowledgeInfos = await knowledgeInfoDB.AsQueryable().ToChildListAsync(s => s.Parent_Id, kInfo.Parent_Id == 0 ? kInfo.Id : kInfo.Parent_Id); + var knows = string.Join(',', knowledgeInfos.Select(s => s.Id + "|" + s.Name)); + var knowDic = knowledgeInfos + .OrderBy(s => s.Id) + .GroupBy(s => s.Name) + .ToDictionary(s => s.First().Name, s => s.First().Id); + var questionRes = new List(); + while (true) + { + questionRes = new List(); + var lastTime = 0; + var endTime = 0; + var timeSpan =(int)(maxVideoTime * 0.5); + while (endTime + 60 < maxVideoTime) + { + try + { + endTime = lastTime + timeSpan; + var nowCaptionStr = string.Join('|', captionsArr + .Where(s => s.Text != "銆") + .Where((s, i) => s.Start > lastTime && s.End < endTime) + .Select(s => s.Start + ":" + s.Text)); + var keyFrameArr = string.IsNullOrEmpty(taskInfo?.PPTVideoCode) + ?string.Empty + : $"閫氳繃鍒嗘瀽瑙嗛鍥惧儚寰楀埌浜嗚棰戞巿璇惧唴瀹瑰彂鐢熶簡鍙樺寲鐨勬椂闂磋妭鐐箋taskInfo.PPTKeyFrame},鎺堣闃舵搴斿綋鍦ㄩ檮杩戞椂闂村彂鐢熷彉鍖栥" ; + var resFormat = """[{"StartTime":寮濮嬬(number),"EndTime":缁撴潫绉(number),"Stage":闃舵(string),"Theme":涓婚(string),"Content":鍐呭鎬荤粨(string)}]"""; + var postMessages = + $"璇烽氳繃瑙嗛瀛楀箷鍐呭鍒嗘瀽鍑鸿棰戜腑{subject}璇惧爞鐨勬巿璇鹃樁娈点" + + $"璇惧爞鍐呭涓巤fileNameInfoRes.鎺堣绔犺妭}绔犺妭鐩稿叧" + + $"{keyFrameArr}" + + $"瀹屾暣鐨勮鍫傛爣鍑嗘祦绋嬪寘鍚互涓5涓樁娈碉細璇剧▼寮曞叆/鏂扮煡璁茶В/渚嬮绮捐/璇惧爞缁冧範/璇剧▼鎬荤粨銆" + + $"閫氳繃鎺堣闃舵鐨勪富瑕佽瑙e唴瀹瑰垎鏋愬嚭瀵瑰簲鐨勬巿璇鹃樁娈靛唴瀹规荤粨銆" + + $"閫氳繃鐢熸垚鐨勫唴瀹规荤粨鍒嗘瀽鍑哄搴旂殑鎺堣闃舵涓婚銆 " + + $"璇锋敞鎰 鏈鍒嗘瀽鐨勮棰戝瓧骞曞彧鏄叾涓竴閮ㄥ垎 涓嶉渶瑕佸垎鏋愬嚭鎵鏈夌被鍨嬬殑鎺堣闃舵銆" + + $"鏈鍚庤妫鏌ユ瘡涓巿璇鹃樁娈电殑鏃堕暱,涓嶅厑璁稿嚭鐜拌秴鍑800绉掓垨鑰呬綆浜50绉掔殑鎺堣闃舵銆" + + $"杈撳嚭鍐呭鍙繑鍥瀓son鏍煎紡({resFormat})" + + $"瀛楀箷鏍煎紡(寮濮嬬:鍐呭|涓嬩竴娈靛瓧骞).浠ヤ笅鏄寘鍚椂闂寸殑瑙嗛瀛楀箷鏂囨湰銆" + + $"瀛楀箷鍒楄〃 {nowCaptionStr} 瀛楀箷缁撴潫!"; + + Console.WriteLine(DateTime.Now + $"=>1.寮濮嬪垎鏋愯棰戝唴瀹 {lastTime}~{endTime}"); + questionRes.AddRange(await ChatAsync(task, postMessages, null)); + lastTime = (int)questionRes.Last().EndTime.Value - (lastTime == 0 ? 0 : 30); + } + catch (Exception ex) + { + Console.WriteLine(DateTime.Now + $"=>鍒嗘瀽瑙嗛鍐呭澶辫触 {lastTime}~{endTime}"); + Console.WriteLine(DateTime.Now + ex.Message); + Console.WriteLine(DateTime.Now + ex.StackTrace); + endTime = lastTime - timeSpan; + + } + } + questionRes = questionRes.OrderBy(s => s.StartTime).ToList(); + + var thems = JsonSerializer.Serialize(questionRes.Adapt());// string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme)); + var checkResFormat1 = """[{"StartTime":寮濮嬬(number),"KnowPoint":鐭ヨ瘑鐐瑰悕绉(string),"KnowPointId":鐭ヨ瘑鐐笽d(string)}]"""; + var knowMessages = + $"鎴戦拡瀵箋subject}璇惧爞鎺堣瑙嗛鍒嗘瀽鍑轰簡瑙嗛鐨勬巿璇鹃樁娈电墖娈点" + + $"鐜板湪闇瑕佷綘閫氳繃姣忎釜鐗囨鐨勫唴瀹规荤粨鏉ュ垎閰嶆纭殑鐭ヨ瘑鐐(鍗曚釜鐗囨鍏佽澶氫釜鐭ヨ瘑鐐圭敤閫楀彿','鍒嗗壊)銆" + + $"杩欐槸鎴戠殑鍒嗘 {thems}銆" + + $"璇惧爞鍐呭涓巤fileNameInfoRes.鎺堣绔犺妭}绔犺妭鐩稿叧" + + $"鏈鍚庤纭繚鍒嗛厤鐨勭煡璇嗙偣鏄敤鎴锋彁渚涚殑,骞朵笖涓瀹氭纭悎鐞!" + + $"杈撳嚭鍐呭鍙繑鍥瀓son鏍煎紡({checkResFormat1})" + + $" 鏍煎紡 (鏂规硶鐐笽d|鏂规硶鐐瑰悕绉) " + + $"鎻愪緵鐨勭煡璇嗙偣鍚嶇О({knows})銆"; + Console.WriteLine(DateTime.Now + "=>2.寮濮嬪垎鏋愯棰戝唴瀹圭煡璇嗙偣"); + var konwRes = await ChatAsync(task, knowMessages, null); + + for (int i = 0; i < konwRes.Count(); i++) + questionRes[i].KnowPoint = konwRes[i].KnowPoint; + VideoKnowRes lastVideoKnow = null; + for (int i = 0; i < questionRes.Count(); i++) + { + var item = questionRes[i]; + // 闃舵绫诲瀷鐩哥瓑,涓旇寖鍥村寘鍚笂涓闃舵 + if (lastVideoKnow != null && lastVideoKnow.Stage == item.Stage && item.StartTime < lastVideoKnow.EndTime) + { + questionRes[i] = null; + lastVideoKnow.EndTime= item.EndTime; + } + lastVideoKnow = questionRes[i]; + } + thems = JsonSerializer.Serialize(questionRes.Adapt()); + var checkResFormat = """{"Score":鎵撳垎(number),"Evaluation":璇勪环(string)""";//,"Data":浼樺寲鍚庣殑鍒嗘(array)}"""; + var checkMessage = "鎴戜负瑙嗛鐨勮瑙e唴瀹瑰仛浜嗕竴浜涘垎娈,甯屾湜浣犺兘閫氳瀛楀箷鍐呭鍚庢鏌ヤ笅鐨勫垎娈垫槸鍚︾鍚堟垜鐨勮姹?" + + $"妫鏌ヨ繖浜涘垎娈电殑鏃堕棿鏄惁鍚堢悊 涓庣浉閭荤殑鏃堕棿娈甸棿闅旀槸鍚﹀浜庡悎鐞嗗尯闂30~900绉掍箣闂?" + + $"鍒嗘鐨勪富棰樺唴瀹,鐭ヨ瘑鐐瑰垎閰嶆槸鍚﹀悎鐞嗙鍚堝疄闄呭悧?" + + $"璇风粰鍑轰綘鐨勬墦鍒(0-100,70鍒嗗強鏍)浠ュ強鎵撳垎鍘熷洜銆" + + $"杩欐槸鎴戠殑鍒嗘 {thems}銆" + + $"鍚庣画鐨勫唴瀹规槸鍖呭惈鏃堕棿鎴崇殑瑙嗛瀛楀箷鐨勫浐瀹氭牸寮忔枃鏈" + + $"瀛楀箷鏍煎紡(璇磋瘽浜:寮濮嬬:缁撴潫绉:鍐呭|涓嬩竴娈靛瓧骞).浠ヤ笅鏄寘鍚椂闂寸殑瑙嗛瀛楀箷鏂囨湰銆傚瓧骞曞垪琛 {captions.Captions}銆" + + $"鏈鍚庤緭鍑烘牸寮忎负json({checkResFormat})"; + + Console.WriteLine(DateTime.Now + "=>3.寮濮嬫鏌ヨ棰戝垎娈电粨鏋"); + var checkRes = await ChatAsync(task, checkMessage, null); + if (checkRes != null && checkRes.Score >= 80) + { + break; + } + else + { + Console.WriteLine(DateTime.Now + $"=>{task} 寰楀垎杩囦綆/鍒嗘闀垮害涓嶅尮閰 寰楀垎{checkRes?.Score} "); + Console.WriteLine(checkRes.Evaluation); + Console.WriteLine(); + } + if (questionRes.Any(s => s.KeepTime < 30)) + { + Console.WriteLine(DateTime.Now + "=>瑙嗛鍒嗘杩囩煭!! 閲嶆柊杩涜AI鍒嗘瀽"); + continue; + } + + } + + //todo 鏈寘鍚殑鐭ヨ瘑鐐圭墖娈 濡備綍澶勭悊 + var insertData = questionRes + .Where(s => !string.IsNullOrEmpty(s.KnowPoint)) + .SelectMany( + s => + { + var ks = s.KnowPoint.Split(",").Distinct(); + return ks.Where(x => knowDic.ContainsKey(x)) + .Select(x => new VideoKonwPoint() + { + Content = s.Content, + Theme = s.Theme, + StartTime = s.StartTime, + EndTime = s.EndTime, + KnowPoint = x, + KnowPointId = knowDic[x].ToString(), + TagId = taskInfo.TagId, + VideoTaskId = taskInfo.Id, + }); + }).ToList(); + await videoKonwPointDB.DeleteAsync(s => s.VideoTaskId == taskId); + await videoKonwPointDB.InsertRangeAsync(insertData); + + + + + await RedisExpand.Redis + .HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes); + + var gptRes = new TaskRes(captions); + await RedisExpand.Redis + .HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes); + RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task); + return gptRes; + } + + public async Task ChatAsync(string task, string postMessages, string postMessages1, string model = "deepseek-reasoner") { var maxTokens = 4000; @@ -236,47 +661,52 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek temperature = 0.2f, messages = messageArr }; - - RedisExpand.SetTaskGPTReqCached(task, chatRep); - var chatResp = await chatClient.Chat(chatRep); - var chatResContent = chatResp?.res; - if (string.IsNullOrEmpty(chatResContent)) - throw new Exception("GPT杩斿洖message鏃犳晥缁撴灉"); - if (chatResp != null) - RedisExpand.SetTaskGPTCached(task, new object[] { chatResp.Value.res, chatResp.Value.u, chatResp.Value.reasoning }); - - chatResContent = chatResContent?.Replace("瀛楀箷鍐呭", "璇惧爞鎯呭喌"); - chatResContent = chatResContent?.Replace("\n", ""); - chatResContent = chatResContent?.Replace("```json", ""); - chatResContent = chatResContent?.Replace("```", ""); - chatResContent = chatResContent?.Replace("}{", "},{"); - chatResContent = chatResContent?.Replace("}|{", "},{"); - chatResContent = chatResContent?.Trim().ExtractJson().FirstOrDefault(); - - var startsStr = typeof(T).IsArray ? "[" : "{"; - var endStr = typeof(T).IsArray ? "]" : "}"; - if (!chatResContent.StartsWith(startsStr)) - chatResContent = startsStr + chatResContent; - if (!chatResContent.EndsWith(endStr)) - chatResContent = chatResContent + endStr; - var options = new JsonSerializerOptions + var tryCount = 10; + while (--tryCount >0) { - // 鍏佽瑙f瀽涓嶄弗鏍肩鍚 JSON 瑙勮寖鐨勫瓧绗︿覆 - AllowTrailingCommas = true, - // 澶勭悊涓嶅尮閰嶇殑 JSON 瀛楃 - ReadCommentHandling = JsonCommentHandling.Skip - }; - try - { - var questionRes = JsonSerializer.Deserialize(chatResContent, options); - if (questionRes is null) - throw new Exception("ChatGPT杩斿洖鏃犳晥缁撴灉"); - return questionRes; - } - catch (Exception ex) - { - throw new Exception("ChatGPT缁撴灉瑙f瀽閿欒 " + ex.Message + ex.Message); + try + { + RedisExpand.SetTaskGPTReqCached(task, chatRep); + var chatResp = await chatClient.Chat(chatRep); + var chatResContent = chatResp?.res; + if (string.IsNullOrEmpty(chatResContent)) + throw new Exception("GPT杩斿洖message鏃犳晥缁撴灉"); + if (chatResp != null) + RedisExpand.SetTaskGPTCached(task, new object[] { chatResp.Value.res, chatResp.Value.u, chatResp.Value.reasoning }); + + chatResContent = chatResContent?.Replace("瀛楀箷鍐呭", "璇惧爞鎯呭喌"); + chatResContent = chatResContent?.Replace("\n", ""); + chatResContent = chatResContent?.Replace("```json", ""); + chatResContent = chatResContent?.Replace("```", ""); + chatResContent = chatResContent?.Replace("}{", "},{"); + chatResContent = chatResContent?.Replace("}|{", "},{"); + chatResContent = chatResContent?.Trim().ExtractJson().FirstOrDefault(); + + var startsStr = typeof(T).IsArray ? "[" : "{"; + var endStr = typeof(T).IsArray ? "]" : "}"; + if (!chatResContent.StartsWith(startsStr)) + chatResContent = startsStr + chatResContent; + if (!chatResContent.EndsWith(endStr)) + chatResContent = chatResContent + endStr; + var options = new JsonSerializerOptions + { + // 鍏佽瑙f瀽涓嶄弗鏍肩鍚 JSON 瑙勮寖鐨勫瓧绗︿覆 + AllowTrailingCommas = true, + // 澶勭悊涓嶅尮閰嶇殑 JSON 瀛楃 + ReadCommentHandling = JsonCommentHandling.Skip + }; + var questionRes = JsonSerializer.Deserialize(chatResContent, options); + if (questionRes is null) + throw new Exception("ChatGPT杩斿洖鏃犳晥缁撴灉"); + return questionRes; + } + catch (Exception ex) + { + Console.WriteLine(DateTime.Now + $"=>ChatGPT缁撴灉瑙f瀽閿欒 閲嶈瘯鍓╀綑{tryCount}"); + Console.WriteLine(ex.Message); + } } + throw new Exception(DateTime.Now+ "=>ChatGPT璇锋眰澶辫触娆℃暟杩囧!!!"); } } } diff --git a/VideoAnalysisCore/AICore/GPT/Dto/QuestionRes.cs b/VideoAnalysisCore/AICore/GPT/Dto/QuestionRes.cs index bc8a6c1..647da00 100644 --- a/VideoAnalysisCore/AICore/GPT/Dto/QuestionRes.cs +++ b/VideoAnalysisCore/AICore/GPT/Dto/QuestionRes.cs @@ -7,6 +7,7 @@ using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using System.Threading.Tasks; +using VideoAnalysisCore.Model.Enum; namespace VideoAnalysisCore.AICore.GPT.Dto { @@ -54,6 +55,10 @@ namespace VideoAnalysisCore.AICore.GPT.Dto /// public virtual string? KnowPointId { get; set; } /// + /// 璇剧▼闃舵 + /// + public virtual string ? Stage { get; set; } + /// /// 鍐呭鎬荤粨 /// public virtual string? Content { get; set; } diff --git a/VideoAnalysisCore/AICore/SherpaOnnx/Speaker.cs b/VideoAnalysisCore/AICore/SherpaOnnx/Speaker.cs index c4b0725..4aa8337 100644 --- a/VideoAnalysisCore/AICore/SherpaOnnx/Speaker.cs +++ b/VideoAnalysisCore/AICore/SherpaOnnx/Speaker.cs @@ -79,10 +79,11 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx var res = segments.Select(s => new OfflineSpeakerRes(s)); await RedisExpand.Redis.HSetAsync(RedisExpandKey.Task(task), "Speaker", res); var speakerStr = JsonSerializer.Serialize(res); - DbScoped.Sugar + await DbScoped.Sugar .Updateable() .SetColumns(it => it.Speaker == speakerStr) - .Where(it => it.Id == long.Parse(task)); + .Where(it => it.Id == long.Parse(task)) + .ExecuteCommandAsync(); //鍔犲叆涓嬩竴闃熷垪 RedisExpand.InsertChannel(RedisChannelEnum.ChatModelAnalysis, task); diff --git a/VideoAnalysisCore/Common/DownloadFile.cs b/VideoAnalysisCore/Common/DownloadFile.cs index 7a58859..97c4be5 100644 --- a/VideoAnalysisCore/Common/DownloadFile.cs +++ b/VideoAnalysisCore/Common/DownloadFile.cs @@ -175,38 +175,86 @@ namespace VideoAnalysisCore.Common .SetColumns(it => it.LocalMediaPath == outputPath) .Where(it => it.Id == long.Parse(task)) .ExecuteCommandAsync(); + //涓嬭浇PPT瑙嗛 + if (!string.IsNullOrEmpty(taskInfo.PPTVideoCode)) + { + try + { + var videoInfo = await vodClient.GetPlayInfoAsync(new AlibabaCloud.SDK.Vod20170321.Models.GetPlayInfoRequest() + { + VideoId = taskInfo.PPTVideoCode, + Formats = "mp4", + OutputType = "cdn", + AuthTimeout = 3600 * 24 * 12, + }); + if (videoInfo is null || videoInfo.StatusCode != 200 && !videoInfo.Body.PlayInfoList.PlayInfo.Any()) + throw new Exception($"{DateTime.Now} 瑙嗛璁㈤槄=>鑾峰彇闃块噷浜戣棰戜俊鎭け璐 VideoCode {taskInfo.TagId} StatusCode {videoInfo?.StatusCode}"); + var url = videoInfo.Body.PlayInfoList.PlayInfo.First().PlayURL; + await Download(url, localPath, "ppt.mp4", + (s, e) => RedisExpand.SetTaskProgress(task, "PPT->" + Math.Round(e.ProgressPercentage, 1) + )); + } + catch (Exception e) + { + await RedisExpand.SetTaskErrorMessage(taskId, e); + } + } + try + {//涓嬭浇鍘熻棰 + await Download(fileUrl, localPath, task + fileExtension, + (s, e) => RedisExpand.SetTaskProgress(task, Math.Round(e.ProgressPercentage,1) + )); - IDownload download = DownloadBuilder.New() + //鍔犲叆涓嬩竴闃熷垪 + RedisExpand.InsertChannel(RedisChannelEnum.SeparateAudio, task); + } + catch (Exception e) + { + await RedisExpand.SetTaskErrorMessage(taskId, e); + } + + + } + + + /// + /// 涓嬭浇鏂囦欢 + /// + /// + /// + /// + /// + public async Task Download(string fileUrl, string localPath, string fileName,Action change) + { + var res = new TaskCompletionSource(); + using IDownload download = DownloadBuilder.New() .WithUrl(fileUrl) .WithDirectory(localPath) - .WithFileName(task + fileExtension) + .WithFileName(fileName) .WithConfiguration(Opt) .Build(); var pI = 0; - download.DownloadProgressChanged += (object? sender, Downloader.DownloadProgressChangedEventArgs e) => + download.DownloadProgressChanged += (object? sender, Downloader.DownloadProgressChangedEventArgs e) => { pI++; if (pI % 20 == 0) - RedisExpand.SetTaskProgress(task, e.ProgressPercentage); + change(sender, e); }; download.DownloadFileCompleted += async (object? sender, AsyncCompletedEventArgs e) => { if (download.Status == DownloadStatus.Failed && e.Error != null) { - await RedisExpand.SetTaskErrorMessage(taskId, e.Error) - .ConfigureAwait(false);//涓嶅垏鍥炰笂涓嬫枃 - return; + res.SetException(e.Error); } else if (download.Status == DownloadStatus.Completed) { - //鍔犲叆涓嬩竴闃熷垪 - RedisExpand.InsertChannel(RedisChannelEnum.SeparateAudio, task); - return; + res.SetResult(); } - }; await download.StartAsync(); + // 绛夊緟鍥炶皟鍑芥暟瀹屾垚 + await res.Task; } } } diff --git a/VideoAnalysisCore/Common/Expand/SqlSugarExpand.cs b/VideoAnalysisCore/Common/Expand/SqlSugarExpand.cs index 0cc7c23..ea2d7cc 100644 --- a/VideoAnalysisCore/Common/Expand/SqlSugarExpand.cs +++ b/VideoAnalysisCore/Common/Expand/SqlSugarExpand.cs @@ -76,7 +76,8 @@ namespace VideoAnalysisCore.Common.Expand if (ex.Parametres == null) return; //var originColor = Console.ForegroundColor; //Console.ForegroundColor = ConsoleColor.DarkRed; - Console.WriteLine($"銆恵DateTime.Now}鈥斺旈敊璇疭QL - [{config.ConfigId}]銆慭r\n" + UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n"); + Console.WriteLine($"銆恵DateTime.Now}鈥斺旈敊璇疭QL - [{config.ConfigId}]銆慭r\n"+ ex.Message + "\r\n" + UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n"); + Console.WriteLine(); //Console.ForegroundColor = originColor; }; db.Aop.DataExecuting = (oldValue, entityInfo) => diff --git a/VideoAnalysisCore/Common/RedisExpand.cs b/VideoAnalysisCore/Common/RedisExpand.cs index 62144e6..1b3172e 100644 --- a/VideoAnalysisCore/Common/RedisExpand.cs +++ b/VideoAnalysisCore/Common/RedisExpand.cs @@ -108,11 +108,11 @@ namespace VideoAnalysisCore.Common /// public static void SetTaskGPTCached(object taskId, object? data) { - Redis.Set(RedisExpandKey.TaskGPT(taskId) + ":Res_" + DateTime.Now.ToString("yyyy/MM/dd_HH/mm/ss"), data, 3600 * 24); + Redis.Set(RedisExpandKey.TaskGPT(taskId) + ":" + DateTime.Now.ToString("MMddHHmmss") + "01", data, 3600 * 24); } public static void SetTaskGPTReqCached(object taskId, object? data) { - Redis.Set(RedisExpandKey.TaskGPT(taskId) + ":Req_" + DateTime.Now.ToString("yyyy/MM/dd_HH/mm/ss"), data, 3600 * 24); + Redis.Set(RedisExpandKey.TaskGPT(taskId) + ":" + DateTime.Now.ToString("MMddHHmmss")+ "00", data, 3600 * 24); } /// /// 鍔犲叆鍒版秷璐归槦鍒 @@ -143,9 +143,9 @@ namespace VideoAnalysisCore.Common /// /// 杩涘害鐧惧垎姣 /// - public static void SetTaskProgress(object taskId, double p) + public static void SetTaskProgress(object taskId, object p) { - Redis.HMSet(RedisExpandKey.Task(taskId), "Progress", Math.Round(p, 2)); + Redis.HMSet(RedisExpandKey.Task(taskId), "Progress", p.ToString()); } /// @@ -220,19 +220,20 @@ namespace VideoAnalysisCore.Common if (Redis is null) throw new Exception("redis鏈垵濮嬪寲"); SubscribeList.Add(RedisChannelEnum.DownloadFile, - (msg) => { TouchChannel(RedisChannelEnum.DownloadFile, msg, - (task) => + (Action)((msg) => { + TouchChannel(RedisChannelEnum.DownloadFile, msg, + (Func)((task) => { using var scope = AppCommon.Services?.CreateScope(); - if (scope is null || scope.ServiceProvider.GetService() is null) + if (scope is null || ServiceProviderServiceExtensions.GetService(scope.ServiceProvider) is null) throw new Exception("DownloadFile 鏈敞鍏"); else - return scope.ServiceProvider.GetService()?.RunTask(task) ?? Task.CompletedTask; - }); - }); + return (Task)(scope.ServiceProvider.GetService()?.RunTask(task) ?? Task.CompletedTask); + })); + })); SubscribeList.Add(RedisChannelEnum.SeparateAudio, - (msg) => { TouchChannel(RedisChannelEnum.SeparateAudio, msg, FFMPGEHandle.Audio2WAV16KAsync); }); + (msg) => { TouchChannel(RedisChannelEnum.SeparateAudio, msg, FFMPGEHandle.RunAsync); }); SubscribeList.Add(RedisChannelEnum.ParsingCaptions, (msg) => { TouchChannel(RedisChannelEnum.ParsingCaptions, msg, SenseVoice.RunTask); }); diff --git a/Learn.VideoAnalysis.API/Controllers/ApiController.cs b/VideoAnalysisCore/Controllers/ApiController.cs similarity index 97% rename from Learn.VideoAnalysis.API/Controllers/ApiController.cs rename to VideoAnalysisCore/Controllers/ApiController.cs index cc903bc..4af8798 100644 --- a/Learn.VideoAnalysis.API/Controllers/ApiController.cs +++ b/VideoAnalysisCore/Controllers/ApiController.cs @@ -12,24 +12,23 @@ using System.Text.Json; using VideoAnalysisCore.Model.Enum; using Yitter.IdGenerator; using VideoAnalysisCore.Model; +using Microsoft.AspNetCore.Http; using VideoAnalysisCore.Model.Dto; -using Learn.VideoAnalysis.API.Controllers.Dto; +using VideoAnalysisCore.Controllers.Dto; -namespace Learn.VideoAnalysis.API.Controllers +namespace VideoAnalysisCore.Controllers { [ApiController] [Route("[controller]/[action]")] public class ApiController : ControllerBase { - private readonly ILogger _logger; private readonly IMapper mp; private readonly Repository videoTaskDB; private readonly Repository videoKonwDB; private readonly IBserGPT chatGPT; - public ApiController(ILogger logger, Repository videoTaskDB, + public ApiController(Repository videoTaskDB, IMapper mp, IBserGPT chatGPT, Repository videoKonwDB) { - _logger = logger; this.videoTaskDB = videoTaskDB; this.mp = mp; this.chatGPT = chatGPT; @@ -169,7 +168,8 @@ namespace Learn.VideoAnalysis.API.Controllers Subject = req.Subject, Tag = req.Tag, TagId = req.TagId, - MediaName = req.Name + MediaName = req.Name, + PPTVideoCode = req.PPTVideoCode, }; //入库 var hashEntries = task.GetType() diff --git a/Learn.VideoAnalysis.API/Controllers/Dto/ApiDto.cs b/VideoAnalysisCore/Controllers/Dto/ApiDto.cs similarity index 97% rename from Learn.VideoAnalysis.API/Controllers/Dto/ApiDto.cs rename to VideoAnalysisCore/Controllers/Dto/ApiDto.cs index 650f11a..ef7f08b 100644 --- a/Learn.VideoAnalysis.API/Controllers/Dto/ApiDto.cs +++ b/VideoAnalysisCore/Controllers/Dto/ApiDto.cs @@ -4,7 +4,7 @@ using UserCenter.Model.Enum; using VideoAnalysisCore.AICore.GPT.Dto; using VideoAnalysisCore.Model.Enum; -namespace Learn.VideoAnalysis.API.Controllers.Dto +namespace VideoAnalysisCore.Controllers.Dto { /// /// 瑙嗛鍒楄〃椤 @@ -120,6 +120,10 @@ namespace Learn.VideoAnalysis.API.Controllers.Dto /// public string? TagId { get; set; } /// + /// 璇剧▼瀵瑰簲ppt瑙嗛 + /// + public string? PPTVideoCode { get; set; } + /// ///鍥炶皟Api鍦板潃 /// //[Required(ErrorMessage = "鍥炶皟Api鍦板潃鏄繀濉」")] diff --git a/VideoAnalysis/Controllers/LJZK_Controller.cs b/VideoAnalysisCore/Controllers/LJZK_Controller.cs similarity index 90% rename from VideoAnalysis/Controllers/LJZK_Controller.cs rename to VideoAnalysisCore/Controllers/LJZK_Controller.cs index 76979a5..563b8e5 100644 --- a/VideoAnalysis/Controllers/LJZK_Controller.cs +++ b/VideoAnalysisCore/Controllers/LJZK_Controller.cs @@ -16,8 +16,10 @@ using static FFmpeg.NET.MetaData; using static System.Runtime.InteropServices.JavaScript.JSType; using Yitter.IdGenerator; using VideoAnalysisCore.AICore.GPT.Dto; +using VideoAnalysisCore.Model; +using VideoAnalysisCore.Controllers.Dto; -namespace Learn.VideoAnalysis.Controllers +namespace VideoAnalysisCore.Controllers { /// /// 蓝鲸字库接口 @@ -26,19 +28,16 @@ namespace Learn.VideoAnalysis.Controllers [Route("LJZK/[action]")] public class LJZK_Controller : ControllerBase { - private readonly ILogger _logger; private readonly IMapper mp; private readonly Repository nodesubscriptionDB; private readonly Repository videoTaskDB; private readonly Repository videoKonwPointDB; private readonly Repository nodePackageInfoDB; private readonly IBserGPT chatGPT; - public LJZK_Controller(ILogger logger, - IMapper mp, IBserGPT chatGPT, Repository nodesubscriptionDB, + public LJZK_Controller( IMapper mp, IBserGPT chatGPT, Repository nodesubscriptionDB, Repository videoTaskDB = null, Repository videoKonwPointDB = null , Repository nodePackageInfoDB = null) { - _logger = logger; this.mp = mp; this.chatGPT = chatGPT; this.nodesubscriptionDB = nodesubscriptionDB; @@ -96,9 +95,12 @@ namespace Learn.VideoAnalysis.Controllers NodeId = req.NodeId, TaskType = req.TaskType, SubjectType = req.SubjectType, + }; nodePackages.Add(np); - if (videoIdArr.Contains(s.VideoCode)) + if (s.AttachmentsInfoType == AttachmentsInfoType.PPT) + continue; + if (videoIdArr.Contains(s.VideoCode)) continue; videos.Add(new VideoTask() { @@ -108,8 +110,11 @@ namespace Learn.VideoAnalysis.Controllers Type = req.TaskType, Subject = req.SubjectType, TagId = s.VideoCode, - MediaUrl =string.Empty, - MediaName = s.VideoName + MediaUrl = string.Empty, + MediaName = s.VideoName, + PPTVideoCode = req.AnalyzeItems + .FirstOrDefault(x => x.AttachmentsInfoType == AttachmentsInfoType.PPT && s.StructurePageContentId == x.StructurePageContentId) + ?.VideoCode, }); } await nodePackageInfoDB.InsertRangeAsync(nodePackages); diff --git a/VideoAnalysisCore/Model/Dto/VideoTaskDto.cs b/VideoAnalysisCore/Model/Dto/VideoTaskDto.cs index 0c9b0df..5fc4659 100644 --- a/VideoAnalysisCore/Model/Dto/VideoTaskDto.cs +++ b/VideoAnalysisCore/Model/Dto/VideoTaskDto.cs @@ -44,7 +44,7 @@ namespace VideoAnalysisCore.Model.Dto /// 鎵ц杩涘害 /// [DisplayName("杩涘害")] - public float Progress { get; set; } + public string Progress { get; set; } /// /// 閿欒淇℃伅 /// diff --git a/VideoAnalysisCore/Model/Enum/StageEnum.cs b/VideoAnalysisCore/Model/Enum/StageEnum.cs new file mode 100644 index 0000000..003e17a --- /dev/null +++ b/VideoAnalysisCore/Model/Enum/StageEnum.cs @@ -0,0 +1,19 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VideoAnalysisCore.Model.Enum +{ + public enum StageEnum + { + 璇剧▼寮曞叆, + 鏂扮煡璁茶В, + 渚嬮绮捐, + 璇惧爞缁冧範, + 浜掑姩璁ㄨ, + 璇剧▼鎬荤粨, + 浣滀笟甯冪疆 + } +} diff --git a/VideoAnalysisCore/Model/Enum/StepLevelEnum.cs b/VideoAnalysisCore/Model/Enum/StepLevelEnum.cs new file mode 100644 index 0000000..662d0f2 --- /dev/null +++ b/VideoAnalysisCore/Model/Enum/StepLevelEnum.cs @@ -0,0 +1,16 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VideoAnalysisCore.Model.Enum +{ + public enum StepLevelEnum + { + 鍒濇 = 1, + 涓 = 2, + 灏炬 = 3, + + } +} diff --git a/VideoAnalysisCore/Model/VideoTask.cs b/VideoAnalysisCore/Model/VideoTask.cs index b7db6f9..80b4480 100644 --- a/VideoAnalysisCore/Model/VideoTask.cs +++ b/VideoAnalysisCore/Model/VideoTask.cs @@ -61,14 +61,24 @@ namespace VideoAnalysisCore.Model /// /// 鑷畾涔夊 浠诲姟瀹屾垚鍚庨檮甯﹂氱煡 /// - [SugarColumn(Length = 500)] - public string Tag { get; set; } + [SugarColumn(Length = 500, IsNullable = true)] + public string? Tag { get; set; } /// - /// 鑷畾涔夊糏d 浠诲姟瀹屾垚鍚庣殑鍑瘉 + /// 鑷畾涔夊糏d /// - [SugarColumn(Length = 500,IsNullable =true)] + [SugarColumn(Length = 50, ColumnDataType = "varchar", IsNullable =true)] public string? TagId { get; set; } /// + /// 鎺堣瑙嗛瀵瑰簲PPT瑙嗛ID + /// + [SugarColumn(Length = 50, ColumnDataType = "varchar", IsNullable = true)] + public string? PPTVideoCode { get; set; } + /// + /// 鎺堣瑙嗛瀵瑰簲PPT瑙嗛鍏抽敭甯 + /// + [SugarColumn(ColumnDataType = "longtext",IsNullable = true)] + public string? PPTKeyFrame { get; set; } + /// /// 瀛楀箷缂撳瓨 /// [SugarColumn(ColumnName = "Captions", ColumnDataType = "longtext", IsNullable = true)] @@ -106,11 +116,6 @@ namespace VideoAnalysisCore.Model /// [SugarColumn( IsNullable = true)] public DateTime? EndTime { get; set; } - /// - /// 寮濮嬫椂闂磋酱 - /// - [SugarColumn(ColumnDataType = "varchar", Length = 255)] - public string StartTime { get; set; } ="{}"; } } diff --git a/VideoAnalysisCore/VideoAnalysisCore.csproj b/VideoAnalysisCore/VideoAnalysisCore.csproj index 241dbd3..89946ab 100644 --- a/VideoAnalysisCore/VideoAnalysisCore.csproj +++ b/VideoAnalysisCore/VideoAnalysisCore.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + True @@ -64,11 +65,13 @@ - + + + diff --git a/testC/Program.cs b/testC/Program.cs new file mode 100644 index 0000000..d57586e --- /dev/null +++ b/testC/Program.cs @@ -0,0 +1,131 @@ +锘縰sing System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using MediaToolkit; +using MediaToolkit.Model; +using MediaToolkit.Options; +using VideoAnalysisCore.Common; +using VideoAnalysisCore.AICore.FFMPGE; +using Microsoft.Extensions.Options; +using System.Diagnostics; + +namespace VideoChangeDetector +{ + class Program + { + static void Main(string[] args) + { + var taskID = "665310769946693"; + var inputFile = $"F:\\Learn.VideoAnalysis\\testC\\bin\\Debug\\net8.0\\video\\{taskID}.mp4"; + //inputFile = "F:\\Learn.VideoAnalysis\\testC\\bin\\Debug\\net8.0\\video\\ppt.mp4"; + string tempFrameDir = "temp_frames"; + var intervalSec = 5; + double threshold = 15.0; + + Stopwatch sw = new Stopwatch(); + sw.Start(); + ExtractVideoFrames(inputFile, tempFrameDir, intervalSec); + sw.Stop(); + Console.WriteLine("瑙嗛鍒囧抚鎬诲叡鑺辫垂{0}ms.", sw.Elapsed.TotalMilliseconds); + Stopwatch sw1 = new Stopwatch(); + sw1.Start(); + DetectChanges(tempFrameDir, threshold); + sw1.Stop(); + Console.WriteLine("瑙嗛鍒嗘瀽甯ф诲叡鑺辫垂{0}ms.", sw1.Elapsed.TotalMilliseconds); + Console.WriteLine("澶勭悊瀹屾垚!"); + } + /// + /// 瑙嗛鍒囩墖 + /// + /// 杈撳叆 + /// 杈撳嚭 + /// 闂撮殧澶氬皯绉 + /// 绾跨▼鏁 + static void ExtractVideoFrames(string inputPath, string outputDir, int intervalSec, int ProcessorCount = 6) + { + Directory.CreateDirectory(outputDir); + var inputFile = new MediaFile { Filename = inputPath }; + using var engine = new Engine(FFMPGEHandle.FFmpegPath); + engine.CustomCommand($"-i {inputPath} -vf \"fps=1/{intervalSec},scale=320:180\" {outputDir}/frame_%03d.jpg"); + } + /// + /// 宸紓姣斿 + /// + /// + /// + /// + static void DetectChanges(string frameDir, double threshold,int ProcessorCount = 6) + { + var frameFiles = Directory.GetFiles(frameDir, "*.jpg") + .OrderBy(f => f) + .ToList(); + + Image prevFrame = null; + string outputDir = "output"; + Directory.CreateDirectory(outputDir); + var options = new ParallelOptions { MaxDegreeOfParallelism = ProcessorCount }; + foreach (var frameFile in frameFiles) + { + using (var currFrame = Image.Load(frameFile)) + { + if (prevFrame != null) + { + double diff = CalculateFrameDifference(prevFrame, currFrame); + double timestamp = GetTimestampFromFileName(frameFile) * 5 ; + + if (diff > threshold) + { + string outputPath = Path.Combine(outputDir, $"change_{timestamp:0000}.jpg"); + currFrame.Save(outputPath); + Console.WriteLine($"鍙樺寲甯: {timestamp}绉掞紝宸紓鍊: {diff:F2}"); + } + else + { + Console.WriteLine($"-------: {timestamp}绉掞紝宸紓鍊: {diff:F2}"); + } + } + prevFrame?.Dispose(); + prevFrame = currFrame.Clone(); + } + } + //Parallel.ForEach(frameFiles, options, frameFile => + //{ + + //}); + } + /// + /// 璁$畻甯у樊寮 + /// + /// + /// + /// + static double CalculateFrameDifference(Image img1, Image img2) + { + // 缁熶竴璋冩暣涓64x64 + var resized1 = img1.Clone(x => x.Resize(96, 96).Grayscale()); + var resized2 = img2.Clone(x => x.Resize(96, 96).Grayscale()); + + long diff = 0; + for (int y = 0; y < resized1.Height; y++) + { + for (int x = 0; x < resized1.Width; x++) + { + var pixel1 = resized1[x, y]; + var pixel2 = resized2[x, y]; + diff += Math.Abs(pixel1.R - pixel2.R); + } + } + return diff / (double)(resized1.Width * resized1.Height); + } + + static double GetTimestampFromFileName(string filePath) + { + string fileName = Path.GetFileNameWithoutExtension(filePath); + return double.Parse(fileName.Split('_')[1]); + } + } +} \ No newline at end of file diff --git a/testC/testC.csproj b/testC/testC.csproj new file mode 100644 index 0000000..026be91 --- /dev/null +++ b/testC/testC.csproj @@ -0,0 +1,19 @@ +锘 + + + Exe + net8.0 + enable + enable + + + + + + + + + + + +