优化 AI分析流程
This commit is contained in:
parent
ae6e5ce836
commit
c39fbb7501
|
|
@ -24,14 +24,18 @@ namespace Learn.VideoAnalysis.Controllers
|
||||||
private readonly ILogger<LJZK_Controller> _logger;
|
private readonly ILogger<LJZK_Controller> _logger;
|
||||||
private readonly IMapper mp;
|
private readonly IMapper mp;
|
||||||
private readonly Repository<NodeSubscription> nodesubscriptionDB;
|
private readonly Repository<NodeSubscription> nodesubscriptionDB;
|
||||||
|
private readonly Repository<VideoTask> videoTaskDB;
|
||||||
|
private readonly Repository<VideoKonwPoint> videoKonwPointDB;
|
||||||
private readonly IBserGPT chatGPT;
|
private readonly IBserGPT chatGPT;
|
||||||
public LJZK_Controller(ILogger<LJZK_Controller> logger,
|
public LJZK_Controller(ILogger<LJZK_Controller> logger,
|
||||||
IMapper mp, IBserGPT chatGPT, Repository<NodeSubscription> nodesubscriptionDB)
|
IMapper mp, IBserGPT chatGPT, Repository<NodeSubscription> nodesubscriptionDB, Repository<VideoTask> videoTaskDB = null, Repository<VideoKonwPoint> videoKonwPointDB = null)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
this.mp = mp;
|
this.mp = mp;
|
||||||
this.chatGPT = chatGPT;
|
this.chatGPT = chatGPT;
|
||||||
this.nodesubscriptionDB = nodesubscriptionDB;
|
this.nodesubscriptionDB = nodesubscriptionDB;
|
||||||
|
this.videoTaskDB = videoTaskDB;
|
||||||
|
this.videoKonwPointDB = videoKonwPointDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -43,12 +47,16 @@ namespace Learn.VideoAnalysis.Controllers
|
||||||
[HttpPost(Name = "NodeSubscription")]
|
[HttpPost(Name = "NodeSubscription")]
|
||||||
public async Task<IActionResult> NodeSubscription(NodeMonitoringReq req)
|
public async Task<IActionResult> NodeSubscription(NodeMonitoringReq req)
|
||||||
{
|
{
|
||||||
|
if(req is null || req.NodeId ==0)
|
||||||
|
return BadRequest("无效的提交数据");
|
||||||
if (nodesubscriptionDB.IsAny(s => s.NodeId == req.NodeId))
|
if (nodesubscriptionDB.IsAny(s => s.NodeId == req.NodeId))
|
||||||
return BadRequest("重复添加了节点监控任务" + req.NodeId);
|
return BadRequest("重复添加了节点监控任务" + req.NodeId);
|
||||||
var res = await nodesubscriptionDB.InsertReturnEntityAsync(new NodeSubscription()
|
var res = await nodesubscriptionDB.InsertReturnEntityAsync(new NodeSubscription()
|
||||||
{
|
{
|
||||||
NodeId = req.NodeId,
|
NodeId = req.NodeId,
|
||||||
TaskType = req.Type ?? default
|
TaskType = req.Type ?? default,
|
||||||
|
Subject = req.Subject?? default,
|
||||||
|
|
||||||
});
|
});
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +84,52 @@ namespace Learn.VideoAnalysis.Controllers
|
||||||
.Select(s => new { Text = s.ToString(), Value = (int)s }));
|
.Select(s => new { Text = s.ToString(), Value = (int)s }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取视频知识点片段<para>taskId/tagId二选一</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="taskId"></param>
|
||||||
|
/// <param name="tagId">自定义id</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet(Name = "TaskKnowInfo")]
|
||||||
|
public async Task<IActionResult> 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()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@ namespace Learn.VideoAnalysis.Expand
|
||||||
public static class CoravelExpand
|
public static class CoravelExpand
|
||||||
{
|
{
|
||||||
public static int MyProperty { get; set; }
|
public static int MyProperty { get; set; }
|
||||||
public static void Init(IServiceCollection service)
|
public static void AddCoravel(this IServiceCollection service)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Console.WriteLine($"{DateTime.Now}=>初始化 Coravel");
|
||||||
service.AddScheduler();
|
service.AddScheduler();
|
||||||
service.AddTransient<NodeSubscriptionJob>();
|
service.AddTransient<NodeSubscriptionJob>();
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +25,9 @@ namespace Learn.VideoAnalysis.Expand
|
||||||
provider.UseScheduler(scheduler =>
|
provider.UseScheduler(scheduler =>
|
||||||
{
|
{
|
||||||
//每5分钟执行一次 未处理视频扫描
|
//每5分钟执行一次 未处理视频扫描
|
||||||
scheduler.Schedule<NodeSubscriptionJob>().EveryFiveMinutes();
|
//scheduler.Schedule<NodeSubscriptionJob>().EveryFiveMinutes();
|
||||||
|
//每天两点
|
||||||
|
scheduler.Schedule<TaskFileClearJob>().DailyAtHour(2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using SqlSugar;
|
||||||
using SqlSugar.IOC;
|
using SqlSugar.IOC;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
@ -17,8 +18,13 @@ namespace Learn.VideoAnalysis.Expand
|
||||||
public static class SqlSugarExpand
|
public static class SqlSugarExpand
|
||||||
{
|
{
|
||||||
public static bool ShowSQL = false;
|
public static bool ShowSQL = false;
|
||||||
public static void InitSqlSugar(this IServiceCollection services)
|
public static void AddSqlSugarExpand(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Console.WriteLine($"{DateTime.Now}=>初始化 YitId雪花ID");
|
||||||
|
var options = new IdGeneratorOptions(ushort.Parse(AppCommon.Config.ID));
|
||||||
|
YitIdHelper.SetIdGenerator(options);
|
||||||
|
|
||||||
#region SqlSugar注入
|
#region SqlSugar注入
|
||||||
var dbList = new List<IocConfig>() {
|
var dbList = new List<IocConfig>() {
|
||||||
new IocConfig()
|
new IocConfig()
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\VideoAnalysisCore\VideoAnalysisCore.csproj" />
|
<ProjectReference Include="..\VideoAnalysisCore\VideoAnalysisCore.csproj" />
|
||||||
|
<PackageReference Include="AlibabaCloud.SDK.Vod20170321" Version="3.6.1" />
|
||||||
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.2-pre01" />
|
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.2-pre01" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.5" />
|
||||||
|
|
|
||||||
|
|
@ -57,10 +57,11 @@ namespace Learn.VideoAnalysis
|
||||||
builder.Configuration.GetSection("AppConfig").Bind(AppCommon.Config);
|
builder.Configuration.GetSection("AppConfig").Bind(AppCommon.Config);
|
||||||
|
|
||||||
//初始化 插件
|
//初始化 插件
|
||||||
builder.Services.InitSqlSugar();
|
builder.Services.AddSqlSugarExpand();
|
||||||
RedisExpand.Init();
|
builder.Services.AddAlibabaCloudVod();
|
||||||
Speaker.Init();
|
builder.Services.AddRedisExpand();
|
||||||
CoravelExpand.Init(builder.Services);
|
builder.Services.AddSpeakerAI();
|
||||||
|
builder.Services.AddCoravel();
|
||||||
//SenseVoice.Init();
|
//SenseVoice.Init();
|
||||||
//异常过滤器
|
//异常过滤器
|
||||||
builder.Services.AddControllersWithViews(options =>
|
builder.Services.AddControllersWithViews(options =>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"AppConfig": {
|
"AppConfig": {
|
||||||
"ID": "APP0001", //程序唯一值
|
"ID": 1, //程序唯一值
|
||||||
"Admin": {
|
"Admin": {
|
||||||
"Account": "admin",
|
"Account": "admin",
|
||||||
"Password": "q1w2e3!@#"
|
"Password": "q1w2e3!@#"
|
||||||
|
|
@ -43,7 +43,12 @@
|
||||||
"DB": {
|
"DB": {
|
||||||
"ConnectionString": "AllowLoadLocalInfile=true;Server=192.168.2.9;User ID=root;Password=qwe123!@#;Port=3306;Database=learn.videoanalysis;CharSet=utf8mb4;pooling=true;SslMode=None",
|
"ConnectionString": "AllowLoadLocalInfile=true;Server=192.168.2.9;User ID=root;Password=qwe123!@#;Port=3306;Database=learn.videoanalysis;CharSet=utf8mb4;pooling=true;SslMode=None",
|
||||||
"SqlType": "MySql",
|
"SqlType": "MySql",
|
||||||
"UpdateTable": true
|
"UpdateTable": false
|
||||||
|
},
|
||||||
|
"AlibabaCloudVod": {
|
||||||
|
"AccessKeyId": "LTAI5tDC6p9h747B7FHbgwkH",
|
||||||
|
"AccessKeySecret": "vRKgmbp1LB05LaGOjh3ZrZxbHSLYLF",
|
||||||
|
"EndPoint": "vod.cn-shanghai.aliyuncs.com" //上传节点
|
||||||
},
|
},
|
||||||
"OtherDBArr": [
|
"OtherDBArr": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ using VideoAnalysisCore.AICore.GPT.ChatGPT;
|
||||||
using VideoAnalysisCore.AICore.SherpaOnnx;
|
using VideoAnalysisCore.AICore.SherpaOnnx;
|
||||||
using VideoAnalysisCore.Model.蓝鲸智库;
|
using VideoAnalysisCore.Model.蓝鲸智库;
|
||||||
using VideoAnalysisCore.Model.Enum;
|
using VideoAnalysisCore.Model.Enum;
|
||||||
|
using Mapster;
|
||||||
|
|
||||||
namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
{
|
{
|
||||||
|
|
@ -51,18 +52,30 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
.Where(s => s.Id == taskId)
|
.Where(s => s.Id == taskId)
|
||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
var subject = "数学";
|
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()
|
var xkwKnows = await knowledgeInfoDB.AsQueryable()
|
||||||
.Where(s => s.Course_Id == 27
|
.Where(s => s.Course_Id == Course_Id
|
||||||
&& (s.Depth == 3
|
&& (s.Depth == 3
|
||||||
|| s.Depth == 2 ))
|
|| s.Depth == 2))
|
||||||
.Select(s => s.Name).ToArrayAsync();
|
.Select(s => s.Name).ToArrayAsync();
|
||||||
string title = taskInfo.MediaName;
|
string title = taskInfo.MediaName;
|
||||||
var fileNameResFormat = "{授课章节: string|null}";
|
var fileNameResFormat = "{授课章节: string|null}";
|
||||||
var fileNamePostMessages = title +
|
var fileNamePostMessages = title +
|
||||||
" 这是一堂课的标题,请你基于标题帮我分析出这堂课所讲授的内容与最恰当的授课章节(关联最贴切的章节,保留一个章节!)." +
|
" 这是一堂课的标题,请你基于标题帮我分析出这堂课所讲授的内容与最恰当的授课章节(关联最贴切的章节,保留一个章节!)." +
|
||||||
$"章节范围限定在[{string.Join(',', xkwKnows)}]范围." +
|
$"章节范围限定在[{string.Join(',', xkwKnows)}]范围内." +
|
||||||
$"输出格式 json字符串 对象格式{fileNameResFormat}";
|
$"输出格式 json字符串 对象格式{fileNameResFormat}";
|
||||||
var fileNameInfoRes = await ChatAsync<FileNameInfo>(task, fileNamePostMessages, null);//, "deepseek-chat");
|
var fileNameInfoRes = await ChatAsync<FileNameInfo>
|
||||||
|
(task, fileNamePostMessages, null);//, "deepseek-chat");
|
||||||
|
|
||||||
var speakerArr = JsonSerializer.Deserialize<OfflineSpeakerRes[]>(taskInfo.Speaker);
|
var speakerArr = JsonSerializer.Deserialize<OfflineSpeakerRes[]>(taskInfo.Speaker);
|
||||||
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions);
|
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions);
|
||||||
|
|
@ -70,31 +83,42 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
var maxVideoTime = captions?.TimeBase?.LastOrDefault()?.End ?? 0;
|
var maxVideoTime = captions?.TimeBase?.LastOrDefault()?.End ?? 0;
|
||||||
var criteriaBuilder = new StringBuilder();
|
var criteriaBuilder = new StringBuilder();
|
||||||
|
|
||||||
var resFormat = """[{"StartTime":开始秒(number),"Theme":主题(string),"KnowPoint":主题方法点(string),"KnowPointId":主题方法点Id(string)"Content":内容总结(string)}]""";
|
var know = await knowledgeInfoDB.GetFirstAsync(s => s.Course_Id == Course_Id && s.Name == fileNameInfoRes.授课章节);
|
||||||
var know = await knowledgeInfoDB.GetFirstAsync(s =>s.Course_Id==27 && s.Name == fileNameInfoRes.授课章节);
|
|
||||||
if (know is null)
|
if (know is null)
|
||||||
throw new Exception("未能找到对应知识点=>"+ fileNameInfoRes.授课章节);
|
throw new Exception("未能找到对应知识点=>" + fileNameInfoRes.授课章节);
|
||||||
var knowledgeInfos = await knowledgeInfoDB.AsQueryable().ToChildListAsync(s => s.Parent_Id, know.Id);
|
//提升到父级
|
||||||
var knows = string.Join(',', knowledgeInfos.Select(s => s.Id + "|"+ s.Name));
|
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
|
var knowDic = knowledgeInfos
|
||||||
.OrderBy(s=>s.Id)
|
.OrderBy(s => s.Id)
|
||||||
.GroupBy(s=>s.Name)
|
.GroupBy(s => s.Name)
|
||||||
.ToDictionary(s => s.First().Name, s => s.First().Id);
|
.ToDictionary(s => s.First().Name, s => s.First().Id);
|
||||||
VideoKnowRes[] questionRes;
|
VideoKnowRes[] questionRes;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
// var postMessages =
|
||||||
|
//$"你的任务是分析视频字幕内容并提取出中国高考考试试题方法点,然后分析出<知识块>,来帮助学生快速了解视频字幕的内容" +
|
||||||
|
//$"通过阅读并理解字幕内容.然后识别出{subject}学科中属于{fileNameInfoRes.授课章节}章节相关的方法点以及对应的时间段。" +
|
||||||
|
//$"关联合并知识内容相似的知识点来合并为<知识块>。" +
|
||||||
|
//$"分配空余未使用的时间段到内容相近的<知识块>时间区间来获取更加详细的上下文,但是请避免<知识块>之间时间重合。" +
|
||||||
|
//$"从提取出的<知识块>内容称来匹配我提供的知识点,来作为片段的知识点(请确保匹配的知识点是用户提供的,否则片段知识点值为空字符串)。" +
|
||||||
|
//$"提供的方法点名称({knows})。 格式 (方法点Id|方法点名称) 如果一个<知识块>出现多个知识点那么知识点Id与知识点名称都用逗号','分割。" +
|
||||||
|
//$"这是输入的视频字幕并且是包含时间戳的视频字幕的固定格式文本。" +
|
||||||
|
////$"字幕格式(说话人:开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" +
|
||||||
|
//$"字幕格式(开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" +
|
||||||
|
//$"最后请检查某些<知识块>之间的过渡是否自然,如果<知识块>时长超过500秒则考虑拆封为两个更加贴切的<知识块>.或者<知识块>时长小于30秒则考虑合并<知识块>到相邻的<知识块>)。" +
|
||||||
|
//$"输出内容只返回json格式({resFormat})";
|
||||||
|
|
||||||
|
var resFormat = """[{"StartTime":开始秒(number),"Theme":主题(string),"Content":内容总结(string)}]""";
|
||||||
var postMessages =
|
var postMessages =
|
||||||
$"你的任务是分析视频字幕内容并提取出中国高考考试试题方法点,然后分析出<知识块>,来帮助学生快速了解视频字幕的内容" +
|
$"你的任务是分析视频字幕内容并提取出中国高考考试试题方法点,然后分析出<知识块>,来帮助学生快速了解视频字幕的内容" +
|
||||||
$"通过阅读并理解字幕内容.然后识别出{subject}学科中属于{fileNameInfoRes.授课章节}章节相关的方法点以及对应的时间段。" +
|
$"通过阅读并理解字幕内容.然后识别出{subject}学科中属于{fileNameInfoRes.授课章节}章节相关的时间段。" +
|
||||||
$"关联合并知识内容相似的知识点来合并为<知识块>。" +
|
$"关联合并知识内容相似的知识点来合并为<知识块>。" +
|
||||||
$"分配空余未使用的时间段到内容相近的<知识块>时间区间来获取更加详细的上下文,但是请避免<知识块>之间时间重合。" +
|
$"分配空余未使用的时间段到内容相近的<知识块>时间区间来获取更加详细的上下文,但是请避免<知识块>之间时间重合。" +
|
||||||
$"从提取出的<知识块>内容称来匹配我提供的知识点,来作为片段的知识点(请确保匹配的知识点是用户提供的,否则片段知识点值为空字符串)。" +
|
|
||||||
$"提供的方法点名称({knows})。 格式 (方法点Id|方法点名称) 如果一个<知识块>出现多个知识点那么知识点Id与知识点名称都用逗号','分割。" +
|
|
||||||
$"这是输入的视频字幕并且是包含时间戳的视频字幕的固定格式文本。" +
|
|
||||||
//$"字幕格式(说话人:开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" +
|
|
||||||
$"字幕格式(开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" +
|
$"字幕格式(开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" +
|
||||||
$"最后请检查某些<知识块>之间的过渡是否自然,如果<知识块>时长超过500秒则考虑拆封为两个更加贴切的<知识块>.或者<知识块>时长小于30秒则考虑合并<知识块>到相邻的<知识块>)。" +
|
$"最后请检查某些<知识块>之间的过渡是否自然,如果<知识块>时长超过500秒则考虑拆封为两个更加贴切的<知识块>.或者<知识块>时长小于30秒则考虑合并<知识块>到相邻的<知识块>)。" +
|
||||||
$"输出格式({resFormat})";
|
$"输出内容只返回json格式({resFormat})";
|
||||||
|
|
||||||
Console.WriteLine(DateTime.Now + "=>开始分析视频内容");
|
Console.WriteLine(DateTime.Now + "=>开始分析视频内容");
|
||||||
|
|
||||||
|
|
@ -103,37 +127,58 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
if (questionRes.Length <= 3)
|
if (questionRes.Length <= 3)
|
||||||
throw new Exception("视频分段数量过低 =>" + questionRes.Length);
|
throw new Exception("视频分段数量过低 =>" + questionRes.Length);
|
||||||
questionRes = questionRes.OrderBy(s => s.StartTime).ToArray();
|
questionRes = questionRes.OrderBy(s => s.StartTime).ToArray();
|
||||||
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;
|
|
||||||
}
|
|
||||||
var thems = string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme));
|
|
||||||
|
|
||||||
var checkResFormat = """{"Score":打分(number),"Evaluation":评价(string),"Data":优化后的分段(array)}""";
|
var thems = JsonSerializer.Serialize(questionRes.Adapt<VideoKnowQueryDto[]>());// string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme));
|
||||||
var checkResFormat1 = """[{"Start":开始秒(number),"Theme":优化后的分段主题(string)}]""";
|
var checkResFormat1 = """[{"StartTime":开始秒(number),"KnowPoint":知识点名称(string),"KnowPointId":知识点Id(string)}]""";
|
||||||
var checkMessage = "我为视频的讲解内容做了一些分段,你能帮我检查下这些分段的时间分配是否合理,标题内容符合实际吗?" +
|
var knowStr = string.Join(',', knowledgeInfos.Select(s => s.Name));
|
||||||
$"请给出你的打分(0-100,70分及格)以及打分原因,并且给出优化后的分段 分段格式(${checkResFormat1})" +
|
var knowMessages =
|
||||||
|
$"我针对视频<{title}>分析出了一些视频的知识片段,现在需要你帮我将每个片段分配恰当的知识点(单个片段允许多个知识点用逗号','分割)。" +
|
||||||
|
$"这是我的分段 {thems}。" +
|
||||||
|
$"提供的知识点名称({knows})。 格式 (方法点Id|方法点名称) " +
|
||||||
|
$"最后请确保分配的知识点是用户提供的,否则片段知识点值留空!。" +
|
||||||
|
$"输出内容只返回json格式({checkResFormat1})";
|
||||||
|
Console.WriteLine(DateTime.Now + "=>开始分析视频内容知识点");
|
||||||
|
var konwRes = await ChatAsync<VideoKnowRes[]>(task, knowMessages, null);
|
||||||
|
|
||||||
|
|
||||||
|
var checkResFormat = """{"Score":打分(number),"Evaluation":评价(string)""";//,"Data":优化后的分段(array)}""";
|
||||||
|
var checkMessage = "我为视频的讲解内容做了一些分段,你能帮我检查下这些分段的时间,主题,知识点分配是否合理符合实际吗?" +
|
||||||
|
$"请给出你的打分(0-100,70分及格)以及打分原因" +//,并且给出优化后的分段 分段格式(${checkResFormat1})" +
|
||||||
|
//$"如果不合理的话强帮我修改优化他们(注意优化知识点时只允许使用已存在的知识点不允许杜撰猜测捏造)" +
|
||||||
$"这是我的分段 {thems}." +
|
$"这是我的分段 {thems}." +
|
||||||
$"后续的内容是包含时间戳的视频字幕的固定格式文本。" +
|
$"后续的内容是包含时间戳的视频字幕的固定格式文本。" +
|
||||||
$"字幕格式(说话人:开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" +
|
$"字幕格式(说话人:开始秒:结束秒:内容|下一段字幕).以下是包含时间的视频字幕文本。字幕列表 {captions.Captions}。" +
|
||||||
$"输出格式为json({checkResFormat})";
|
$"最后输出格式为json({checkResFormat})";
|
||||||
|
|
||||||
Console.WriteLine(DateTime.Now + "=>开始检查视频分段结果");
|
Console.WriteLine(DateTime.Now + "=>开始检查视频分段结果");
|
||||||
var checkRes = await ChatAsync <CheckMessageDto>(task, checkMessage, null);
|
var checkRes = await ChatAsync<CheckMessageDto>(task, checkMessage, null);
|
||||||
if (checkRes != null && checkRes.Score >= 80)
|
if (checkRes != null && checkRes.Score >= 80 && questionRes.Count() == checkRes.Data.Count())
|
||||||
|
{
|
||||||
|
for (int i = 0; i < checkRes.Data.Count(); i++)
|
||||||
|
{
|
||||||
|
questionRes[i].Theme = checkRes.Data[i].Theme;
|
||||||
|
questionRes[i].StartTime = checkRes.Data[i].StartTime;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine(DateTime.Now + $"=>{task} 得分过低 " + checkRes?.Score );
|
|
||||||
Console.WriteLine( checkRes.Evaluation);
|
Console.WriteLine(DateTime.Now + $"=>{task} 得分过低/分段长度不匹配 得分{checkRes?.Score} 长度 {questionRes.Count()}/{checkRes.Data.Count()}");
|
||||||
|
Console.WriteLine(checkRes.Evaluation);
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
//todo 未包含的知识点片段 如何处理
|
//todo 未包含的知识点片段 如何处理
|
||||||
var insertData = questionRes
|
var insertData = questionRes
|
||||||
.Where(s => !string.IsNullOrEmpty(s.KnowPoint))
|
.Where(s => !string.IsNullOrEmpty(s.KnowPoint))
|
||||||
|
|
@ -169,7 +214,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task);
|
RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task);
|
||||||
return gptRes;
|
return gptRes;
|
||||||
}
|
}
|
||||||
public async Task<T> ChatAsync<T>(string task, string postMessages, string postMessages1, string model= "deepseek-reasoner")
|
public async Task<T> ChatAsync<T>(string task, string postMessages, string postMessages1, string model = "deepseek-reasoner")
|
||||||
{
|
{
|
||||||
var maxTokens = 4000;
|
var maxTokens = 4000;
|
||||||
Message[] messageArr = [
|
Message[] messageArr = [
|
||||||
|
|
@ -180,7 +225,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
var chatRep = new ChatRequest
|
var chatRep = new ChatRequest
|
||||||
{
|
{
|
||||||
model = model,
|
model = model,
|
||||||
stream = model== "deepseek-reasoner",
|
stream = model == "deepseek-reasoner",
|
||||||
max_tokens = maxTokens,
|
max_tokens = maxTokens,
|
||||||
temperature = 0.2f,
|
temperature = 0.2f,
|
||||||
messages = messageArr
|
messages = messageArr
|
||||||
|
|
@ -200,17 +245,32 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
chatResContent = chatResContent?.Replace("```", "");
|
chatResContent = chatResContent?.Replace("```", "");
|
||||||
chatResContent = chatResContent?.Replace("}{", "},{");
|
chatResContent = chatResContent?.Replace("}{", "},{");
|
||||||
chatResContent = chatResContent?.Replace("}|{", "},{");
|
chatResContent = chatResContent?.Replace("}|{", "},{");
|
||||||
chatResContent = chatResContent?.Trim();
|
chatResContent = chatResContent?.Trim().ExtractJson().FirstOrDefault();
|
||||||
|
|
||||||
var startsStr = typeof(T).IsArray ? "[" : "{";
|
var startsStr = typeof(T).IsArray ? "[" : "{";
|
||||||
var endStr = typeof(T).IsArray ? "]" : "}";
|
var endStr = typeof(T).IsArray ? "]" : "}";
|
||||||
if (!chatResContent.StartsWith(startsStr))
|
if (!chatResContent.StartsWith(startsStr))
|
||||||
chatResContent = startsStr + chatResContent;
|
chatResContent = startsStr + chatResContent;
|
||||||
if (!chatResContent.EndsWith(endStr))
|
if (!chatResContent.EndsWith(endStr))
|
||||||
chatResContent = chatResContent + endStr;
|
chatResContent = chatResContent + endStr;
|
||||||
var questionRes = JsonSerializer.Deserialize<T>(chatResContent);
|
var options = new JsonSerializerOptions
|
||||||
if (questionRes is null)
|
{
|
||||||
throw new Exception("ChatGPT返回无效结果");
|
// 允许解析不严格符合 JSON 规范的字符串
|
||||||
return questionRes;
|
AllowTrailingCommas = true,
|
||||||
|
// 处理不匹配的 JSON 字符
|
||||||
|
ReadCommentHandling = JsonCommentHandling.Skip
|
||||||
|
};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var questionRes = JsonSerializer.Deserialize<T>(chatResContent, options);
|
||||||
|
if (questionRes is null)
|
||||||
|
throw new Exception("ChatGPT返回无效结果");
|
||||||
|
return questionRes;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception("ChatGPT结果解析错误 " + ex.Message + ex.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,33 +10,53 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace VideoAnalysisCore.AICore.GPT.Dto
|
namespace VideoAnalysisCore.AICore.GPT.Dto
|
||||||
{
|
{
|
||||||
|
public class VideoKnowQueryDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 问题解释
|
||||||
|
/// </summary>
|
||||||
|
public virtual float? StartTime { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 主题
|
||||||
|
/// </summary>
|
||||||
|
public virtual string? Theme { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 知识点
|
||||||
|
/// </summary>
|
||||||
|
public virtual string? KnowPoint { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 内容总结
|
||||||
|
/// </summary>
|
||||||
|
public virtual string? Content { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
public class VideoKnowRes
|
public class VideoKnowRes
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 问题解释
|
/// 问题解释
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float? StartTime { get; set; }
|
public virtual float? StartTime { get; set; }
|
||||||
public float? EndTime { get; set; }
|
public virtual float? EndTime { get; set; }
|
||||||
///// <summary>
|
/// <summary>
|
||||||
///// 持续时间
|
/// 持续时间
|
||||||
///// </summary>
|
/// </summary>
|
||||||
public float? KeepTime => (EndTime ?? 0) - StartTime ?? 0;
|
public virtual float? KeepTime => (EndTime ?? 0) - StartTime ?? 0;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主题
|
/// 主题
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Theme { get; set; }
|
public virtual string? Theme { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 知识点
|
/// 知识点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? KnowPoint { get; set; }
|
public virtual string? KnowPoint { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 知识点ID
|
/// 知识点ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? KnowPointId { get; set; }
|
public virtual string? KnowPointId { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内容总结
|
/// 内容总结
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Content { get; set; }
|
public virtual string? Content { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
public class FileNameInfo
|
public class FileNameInfo
|
||||||
|
|
|
||||||
|
|
@ -262,10 +262,11 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
Console.WriteLine(DateTime.Now + "=> SenseVoice 字幕数量"+ res.Count);
|
Console.WriteLine(DateTime.Now + "=> SenseVoice 字幕数量"+ res.Count);
|
||||||
|
|
||||||
var captionsStr = JsonSerializer.Serialize(res);
|
var captionsStr = JsonSerializer.Serialize(res);
|
||||||
DbScoped.SugarScope
|
await DbScoped.SugarScope
|
||||||
.Updateable<VideoTask>()
|
.Updateable<VideoTask>()
|
||||||
.SetColumns(it => it.Captions == captionsStr)
|
.SetColumns(it => it.Captions == captionsStr)
|
||||||
.Where(it => it.Id == long.Parse(task));
|
.Where(it => it.Id == long.Parse(task))
|
||||||
|
.ExecuteCommandAsync();
|
||||||
await RedisExpand.Redis.HMSetAsync(RedisExpandKey.Task(task), "Captions", res);
|
await RedisExpand.Redis.HMSetAsync(RedisExpandKey.Task(task), "Captions", res);
|
||||||
//RedisExpand.InsertChannel(Enum.RedisChannelEnum.ParsingSpeaker, task);
|
//RedisExpand.InsertChannel(Enum.RedisChannelEnum.ParsingSpeaker, task);
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.ChatModelAnalysis, task);
|
RedisExpand.InsertChannel(RedisChannelEnum.ChatModelAnalysis, task);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using SqlSugar.IOC;
|
||||||
using VideoAnalysisCore.Model;
|
using VideoAnalysisCore.Model;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using VideoAnalysisCore.Model.Enum;
|
using VideoAnalysisCore.Model.Enum;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace VideoAnalysisCore.AICore.SherpaOnnx
|
namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
{
|
{
|
||||||
|
|
@ -16,7 +17,7 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
/// 说话人模型
|
/// 说话人模型
|
||||||
/// <para>pyannote </para>
|
/// <para>pyannote </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Speaker
|
public static class Speaker
|
||||||
{
|
{
|
||||||
private static OfflineSpeakerDiarization? SD;
|
private static OfflineSpeakerDiarization? SD;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -25,9 +26,9 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
/// <param name="speakerNumber"></param>
|
/// <param name="speakerNumber"></param>
|
||||||
/// <param name="threshold"></param>
|
/// <param name="threshold"></param>
|
||||||
/// <param name="useGPU"></param>
|
/// <param name="useGPU"></param>
|
||||||
public static void Init(int speakerNumber = 2, float threshold = 0.6f,bool useGPU = false)
|
public static void AddSpeakerAI(this IServiceCollection service,int speakerNumber = 2, float threshold = 0.6f,bool useGPU = false)
|
||||||
{
|
{
|
||||||
Console.WriteLine("初始化 Speaker");
|
Console.WriteLine($"{DateTime.Now}=>初始化 Speaker");
|
||||||
var config = new OfflineSpeakerDiarizationConfig();
|
var config = new OfflineSpeakerDiarizationConfig();
|
||||||
//Pyannote模型地址
|
//Pyannote模型地址
|
||||||
config.Segmentation.Pyannote.Model = Path.Combine(AppCommon.AIModelFile, "sherpa-onnx-pyannote-segmentation-3-0", "model.onnx");
|
config.Segmentation.Pyannote.Model = Path.Combine(AppCommon.AIModelFile, "sherpa-onnx-pyannote-segmentation-3-0", "model.onnx");
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AlibabaCloud.OpenApiClient.Models;
|
||||||
|
using AlibabaCloud.SDK.Vod20170321;
|
||||||
|
using AlibabaCloud.SDK.Vod20170321.Models;
|
||||||
|
using AlibabaCloud.TeaUtil.Models;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using VideoAnalysisCore.Job;
|
||||||
|
|
||||||
|
namespace VideoAnalysisCore.Common
|
||||||
|
{
|
||||||
|
public class AlibabaCloudVodConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// id
|
||||||
|
/// </summary>
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
///密钥
|
||||||
|
/// </summary>
|
||||||
|
public string AccessKeySecret { get; set; }
|
||||||
|
public string Endpoint { get; set; } = "vod.cn-shanghai.aliyuncs.com";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 阿里云 视频点播拓展
|
||||||
|
/// </summary>
|
||||||
|
public static class AlibabaCloudVodExpand
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 使用阿里云 vod拓展
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="service"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static void AddAlibabaCloudVod(this IServiceCollection service)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{DateTime.Now}=>初始化 阿里云VOD");
|
||||||
|
// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
|
||||||
|
// 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378671.html。
|
||||||
|
Config config = new()
|
||||||
|
{
|
||||||
|
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
|
||||||
|
AccessKeyId = AppCommon.Config.AlibabaCloudVod.AccessKeyId,
|
||||||
|
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
|
||||||
|
AccessKeySecret = AppCommon.Config.AlibabaCloudVod.AccessKeySecret,
|
||||||
|
Endpoint = AppCommon.Config.AlibabaCloudVod.Endpoint
|
||||||
|
};
|
||||||
|
// Endpoint 请参考 https://api.aliyun.com/product/vod
|
||||||
|
var c = new Client(config);
|
||||||
|
service.AddSingleton(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -87,6 +87,50 @@ namespace VideoAnalysisCore.Common
|
||||||
static Dictionary<string, string> FormulaData;
|
static Dictionary<string, string> FormulaData;
|
||||||
static string FormulaDataKey;
|
static string FormulaDataKey;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// 识别字符串中的json字符串
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<string> ExtractJson(this string input)
|
||||||
|
{
|
||||||
|
List<string> jsonList = new List<string>();
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while (index < input.Length)
|
||||||
|
{
|
||||||
|
if (input[index] == '{' || input[index] == '[')
|
||||||
|
{
|
||||||
|
int startIndex = index;
|
||||||
|
int openCount = 1;
|
||||||
|
index++;
|
||||||
|
|
||||||
|
while (index < input.Length && openCount > 0)
|
||||||
|
{
|
||||||
|
if (input[index] == '{' || input[index] == '[')
|
||||||
|
{
|
||||||
|
openCount++;
|
||||||
|
}
|
||||||
|
else if (input[index] == '}' || input[index] == ']')
|
||||||
|
{
|
||||||
|
openCount--;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openCount == 0)
|
||||||
|
{
|
||||||
|
string json = input.Substring(startIndex, index - startIndex);
|
||||||
|
jsonList.Add(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonList;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
/// 处理数学公式
|
/// 处理数学公式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="f"></param>
|
/// <param name="f"></param>
|
||||||
|
|
@ -399,6 +443,10 @@ namespace VideoAnalysisCore.Common
|
||||||
/// ChatGpt
|
/// ChatGpt
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ChatGptConfig ChatGpt { get; set; } = new ChatGptConfig();
|
public ChatGptConfig ChatGpt { get; set; } = new ChatGptConfig();
|
||||||
|
/// <summary>
|
||||||
|
/// 阿里云视频点播配置
|
||||||
|
/// </summary>
|
||||||
|
public AlibabaCloudVodConfig AlibabaCloudVod { get; set; } = new AlibabaCloudVodConfig();
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using AntDesign;
|
using AntDesign;
|
||||||
using Downloader;
|
using Downloader;
|
||||||
|
using SqlSugar;
|
||||||
using SqlSugar.IOC;
|
using SqlSugar.IOC;
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
@ -106,9 +107,11 @@ namespace VideoAnalysisCore.Common
|
||||||
{
|
{
|
||||||
if (Opt is null)
|
if (Opt is null)
|
||||||
Init();
|
Init();
|
||||||
|
var taskId = long.Parse(task);
|
||||||
//获取资源文件 地址
|
//获取资源文件 地址
|
||||||
var fileUrl = RedisExpand.Redis.HMGet(RedisExpandKey.Task(task), "MediaUrl")
|
var fileUrl =await DbScoped.SugarScope.Queryable<VideoTask>()
|
||||||
.FirstOrDefault();
|
.Where(s => s.Id == taskId)
|
||||||
|
.Select(s=>s.MediaUrl).FirstAsync();
|
||||||
if (string.IsNullOrEmpty(fileUrl))
|
if (string.IsNullOrEmpty(fileUrl))
|
||||||
throw new Exception($"任务id[{task}] 资源地址无效 {fileUrl}");
|
throw new Exception($"任务id[{task}] 资源地址无效 {fileUrl}");
|
||||||
|
|
||||||
|
|
@ -125,10 +128,11 @@ namespace VideoAnalysisCore.Common
|
||||||
var outputPath = Path.Combine(localPath, task + fileExtension);
|
var outputPath = Path.Combine(localPath, task + fileExtension);
|
||||||
if (!Directory.Exists(localPath)) Directory.CreateDirectory(localPath);
|
if (!Directory.Exists(localPath)) Directory.CreateDirectory(localPath);
|
||||||
|
|
||||||
DbScoped.SugarScope
|
await DbScoped.SugarScope
|
||||||
.Updateable<VideoTask>()
|
.Updateable<VideoTask>()
|
||||||
.SetColumns(it => it.LocalMediaPath == outputPath)
|
.SetColumns(it => it.LocalMediaPath == outputPath)
|
||||||
.Where(it => it.Id == long.Parse(task));
|
.Where(it => it.Id == long.Parse(task))
|
||||||
|
.ExecuteCommandAsync();
|
||||||
|
|
||||||
IDownload download = DownloadBuilder.New()
|
IDownload download = DownloadBuilder.New()
|
||||||
.WithUrl(fileUrl)
|
.WithUrl(fileUrl)
|
||||||
|
|
@ -147,7 +151,7 @@ namespace VideoAnalysisCore.Common
|
||||||
{
|
{
|
||||||
if (download.Status == DownloadStatus.Failed && e.Error != null)
|
if (download.Status == DownloadStatus.Failed && e.Error != null)
|
||||||
{
|
{
|
||||||
await RedisExpand.SetTaskErrorMessage(long.Parse(task), e.Error)
|
await RedisExpand.SetTaskErrorMessage(taskId, e.Error)
|
||||||
.ConfigureAwait(false);//不切回上下文
|
.ConfigureAwait(false);//不切回上下文
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,14 +67,14 @@ namespace VideoAnalysisCore.Common
|
||||||
/// 任务对象地址
|
/// 任务对象地址
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Task(object taskId) => BaseKey + "Task:" + taskId;
|
public static string Task(object taskId) => BaseKey + "Task:" + taskId;
|
||||||
public static string IDTask => BaseKey + AppCommon.Config.ID;
|
public static string IDTask => BaseKey + "Services:" + AppCommon.Config.ID;
|
||||||
public static string TaskGPT(object taskId) => Task(taskId) + ":GPTCached";
|
public static string TaskGPT(object taskId) => Task(taskId) + ":GPTCached";
|
||||||
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// redis拓展
|
/// redis拓展
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RedisExpand
|
public static class RedisExpand
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// redis 连接
|
/// redis 连接
|
||||||
|
|
@ -90,9 +90,9 @@ namespace VideoAnalysisCore.Common
|
||||||
/// 初始化 redis
|
/// 初始化 redis
|
||||||
/// <para>需要在初始化配置文件时候调用</para>
|
/// <para>需要在初始化配置文件时候调用</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void Init()
|
public static void AddRedisExpand(this IServiceCollection service)
|
||||||
{
|
{
|
||||||
Console.WriteLine("初始化 redis");
|
Console.WriteLine($"{DateTime.Now}=>初始化 Redis");
|
||||||
Redis.Serialize = obj => JsonSerializer.Serialize(obj);
|
Redis.Serialize = obj => JsonSerializer.Serialize(obj);
|
||||||
Redis.Deserialize = (json, type) => JsonSerializer.Deserialize(json, type);
|
Redis.Deserialize = (json, type) => JsonSerializer.Deserialize(json, type);
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
|
|
@ -117,10 +117,16 @@ namespace VideoAnalysisCore.Common
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 加入到消费队列
|
/// 加入到消费队列
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="taskId"></param>
|
/// <param name="taskIds"></param>
|
||||||
public static void JoinQueue(params long[] taskId)
|
public static void JoinQueue(params long[] taskIds)
|
||||||
{
|
{ //事务
|
||||||
Redis.LPush(RedisExpandKey.ChannelKey, taskId);
|
using (var tran = Redis.Multi())
|
||||||
|
{
|
||||||
|
foreach (var item in taskIds)
|
||||||
|
tran.LPush(RedisExpandKey.ChannelKey, item);
|
||||||
|
object[] ret = tran.Exec();
|
||||||
|
Console.WriteLine(ret[0] + ", " + ret[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取任务进度
|
/// 获取任务进度
|
||||||
|
|
@ -179,7 +185,7 @@ namespace VideoAnalysisCore.Common
|
||||||
if (taskData.Captions == "[]")
|
if (taskData.Captions == "[]")
|
||||||
taskData.Captions = (await Redis.HMGetAsync(RedisExpandKey.Task(task), "Captions")).First();
|
taskData.Captions = (await Redis.HMGetAsync(RedisExpandKey.Task(task), "Captions")).First();
|
||||||
if (taskData.Speaker == "[]")
|
if (taskData.Speaker == "[]")
|
||||||
taskData.Speaker = (await Redis.HMGetAsync(RedisExpandKey.Task(task), "Speaker")).First();
|
taskData.Speaker = (await Redis.HMGetAsync(RedisExpandKey.Task(task), "Speaker"))?.FirstOrDefault()??"[]";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
using Coravel.Invocable;
|
using AlibabaCloud.SDK.Vod20170321;
|
||||||
|
using Coravel.Invocable;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using UserCenter.Model.Enum;
|
||||||
using VideoAnalysisCore.Common;
|
using VideoAnalysisCore.Common;
|
||||||
using VideoAnalysisCore.Model;
|
using VideoAnalysisCore.Model;
|
||||||
using VideoAnalysisCore.Model.Dto;
|
using VideoAnalysisCore.Model.Dto;
|
||||||
using VideoAnalysisCore.Model.蓝鲸智库;
|
using VideoAnalysisCore.Model.蓝鲸智库;
|
||||||
|
using Yitter.IdGenerator;
|
||||||
|
|
||||||
namespace VideoAnalysisCore.Job
|
namespace VideoAnalysisCore.Job
|
||||||
{
|
{
|
||||||
|
|
@ -17,26 +21,32 @@ namespace VideoAnalysisCore.Job
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NodeSubscriptionJob : IInvocable
|
public class NodeSubscriptionJob : IInvocable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 每个扫描文件包每次取出{20}个
|
||||||
|
/// </summary>
|
||||||
|
private readonly int TopLength = 20;
|
||||||
private readonly Repository<NodeSubscription> nodesubscriptionDB;
|
private readonly Repository<NodeSubscription> nodesubscriptionDB;
|
||||||
private readonly Repository<Attachments> attachmentsDB;
|
private readonly Repository<Attachments> attachmentsDB;
|
||||||
private readonly Repository<VideoTask> videotaskDB;
|
private readonly Repository<VideoTask> videotaskDB;
|
||||||
public NodeSubscriptionJob(Repository<Attachments> videoTaskDB, Repository<NodeSubscription> nodesubscriptionDB, Repository<VideoTask> videotaskDB)
|
private readonly Client vodClient;
|
||||||
|
public NodeSubscriptionJob(Repository<Attachments> videoTaskDB, Repository<NodeSubscription> nodesubscriptionDB, Repository<VideoTask> videotaskDB, Client vodClient)
|
||||||
{
|
{
|
||||||
this.attachmentsDB = videoTaskDB;
|
this.attachmentsDB = videoTaskDB;
|
||||||
this.nodesubscriptionDB = nodesubscriptionDB;
|
this.nodesubscriptionDB = nodesubscriptionDB;
|
||||||
this.videotaskDB = videotaskDB;
|
this.videotaskDB = videotaskDB;
|
||||||
|
this.vodClient = vodClient;
|
||||||
}
|
}
|
||||||
public async Task Invoke()
|
public async Task Invoke()
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{DateTime.Now} Invoke=>{this.GetType().FullName}");
|
Console.WriteLine($"{DateTime.Now} Invoke=>{this.GetType().FullName}");
|
||||||
|
|
||||||
var tasks = await nodesubscriptionDB.GetListAsync(s => s.Enable);
|
var tasks = await nodesubscriptionDB.GetListAsync(s => s.Enable && s.Subject ==SubjectEnum.数学);
|
||||||
foreach (var item in tasks)
|
foreach (var item in tasks)
|
||||||
{
|
{
|
||||||
var fileNodeId = item.NodeId;
|
var fileNodeId = item.NodeId;
|
||||||
var data = attachmentsDB.Context.Ado
|
var data = attachmentsDB.Context.Ado
|
||||||
.SqlQuery<Attachments>($"""
|
.SqlQuery<Attachments>($"""
|
||||||
SELECT
|
SELECT top {TopLength}
|
||||||
*
|
*
|
||||||
FROM
|
FROM
|
||||||
Attachments
|
Attachments
|
||||||
|
|
@ -62,22 +72,43 @@ namespace VideoAnalysisCore.Job
|
||||||
AND (
|
AND (
|
||||||
NAME NOT LIKE '%PPT%'
|
NAME NOT LIKE '%PPT%'
|
||||||
OR NAME NOT LIKE '%ppt%'
|
OR NAME NOT LIKE '%ppt%'
|
||||||
)
|
) and id>{item.LastId}
|
||||||
""");
|
""");
|
||||||
|
if (data is null || data.Count == 0)
|
||||||
|
continue;
|
||||||
|
Console.WriteLine($"{DateTime.Now} 视频订阅=>Node:{item.NodeId} 数量{data.Count}");
|
||||||
|
|
||||||
var videos = data.Select(s => new VideoTask()
|
var videos = new List<VideoTask>(data.Count);
|
||||||
|
foreach (var s in data)
|
||||||
{
|
{
|
||||||
ComeFrom = "127.0.0.1",
|
var videoInfo = await vodClient.GetPlayInfoAsync(new AlibabaCloud.SDK.Vod20170321.Models.GetPlayInfoRequest()
|
||||||
ApiToken = "",
|
{
|
||||||
Type = item.TaskType,
|
VideoId = s.VideoCode, Formats="mp4",OutputType = "cdn", AuthTimeout = 3600 * 24 * 3,
|
||||||
Subject = item.Subject,
|
});
|
||||||
Tag = string.Empty,
|
if (videoInfo is null || videoInfo.StatusCode != 200 && !videoInfo.Body.PlayInfoList.PlayInfo.Any())
|
||||||
TagId = s.VideoCode,
|
{
|
||||||
MediaUrl = s.Url,
|
Console.WriteLine($"{DateTime.Now} 视频订阅=>获取阿里云视频信息失败 VideoCode {s.VideoCode} StatusCode {videoInfo?.StatusCode}");
|
||||||
MediaName = s.Name
|
continue;
|
||||||
}).ToArray();
|
}
|
||||||
|
videos.Add(new VideoTask()
|
||||||
//入库
|
{
|
||||||
|
Id = YitIdHelper.NextId(),
|
||||||
|
ComeFrom = "127.0.0.1",
|
||||||
|
ApiToken = "",
|
||||||
|
Type = item.TaskType,
|
||||||
|
Subject = item.Subject,
|
||||||
|
Tag = item.NodeId.ToString(),
|
||||||
|
TagId = s.VideoCode,
|
||||||
|
MediaUrl = videoInfo.Body.PlayInfoList.PlayInfo.First().PlayURL,
|
||||||
|
MediaName = s.Name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var maxId = data.Max(s => s.Id);
|
||||||
|
//入库 更新最后扫描记录
|
||||||
|
await nodesubscriptionDB.AsUpdateable()
|
||||||
|
.SetColumns(it => it.LastId == maxId)
|
||||||
|
.Where(it => it.Id == item.Id)
|
||||||
|
.ExecuteCommandAsync();
|
||||||
await videotaskDB.InsertRangeAsync(videos);
|
await videotaskDB.InsertRangeAsync(videos);
|
||||||
var ids = videos.Select(s => s.Id).ToArray();
|
var ids = videos.Select(s => s.Id).ToArray();
|
||||||
RedisExpand.JoinQueue(ids);
|
RedisExpand.JoinQueue(ids);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
using AlibabaCloud.SDK.Vod20170321;
|
||||||
|
using Coravel.Invocable;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UserCenter.Model.Enum;
|
||||||
|
using VideoAnalysisCore.Common;
|
||||||
|
using VideoAnalysisCore.Model;
|
||||||
|
using VideoAnalysisCore.Model.Dto;
|
||||||
|
using VideoAnalysisCore.Model.Interface;
|
||||||
|
using VideoAnalysisCore.Model.蓝鲸智库;
|
||||||
|
using Yitter.IdGenerator;
|
||||||
|
|
||||||
|
namespace VideoAnalysisCore.Job
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 下载视频的任务文件清理任务
|
||||||
|
/// </summary>
|
||||||
|
public class TaskFileClearJob : IInvocable
|
||||||
|
{
|
||||||
|
private readonly Repository<VideoTask> videotaskDB;
|
||||||
|
|
||||||
|
public TaskFileClearJob(Repository<VideoTask> videotaskDB)
|
||||||
|
{
|
||||||
|
this.videotaskDB = videotaskDB;
|
||||||
|
}
|
||||||
|
public void DeleteOldCompletedTaskCaches()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var startTime = -2;
|
||||||
|
var timeSpan = startTime - 3;
|
||||||
|
// 计算 2 天前的时间
|
||||||
|
DateTime twoDaysAgo = DateTime.Now.AddDays(startTime);
|
||||||
|
DateTime endDaysAgo = DateTime.Now.AddDays(timeSpan);
|
||||||
|
|
||||||
|
// 查询 2 天前任务执行完成的记录
|
||||||
|
var completedTasks = videotaskDB.AsQueryable()
|
||||||
|
.Where(t =>
|
||||||
|
t.LastEnum == Model.Enum.RedisChannelEnum.EndTask
|
||||||
|
&& t.CreateTime < twoDaysAgo
|
||||||
|
&& t.CreateTime > endDaysAgo)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 遍历查询结果,删除缓存文件
|
||||||
|
foreach (var task in completedTasks)
|
||||||
|
{
|
||||||
|
var path = task.Id.ToString().LocalPath();
|
||||||
|
if (!string.IsNullOrEmpty(path) && Directory.Exists(path))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.Delete(path, true);
|
||||||
|
Console.WriteLine($"已删除缓存文件: {task.LocalMediaPath}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"删除缓存文件 {task.Id} 时出错: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"清理缓存时出错: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async Task Invoke()
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{DateTime.Now} Invoke=>{this.GetType().FullName}");
|
||||||
|
|
||||||
|
DeleteOldCompletedTaskCaches();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -57,6 +57,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AlibabaCloud.SDK.Vod20170321" Version="3.6.1" />
|
||||||
<PackageReference Include="Coravel" Version="6.0.2" />
|
<PackageReference Include="Coravel" Version="6.0.2" />
|
||||||
<PackageReference Include="FreeRedis" Version="1.3.2" />
|
<PackageReference Include="FreeRedis" Version="1.3.2" />
|
||||||
<PackageReference Include="Downloader" Version="3.2.1" />
|
<PackageReference Include="Downloader" Version="3.2.1" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue