Compare commits
4 Commits
d433d84d46
...
4ab527c388
| Author | SHA1 | Date |
|---|---|---|
|
|
4ab527c388 | |
|
|
19855abb6f | |
|
|
f7c787cdf7 | |
|
|
f943c4fec4 |
|
|
@ -34,6 +34,7 @@ namespace Learn.VideoAnalysis.API
|
||||||
//初始化 插件
|
//初始化 插件
|
||||||
builder.Configuration.AddAppConfig(args);
|
builder.Configuration.AddAppConfig(args);
|
||||||
|
|
||||||
|
builder.Services.AddHttpClient();
|
||||||
builder.Services.AddSqlSugarExpand();
|
builder.Services.AddSqlSugarExpand();
|
||||||
builder.Services.AddRedisExpand();
|
builder.Services.AddRedisExpand();
|
||||||
builder.Services.AddCoravel();
|
builder.Services.AddCoravel();
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
@using VideoAnalysisCore.Model.Dto
|
@using VideoAnalysisCore.Model.Dto
|
||||||
@using VideoAnalysisCore.Model.Enum;
|
@using VideoAnalysisCore.Model.Enum;
|
||||||
|
|
||||||
|
|
||||||
<Table @ref="_table" Loading="tableLoading" TItem="VideoTaskDto" ScrollY="600px" PageSize="10" Total="_total" DataSource="_dataSource"
|
<Table @ref="_table" Loading="tableLoading" TItem="VideoTaskDto" ScrollY="600px" PageSize="10" Total="_total" DataSource="_dataSource"
|
||||||
OnRowClick="(r)=>r.Expanded = !r.Expanded"
|
OnRowClick="(r)=>r.Expanded = !r.Expanded"
|
||||||
@bind-SelectedRows="_selectedRows" OnChange="OnChange"
|
@bind-SelectedRows="_selectedRows" OnChange="OnChange"
|
||||||
|
|
@ -20,15 +21,14 @@
|
||||||
<ColumnDefinitions Context="row">
|
<ColumnDefinitions Context="row">
|
||||||
<Selection />
|
<Selection />
|
||||||
<PropertyColumn Property="c=>c.Id" Width="110px" Filterable="true" Sortable="true" />
|
<PropertyColumn Property="c=>c.Id" Width="110px" Filterable="true" Sortable="true" />
|
||||||
<PropertyColumn Property="c=>c.MediaName" Width="200px" />
|
|
||||||
<PropertyColumn Property="c=>c.TagId" Width="160px" />
|
<PropertyColumn Property="c=>c.TagId" Width="160px" />
|
||||||
<PropertyColumn Property="c=>c.LastEnum" Width="150px" />
|
<PropertyColumn Property="c=>c.LastEnum" Width="150px" />
|
||||||
<PropertyColumn Property="c=>c.Subject" Width="100px" />
|
<PropertyColumn Property="c=>c.Subject" Width="100px" />
|
||||||
<PropertyColumn Property="c=>c.ComeFrom" Width="100px" />
|
<PropertyColumn Property="c=>c.ComeFrom" Width="100px" />
|
||||||
<PropertyColumn Property="c=>c.MediaUrl" Width="320px" />
|
<PropertyColumn Property="c=>c.MediaUrl" Width="320px" />
|
||||||
<PropertyColumn Property="c=>c.TotalTokens" Width="100px" />
|
|
||||||
<PropertyColumn Property="c=>c.CreateTime" />
|
<PropertyColumn Property="c=>c.CreateTime" />
|
||||||
</ColumnDefinitions>
|
</ColumnDefinitions>
|
||||||
|
|
||||||
<ExpandTemplate Context="rowData">
|
<ExpandTemplate Context="rowData">
|
||||||
<Descriptions Title="任务详情" Bordered>
|
<Descriptions Title="任务详情" Bordered>
|
||||||
|
|
||||||
|
|
@ -51,25 +51,25 @@
|
||||||
</Button>
|
</Button>
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
|
|
||||||
<DescriptionsItem Title="任务时间轴" Span="5">
|
<DescriptionsItem Title="任务时间轴" Span="6">
|
||||||
<Steps Current="@((int)rowData.Data.LastEnum)" Status="@rowData.Data.TaskStatus">
|
<Steps Current="@((int)rowData.Data.LastEnum)" Status="@rowData.Data.TaskStatus">
|
||||||
<Step Title="下载文件"
|
<Step Title="下载文件"
|
||||||
Description="@RowST(rowData,RedisChannelEnum.DownloadFile)" />
|
Description="@RowST(rowData,RedisChannelEnum.下载文件)" />
|
||||||
|
|
||||||
<Step Title="分离音频"
|
<Step Title="分离音频"
|
||||||
Description="@RowST(rowData,RedisChannelEnum.SeparateAudio)" />
|
Description="@RowST(rowData,RedisChannelEnum.分离音频)" />
|
||||||
|
|
||||||
<Step Title="解析字幕"
|
<Step Title="解析字幕"
|
||||||
Description="@RowST(rowData,RedisChannelEnum.ParsingCaptions)" />
|
Description="@RowST(rowData,RedisChannelEnum.解析字幕)" />
|
||||||
|
|
||||||
<Step Title="解析说话人"
|
|
||||||
Description="@RowST(rowData,RedisChannelEnum.ParsingSpeaker)" />
|
|
||||||
|
|
||||||
<Step Title="Chat模型分析"
|
<Step Title="AI模型分析"
|
||||||
Description="@RowST(rowData,RedisChannelEnum.ChatModelAnalysis)" />
|
Description="@RowST(rowData,RedisChannelEnum.AI模型分析)" />
|
||||||
|
<Step Title="AI分析试题"
|
||||||
|
Description="@RowST(rowData,RedisChannelEnum.AI分析试题)" />
|
||||||
|
|
||||||
<Step Title="结束任务"
|
<Step Title="结束任务"
|
||||||
Description="@RowST(rowData,RedisChannelEnum.EndTask)" />
|
Description="@RowST(rowData,RedisChannelEnum.结束任务)" />
|
||||||
</Steps>
|
</Steps>
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ namespace Learn.VideoAnalysis.Components.Pages
|
||||||
/// <param name="query"></param>
|
/// <param name="query"></param>
|
||||||
async void ReStart()
|
async void ReStart()
|
||||||
{
|
{
|
||||||
await RedisExpand.SetTaskErrorMessage(reStartTask.Id, null);
|
await RedisExpand.ClearTaskError(reStartTask.Id);
|
||||||
_=Task.Run(() =>
|
_=Task.Run(() =>
|
||||||
RedisExpand.InsertChannel((RedisChannelEnum)selectEnum, reStartTask.Id)
|
RedisExpand.InsertChannel((RedisChannelEnum)selectEnum, reStartTask.Id)
|
||||||
);
|
);
|
||||||
|
|
@ -128,8 +128,8 @@ namespace Learn.VideoAnalysis.Components.Pages
|
||||||
var data = RedisExpand.Redis.HMGet<string>(RedisExpandKey.Task(item.Id),
|
var data = RedisExpand.Redis.HMGet<string>(RedisExpandKey.Task(item.Id),
|
||||||
"Progress", "LastEnum", "StartTime", "ErrorMessage");
|
"Progress", "LastEnum", "StartTime", "ErrorMessage");
|
||||||
item.Progress = data[0];
|
item.Progress = data[0];
|
||||||
item.LastEnum = data[1].ToEnum<RedisChannelEnum>() ?? default;
|
item.LastEnum = data[1] == null ?default:data[1].ToEnum<RedisChannelEnum>() ?? default;
|
||||||
item.StartTimeDic = System.Text.Json.JsonSerializer.Deserialize<Dictionary<RedisChannelEnum, DateTime>>(data[2]) ?? null;
|
item.StartTimeDic = data[2]==null?null: System.Text.Json.JsonSerializer.Deserialize<Dictionary<RedisChannelEnum, DateTime>>(data[2]) ?? null;
|
||||||
item.ErrorMessage = data[3];
|
item.ErrorMessage = data[3];
|
||||||
rowRestartLoading = false;
|
rowRestartLoading = false;
|
||||||
var statusStr = "wait";
|
var statusStr = "wait";
|
||||||
|
|
@ -138,7 +138,7 @@ namespace Learn.VideoAnalysis.Components.Pages
|
||||||
statusStr= "wait";
|
statusStr= "wait";
|
||||||
else if (!string.IsNullOrEmpty(rowData.Data.ErrorMessage))
|
else if (!string.IsNullOrEmpty(rowData.Data.ErrorMessage))
|
||||||
statusStr= "error";
|
statusStr= "error";
|
||||||
else if (dic.ContainsKey(RedisChannelEnum.EndTask))
|
else if (dic.ContainsKey(RedisChannelEnum.结束任务))
|
||||||
statusStr= "finish";
|
statusStr= "finish";
|
||||||
item.TaskStatus = statusStr;
|
item.TaskStatus = statusStr;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@
|
||||||
<div id="segmentsContainer" class="sc">
|
<div id="segmentsContainer" class="sc">
|
||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
<button class="gudingBtn" onclick="gd(this)">🔒</button>
|
<button class="gudingBtn" onclick="gd(this)">🔒</button></h2>
|
||||||
@nowTask.MediaName</h2>
|
|
||||||
@for (int i = 0; i < videoKnows.Length; i++)
|
@for (int i = 0; i < videoKnows.Length; i++)
|
||||||
{
|
{
|
||||||
var item = videoKnows[i];
|
var item = videoKnows[i];
|
||||||
|
|
@ -20,17 +19,20 @@
|
||||||
</div>
|
</div>
|
||||||
<div>概览: @item.Content</div>
|
<div>概览: @item.Content</div>
|
||||||
<br />
|
<br />
|
||||||
|
@if (item.QuestionArr != null)
|
||||||
|
{
|
||||||
@foreach (var q in item.QuestionArr)
|
@foreach (var q in item.QuestionArr)
|
||||||
{
|
{
|
||||||
|
|
||||||
<div class="knowQuestion" onclick="spClickTime(@q.StartTime)">
|
<div class="knowQuestion" onclick="spClickTime(@q.StartTime)">
|
||||||
<h3>问题: <span class="kSpan">@q.StartTime 秒</span></h3>
|
<h3>问题: <span class="kSpan">@q.StartTime 秒</span></h3>
|
||||||
<div class="kSpan">@q.TopicStem</div>
|
<div class="kSpan">@q.TopicStem</div>
|
||||||
<div >@q.Question</div>
|
<div>@q.Question</div>
|
||||||
<img style="text-align:center" src="@q.ImageUrl" width="320" height="180" />
|
<img style="text-align:center" src="@q.PPTImageUrl" width="320" height="180" />
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
}
|
}
|
||||||
|
}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ namespace Learn.VideoAnalysis.Components.Pages
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
videoPath = AppCommon.GetVideoPath(nowTask.Id.ToString());
|
videoPath = AppCommon.GetVideoPath(nowTask.Id.ToString());
|
||||||
|
|
||||||
if (nowTask.VideoType == AttachmentsInfoType.Review)
|
if (nowTask.VideoType == AttachmentsInfoType.复习)
|
||||||
{
|
{
|
||||||
var questionArr = await videoQuestionDB
|
var questionArr = await videoQuestionDB
|
||||||
.AsQueryable().Where(s => s.VideoTaskId == nowTask.Id)
|
.AsQueryable().Where(s => s.VideoTaskId == nowTask.Id)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ using Microsoft.OpenApi.Models;
|
||||||
using VideoAnalysisCore.AICore.SherpaOnnx;
|
using VideoAnalysisCore.AICore.SherpaOnnx;
|
||||||
using Mapster;
|
using Mapster;
|
||||||
using VideoAnalysisCore.AICore.GPT;
|
using VideoAnalysisCore.AICore.GPT;
|
||||||
using VideoAnalysisCore.AICore.GPT.KIMI;
|
|
||||||
using VideoAnalysisCore.AICore.GPT.ChatGPT;
|
using VideoAnalysisCore.AICore.GPT.ChatGPT;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
using VideoAnalysisCore.AICore.GPT.DeepSeek;
|
using VideoAnalysisCore.AICore.GPT.DeepSeek;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,14 @@
|
||||||
"FFmpeg": {
|
"FFmpeg": {
|
||||||
" TimeSlice": 600
|
" TimeSlice": 600
|
||||||
},
|
},
|
||||||
|
"AliyunOSS": {
|
||||||
|
"AccessKeyId": "LTAI5tDC6p9h747B7FHbgwkH",
|
||||||
|
"AccessKeySecret": "vRKgmbp1LB05LaGOjh3ZrZxbHSLYLF",
|
||||||
|
"BucketDomain": "https://learn-videoanalysis.oss-cn-chengdu.aliyuncs.com",
|
||||||
|
"Region": "cn-chengdu",
|
||||||
|
"BucketName": "learn-videoanalysis",
|
||||||
|
"EndPoint": "oss-cn-chengdu.aliyuncs.com" //上传节点
|
||||||
|
},
|
||||||
"DB": {
|
"DB": {
|
||||||
//"ConnectionString": "AllowLoadLocalInfile=true;Server=10.255.255.3;Port=3306;Database=learn.videoanalysis;User ID=marking;Password=qwe123!@#;CharSet=utf8mb4;pooling=true;SslMode=None",
|
//"ConnectionString": "AllowLoadLocalInfile=true;Server=10.255.255.3;Port=3306;Database=learn.videoanalysis;User ID=marking;Password=qwe123!@#;CharSet=utf8mb4;pooling=true;SslMode=None",
|
||||||
"ConnectionString": "AllowLoadLocalInfile=true;Server=rm-2vc20nd3d11g0oh6g2o.rwlb.cn-chengdu.rds.aliyuncs.com;User ID=marking;Password=poiuytPOIUYT098765)(*&^%;Port=3306;Database=learn.videoanalysis;CharSet=utf8mb4;pooling=true;SslMode=None",
|
"ConnectionString": "AllowLoadLocalInfile=true;Server=rm-2vc20nd3d11g0oh6g2o.rwlb.cn-chengdu.rds.aliyuncs.com;User ID=marking;Password=poiuytPOIUYT098765)(*&^%;Port=3306;Database=learn.videoanalysis;CharSet=utf8mb4;pooling=true;SslMode=None",
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ namespace VideoAnalysisCore.AICore.FFMPGE
|
||||||
|
|
||||||
RedisExpand.SetTaskProgress(task, "Frame=>50%");
|
RedisExpand.SetTaskProgress(task, "Frame=>50%");
|
||||||
Image<Rgb24> prevFrame = null;
|
Image<Rgb24> prevFrame = null;
|
||||||
var keyFrames = new List<int>();
|
var keyFrames = new List<int>(5);
|
||||||
foreach (var frameFile in frameFiles)
|
foreach (var frameFile in frameFiles)
|
||||||
{
|
{
|
||||||
using (var currFrame = Image.Load<Rgb24>(frameFile))
|
using (var currFrame = Image.Load<Rgb24>(frameFile))
|
||||||
|
|
@ -171,22 +171,22 @@ namespace VideoAnalysisCore.AICore.FFMPGE
|
||||||
ffmpeg.Error += (sender, e) =>
|
ffmpeg.Error += (sender, e) =>
|
||||||
{
|
{
|
||||||
var ee = new Exception($"音频转码出现异常 \r\n[{e.Input.Name} => {e.Output.Name}]: 错误: {e.Exception.Message}");
|
var ee = new Exception($"音频转码出现异常 \r\n[{e.Input.Name} => {e.Output.Name}]: 错误: {e.Exception.Message}");
|
||||||
RedisExpand.SetTaskErrorMessage(long.Parse(task), ee);
|
|
||||||
throw ee;
|
throw ee;
|
||||||
};
|
};
|
||||||
|
|
||||||
var conversionOptions = new ConversionOptions
|
var conversionOptions = new ConversionOptions
|
||||||
{
|
{
|
||||||
ExtraArguments = "-ar 16000 -ac 1"
|
ExtraArguments = "-ar 16000 -ac 1"
|
||||||
//+ (AppCommon.AppSetting.FFmpeg.TimeSlice == 0
|
|
||||||
//?string.Empty
|
|
||||||
//: $"-f segment -reset_timestamps 1 -segment_time {AppCommon.AppSetting.FFmpeg.TimeSlice}")
|
|
||||||
};
|
};
|
||||||
var res = await ffmpeg.ConvertAsync(inputFile, outputFile, conversionOptions);
|
try
|
||||||
|
{
|
||||||
|
await ffmpeg.ConvertAsync(inputFile, outputFile, conversionOptions);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine($"{DateTime.Now}=>音频转码完成");
|
|
||||||
//加入下一队列
|
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.ParsingCaptions, task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,11 @@ namespace VideoAnalysisCore.AICore.GPT
|
||||||
/// <param name="task">任务id</param>
|
/// <param name="task">任务id</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<TaskRes> GetKnow(string task);
|
public Task<TaskRes> GetKnow(string task);
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 视频分段内的 试题
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">任务id</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task GetVideoQuestion(string task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,8 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
|
||||||
.Where(s => s.Course_Id == 27
|
.Where(s => s.Course_Id == 27
|
||||||
&& s.Depth == 2)
|
&& s.Depth == 2)
|
||||||
.Select(s => s.Name).ToArrayAsync();
|
.Select(s => s.Name).ToArrayAsync();
|
||||||
string title = taskInfo.MediaName;
|
|
||||||
var fileNameResFormat = "{授课章节: string|null, 授课内容:string}";
|
var fileNameResFormat = "{授课章节: string|null, 授课内容:string}";
|
||||||
var fileNamePostMessages = title +
|
var fileNamePostMessages =
|
||||||
" 这是一堂课的标题,请你帮我分析一些关于课堂方面的内容." +
|
" 这是一堂课的标题,请你帮我分析一些关于课堂方面的内容." +
|
||||||
$"1.分析出高中{subject}课堂授课的主要章节(例如 章节: 数列),章节范围限定在[{string.Join(',', xkwKnows)}]范围." +
|
$"1.分析出高中{subject}课堂授课的主要章节(例如 章节: 数列),章节范围限定在[{string.Join(',', xkwKnows)}]范围." +
|
||||||
$"2.分析出这堂课的主要授课内容." +
|
$"2.分析出这堂课的主要授课内容." +
|
||||||
|
|
@ -184,7 +183,6 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
|
||||||
var gptRes = new TaskRes(captions);
|
var gptRes = new TaskRes(captions);
|
||||||
await RedisExpand.Redis
|
await RedisExpand.Redis
|
||||||
.HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes);
|
.HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes);
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task);
|
|
||||||
return gptRes;
|
return gptRes;
|
||||||
}
|
}
|
||||||
public async Task<T> ChatAsync<T>(string task, string postMessages, string postMessages1, string resFormat)
|
public async Task<T> ChatAsync<T>(string task, string postMessages, string postMessages1, string resFormat)
|
||||||
|
|
@ -417,9 +415,13 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
|
||||||
|
|
||||||
await RedisExpand.Redis
|
await RedisExpand.Redis
|
||||||
.HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes);
|
.HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes);
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task);
|
|
||||||
|
|
||||||
return gptRes;
|
return gptRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task GetVideoQuestion(string task)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ using System.Net.Http;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using VideoAnalysisCore.AICore.GPT.KIMI;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -121,7 +120,6 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
Console.WriteLine(e.Message);
|
Console.WriteLine(e.Message);
|
||||||
Console.WriteLine(e.StackTrace);
|
Console.WriteLine(e.StackTrace);
|
||||||
Console.WriteLine("==============================================");
|
Console.WriteLine("==============================================");
|
||||||
|
|
||||||
Thread.Sleep(1000);
|
Thread.Sleep(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using VideoAnalysisCore.AICore.GPT.KIMI;
|
|
||||||
|
|
||||||
namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ using FFmpeg.NET.Services;
|
||||||
using Aliyun.OSS;
|
using Aliyun.OSS;
|
||||||
using Yitter.IdGenerator;
|
using Yitter.IdGenerator;
|
||||||
using VideoAnalysisCore.Common.Expand;
|
using VideoAnalysisCore.Common.Expand;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
{
|
{
|
||||||
|
|
@ -57,18 +58,9 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
/// 获取内容对应的章节
|
/// 获取内容对应的章节
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<List<VideoKonwPoint>> GetVideoKnow(List<VideoKnowRes> questionRes, VideoTask taskInfo, string sections, int course_Id)
|
private async Task<List<VideoKonwPoint>> GetVideoKnow(List<VideoKnowRes> questionRes, VideoTask taskInfo,
|
||||||
|
string sections, List<KnowledgeInfo> knowledgeInfos)
|
||||||
{
|
{
|
||||||
|
|
||||||
var know = await knowledgeInfoDB.GetFirstAsync(s => s.Course_Id == course_Id && s.Name == sections);
|
|
||||||
if (know is null)
|
|
||||||
throw new Exception("未能找到对应知识点=>" + sections);
|
|
||||||
|
|
||||||
var subject = taskInfo.Subject.ToString();
|
|
||||||
|
|
||||||
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 knows = string.Join(',', knowledgeInfos.Select(s => s.Id + "|" + s.Name));
|
||||||
var knowDic = knowledgeInfos
|
var knowDic = knowledgeInfos
|
||||||
.OrderBy(s => s.Id)
|
.OrderBy(s => s.Id)
|
||||||
|
|
@ -79,7 +71,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
var thems = JsonSerializer.Serialize(questionRes.Adapt<VideoKnowQueryDto[]>());// string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme));
|
var thems = JsonSerializer.Serialize(questionRes.Adapt<VideoKnowQueryDto[]>());// string.Join(',', questionRes.Select(s => s.StartTime + "->" + s.Theme));
|
||||||
var checkResFormat1 = """[{"StartTime":开始秒(number),"KnowPoint":知识点名称(string),"KnowPointId":知识点Id(string)}]""";
|
var checkResFormat1 = """[{"StartTime":开始秒(number),"KnowPoint":知识点名称(string),"KnowPointId":知识点Id(string)}]""";
|
||||||
var knowMessages =
|
var knowMessages =
|
||||||
$"我针对{subject}课堂授课视频分析出了视频的授课阶段片段。" +
|
$"我针对{taskInfo.Subject}课堂授课视频分析出了视频的授课阶段片段。" +
|
||||||
$"现在需要你通过每个片段的内容总结来分配正确的知识点(单个片段允许多个知识点用逗号','分割)。" +
|
$"现在需要你通过每个片段的内容总结来分配正确的知识点(单个片段允许多个知识点用逗号','分割)。" +
|
||||||
$"这是我的分段 {thems}。" +
|
$"这是我的分段 {thems}。" +
|
||||||
$"课堂内容与{sections}章节相关" +
|
$"课堂内容与{sections}章节相关" +
|
||||||
|
|
@ -136,7 +128,6 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
&& (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;
|
|
||||||
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions);
|
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions);
|
||||||
|
|
||||||
var fileNameResFormat = "{授课章节: string|null}";
|
var fileNameResFormat = "{授课章节: string|null}";
|
||||||
|
|
@ -173,7 +164,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
{
|
{
|
||||||
//校验结果质量
|
//校验结果质量
|
||||||
var thems = JsonSerializer.Serialize(questionRes.Adapt<VideoKnowQueryDto[]>());
|
var thems = JsonSerializer.Serialize(questionRes.Adapt<VideoKnowQueryDto[]>());
|
||||||
var pptFormat = taskInfo.VideoType==AttachmentsInfoType.PPT
|
var pptFormat = taskInfo.VideoType==AttachmentsInfoType.复习
|
||||||
? "这堂课是习题课,所讲解内容都是试题。"
|
? "这堂课是习题课,所讲解内容都是试题。"
|
||||||
: string.Empty;
|
: string.Empty;
|
||||||
var checkResFormat = """{"Score":打分(number),"Evaluation":评价(string)""";//,"Data":优化后的分段(array)}""";
|
var checkResFormat = """{"Score":打分(number),"Evaluation":评价(string)""";//,"Data":优化后的分段(array)}""";
|
||||||
|
|
@ -271,7 +262,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
: $"图像视频中授课内容PPT发生了变化的时间节点是{taskInfo.PPTKeyFrame},授课阶段结果可以参考这些时间节点。";
|
: $"图像视频中授课内容PPT发生了变化的时间节点是{taskInfo.PPTKeyFrame},授课阶段结果可以参考这些时间节点。";
|
||||||
var resFormat = """[{"StartTime":开始秒(number),"EndTime":结束秒(number),"Stage":阶段(string),"Theme":主题(string),"Content":内容总结(string)}]""";
|
var resFormat = """[{"StartTime":开始秒(number),"EndTime":结束秒(number),"Stage":阶段(string),"Theme":主题(string),"Content":内容总结(string)}]""";
|
||||||
|
|
||||||
var exerciseClass = taskInfo?.VideoType == AttachmentsInfoType.Review
|
var exerciseClass = taskInfo?.VideoType == AttachmentsInfoType.复习
|
||||||
? $"但是本堂课是习题课,所以每个阶段是不同的例题讲解内容。"
|
? $"但是本堂课是习题课,所以每个阶段是不同的例题讲解内容。"
|
||||||
: string.Empty;
|
: string.Empty;
|
||||||
//$"请注意 本次分析的视频字幕只是其中一部分 不需要分析出所有类型的授课阶段。";
|
//$"请注意 本次分析的视频字幕只是其中一部分 不需要分析出所有类型的授课阶段。";
|
||||||
|
|
@ -290,7 +281,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
|
|
||||||
Console.WriteLine(DateTime.Now + $"=>{taskInfo.Id.ToString()}.开始分析视频内容 {tryCount}");
|
Console.WriteLine(DateTime.Now + $"=>{taskInfo.Id.ToString()}.开始分析视频内容 {tryCount}");
|
||||||
var resData = await ChatAsync<VideoKnowRes[]>(taskInfo.Id.ToString(), postMessages, "分析字幕");
|
var resData = await ChatAsync<VideoKnowRes[]>(taskInfo.Id.ToString(), postMessages, "分析字幕");
|
||||||
if (taskInfo?.VideoType == AttachmentsInfoType.Review)
|
if (taskInfo?.VideoType == AttachmentsInfoType.复习)
|
||||||
foreach (var item in resData)
|
foreach (var item in resData)
|
||||||
item.Stage = StageEnum.例题精讲.ToString();
|
item.Stage = StageEnum.例题精讲.ToString();
|
||||||
questionRes.AddRange(resData);
|
questionRes.AddRange(resData);
|
||||||
|
|
@ -378,22 +369,27 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
/// 提取试题
|
/// 提取试题
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<SenseVoiceRes[]> AnalysisVideoQuestions(VideoTask taskInfo)
|
private async Task<SenseVoiceRes[]> AnalysisVideoQuestions(VideoTask taskInfo, List<KnowledgeInfo> knowledgeInfos)
|
||||||
{
|
{
|
||||||
Console.WriteLine(DateTime.Now + $"=>{taskInfo.Id} 提取试题");
|
Console.WriteLine(DateTime.Now + $"=>{taskInfo.Id} 提取试题");
|
||||||
if (taskInfo is null || string.IsNullOrEmpty(taskInfo.PPTKeyFrame))
|
if (taskInfo is null || string.IsNullOrEmpty(taskInfo.PPTKeyFrame))
|
||||||
return null;
|
return null;
|
||||||
var farmeArr = JsonSerializer.Deserialize<int[]>(taskInfo.PPTKeyFrame);
|
var farmeArr = JsonSerializer.Deserialize<int[]>(taskInfo.PPTKeyFrame);
|
||||||
|
|
||||||
|
var knowArrStr = string.Join(',', knowledgeInfos.Select(s => s.Id + "|" + s.Name));
|
||||||
var videoKnowArr = await videoKonwPointDB.GetListAsync(s => s.VideoTaskId == taskInfo.Id);
|
var videoKnowArr = await videoKonwPointDB.GetListAsync(s => s.VideoTaskId == taskInfo.Id);
|
||||||
|
var videoKnowDic = knowledgeInfos
|
||||||
|
.GroupBy(s => s.Id)
|
||||||
|
.ToDictionary(s => s.Key, s => s.First());
|
||||||
var insertData =new List<VideoQuestionOSSDto>();
|
var insertData =new List<VideoQuestionOSSDto>();
|
||||||
var insertQuestionKonw = new List<VideoQuestionKonw>();
|
var insertQuestionKonw = new List<VideoQuestionKonw>();
|
||||||
foreach (var item in farmeArr)
|
foreach (var item in farmeArr)
|
||||||
{
|
{
|
||||||
var knowInfoArr = videoKnowArr
|
var knowInfoArr = videoKnowArr
|
||||||
.Where(s => item+5 >= s.StartTime && item+5 <= s.EndTime)
|
.Where(s => item+20 >= s.StartTime && item < s.EndTime)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
if (knowInfoArr is null || knowInfoArr.Count() ==0)
|
if (knowInfoArr is null || knowInfoArr.Count() ==0)
|
||||||
return null;
|
continue;
|
||||||
var tryCount = 50;
|
var tryCount = 50;
|
||||||
while (tryCount>1)
|
while (tryCount>1)
|
||||||
{
|
{
|
||||||
|
|
@ -409,20 +405,24 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
break;
|
break;
|
||||||
Console.WriteLine(DateTime.Now + $"=>{taskInfo.Id} 提取{knowInfoArr.First().StartTime}秒试题的试题内容");
|
Console.WriteLine(DateTime.Now + $"=>{taskInfo.Id} 提取{knowInfoArr.First().StartTime}秒试题的试题内容");
|
||||||
Console.WriteLine( sRes.Result.res.value);
|
Console.WriteLine( sRes.Result.res.value);
|
||||||
var knowArr=JsonSerializer.Serialize(knowInfoArr.Select(s => new { s.KnowPointId, s.KnowPoint }));
|
//var knowArr=JsonSerializer.Serialize(knowInfoArr.Select(s => new { s.KnowPointId, s.KnowPoint }));
|
||||||
var resFormat = """[{"Type":string(试题类型),"TopicStem":string(试题题干),"QuestionArr":[{"Question":string(子问题),"KnowPointId":(string)知识点ID}]}]""";
|
var resFormat = """[{"Type":string(试题类型),"TopicStem":string(试题题干),"QuestionArr":[{"Question":string(子问题),"KnowPointId":(string)知识点ID}]}]""";
|
||||||
var postMessages =
|
var postMessages =
|
||||||
$"我将提供一段内容是md格式的试题内容字符串。" +
|
$"我将提供一段内容是Markdown格式的试题。" +
|
||||||
$"请提取出其中的试题内容试,题干以及每个试题的问题。并且为每个试题关联上在我限定范围内的知识点(多个则用逗号分割)。" +
|
$"请提取出其中有效的试题内容(包括 题干,公式试题内提出的问题 )。" +
|
||||||
$"知识点格式(json格式)范围[{knowArr}]。" +
|
$"为每个问题关联上限定范围内的知识点(多个则用逗号分割),知识点格式 (知识点Id|知识点名称)知识点范围[{knowArrStr}]。" +
|
||||||
$"排除不是试题内容的文字,优化公式排版并且去除题号,但不要修改latex数学公式。" +
|
$"排除不是试题内容的文字,优化试题排版并且去除题号,尽量保留latex数学公式。" +
|
||||||
$"如果存在多道题,则需要拆分成为多个试题对象!" +
|
$"如果存在多道题,则需要拆分成为多个试题对象!" +
|
||||||
$"试题的类型约束在 填空题/判断题/选择题/解答题 范围内。" +
|
$"试题的类型约束在 填空题/判断题/选择题/解答题/填空题 范围内。" +
|
||||||
$"请检查我提供的字符串内容,如不包含问题试题则返回`[]`" +
|
$"如果是有效试题且题干中存在下划线则试题的题型应该是填空题。" +
|
||||||
|
$"请检查我提供的字符串内容,如果不能识别知识点则不处理知识点,如不包含问题试题则返回`[]`" +
|
||||||
$"输出内容只返回json格式为({resFormat})" +
|
$"输出内容只返回json格式为({resFormat})" +
|
||||||
$"以下是试题内容" +
|
$"以下是试题内容" +
|
||||||
$"`{sRes.Result.res.value}`";
|
$"`{sRes.Result.res.value}`";
|
||||||
var resData = await ChatAsync<VideoQuestionOSSDto[]>(taskInfo.Id.ToString(), postMessages, "提取试题", "deepseek-chat");
|
var resData = await ChatAsync<VideoQuestionOSSDto[]>(taskInfo.Id.ToString(), postMessages, "提取试题", "deepseek-chat");
|
||||||
|
//var resData = await ChatAsync<VideoQuestionOSSDto[]>(taskInfo.Id.ToString(), postMessages, "提取试题");
|
||||||
|
if(resData is null || resData.Count()==0)
|
||||||
|
break;
|
||||||
foreach (var q in resData)
|
foreach (var q in resData)
|
||||||
{
|
{
|
||||||
var TopicId = YitIdHelper.NextId();
|
var TopicId = YitIdHelper.NextId();
|
||||||
|
|
@ -436,16 +436,22 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
vq.StageId = knowInfoArr.First().StageId;
|
vq.StageId = knowInfoArr.First().StageId;
|
||||||
vq.Question = qt.Question;
|
vq.Question = qt.Question;
|
||||||
vq.TopicId = TopicId;
|
vq.TopicId = TopicId;
|
||||||
|
vq.Type = q.Type;
|
||||||
|
if(string.IsNullOrEmpty(qt.KnowPointId))
|
||||||
|
continue;//重试
|
||||||
insertData.Add(vq);
|
insertData.Add(vq);
|
||||||
foreach (var kid in qt.KnowPointId.Split(","))
|
foreach (var kid in qt.KnowPointId.Split(","))
|
||||||
{
|
{
|
||||||
|
var tryOk = int.TryParse(kid.Split("|").First(),out int kidd);
|
||||||
|
if(!tryOk || !videoKnowDic.ContainsKey(kidd))
|
||||||
|
continue;//跳过未识别知识点
|
||||||
insertQuestionKonw.Add(new VideoQuestionKonw()
|
insertQuestionKonw.Add(new VideoQuestionKonw()
|
||||||
{
|
{
|
||||||
KnowPoint = knowInfoArr.First(s => s.KnowPointId == kid).KnowPoint,
|
KnowPoint = videoKnowDic[kidd].Name,
|
||||||
KnowPointId = kid,
|
KnowPointId = kid,
|
||||||
StageId = q.StageId,
|
StageId = q.StageId,
|
||||||
VideoTaskId = q.VideoTaskId,
|
VideoTaskId = vq.VideoTaskId,
|
||||||
VideoQuestionId = q.Id,
|
VideoQuestionId = vq.Id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -467,7 +473,6 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
await videoQuestionKonwDB.AsDeleteable()
|
await videoQuestionKonwDB.AsDeleteable()
|
||||||
.Where(s => s.VideoTaskId == taskInfo.Id)
|
.Where(s => s.VideoTaskId == taskInfo.Id)
|
||||||
.ExecuteCommandAsync();
|
.ExecuteCommandAsync();
|
||||||
|
|
||||||
await videoQuestionDB.InsertRangeAsync(insertData.Adapt<VideoQuestion[]>());
|
await videoQuestionDB.InsertRangeAsync(insertData.Adapt<VideoQuestion[]>());
|
||||||
await videoQuestionKonwDB.InsertRangeAsync(insertQuestionKonw);
|
await videoQuestionKonwDB.InsertRangeAsync(insertQuestionKonw);
|
||||||
|
|
||||||
|
|
@ -502,6 +507,14 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions);
|
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions);
|
||||||
//处理视频授课章节
|
//处理视频授课章节
|
||||||
var sections = await GetSections(taskInfo, Course_Id);
|
var sections = await GetSections(taskInfo, Course_Id);
|
||||||
|
|
||||||
|
var know = await knowledgeInfoDB.GetFirstAsync(s => s.Course_Id == Course_Id && s.Name == sections);
|
||||||
|
if (know is null)
|
||||||
|
throw new Exception("未能找到对应知识点=>" + sections);
|
||||||
|
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);
|
||||||
|
|
||||||
//AI优化字幕
|
//AI优化字幕
|
||||||
captionsArr = await OptimizeSubtitles(taskInfo, captionsArr, sections);
|
captionsArr = await OptimizeSubtitles(taskInfo, captionsArr, sections);
|
||||||
//合并字幕
|
//合并字幕
|
||||||
|
|
@ -516,7 +529,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
|
|
||||||
if (questionRes.Count == 0) continue;
|
if (questionRes.Count == 0) continue;
|
||||||
//处理分段 知识点
|
//处理分段 知识点
|
||||||
var insertData = await GetVideoKnow(questionRes, taskInfo, sections, Course_Id);
|
var insertData = await GetVideoKnow(questionRes, taskInfo, sections, knowledgeInfos);
|
||||||
//校验结果质量
|
//校验结果质量
|
||||||
var checkRes = await VerifySpanQuality(questionRes, taskInfo, captions, sections, Course_Id);
|
var checkRes = await VerifySpanQuality(questionRes, taskInfo, captions, sections, Course_Id);
|
||||||
|
|
||||||
|
|
@ -543,11 +556,50 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
||||||
await RedisExpand.Redis
|
await RedisExpand.Redis
|
||||||
.HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes);
|
.HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes);
|
||||||
|
|
||||||
if (taskInfo.VideoType == AttachmentsInfoType.Review)
|
|
||||||
await AnalysisVideoQuestions(taskInfo);
|
|
||||||
|
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 视频分段内的 试题
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">任务id</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task GetVideoQuestion(string task)
|
||||||
|
{
|
||||||
|
var taskId = long.Parse(task);
|
||||||
|
var taskInfo = await videoTaskDB.AsQueryable()
|
||||||
|
.Where(s => s.Id == taskId)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
|
if (taskInfo.VideoType != AttachmentsInfoType.复习)
|
||||||
|
return;
|
||||||
|
var subject = taskInfo.Subject.ToString();
|
||||||
|
var Course_Id = 27;
|
||||||
|
switch (taskInfo.Type)//处理不同任务类型的知识点树
|
||||||
|
{
|
||||||
|
case TaskTypeEnum.蓝鲸智库_中职视频分段:
|
||||||
|
Course_Id = 51;
|
||||||
|
break;
|
||||||
|
case TaskTypeEnum.蓝鲸智库_视频分段:
|
||||||
|
default:
|
||||||
|
Course_Id = 27;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions);
|
||||||
|
//处理视频授课章节
|
||||||
|
var sections = await GetSections(taskInfo, Course_Id);
|
||||||
|
var know = await knowledgeInfoDB.GetFirstAsync(s => s.Course_Id == Course_Id && s.Name == sections);
|
||||||
|
if (know is null)
|
||||||
|
throw new Exception("未能找到对应知识点=>" + sections);
|
||||||
|
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);
|
||||||
|
//开始分析复习课 试题
|
||||||
|
await AnalysisVideoQuestions(taskInfo, knowledgeInfos);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,356 +0,0 @@
|
||||||
using VideoAnalysisCore.Common;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Net;
|
|
||||||
using Azure;
|
|
||||||
using System.Reflection.PortableExecutable;
|
|
||||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// https://platform.moonshot.cn/docs/api-reference
|
|
||||||
/// </summary>
|
|
||||||
namespace VideoAnalysisCore.AICore.GPT.KIMI
|
|
||||||
{
|
|
||||||
|
|
||||||
public class MoonshotClient
|
|
||||||
{
|
|
||||||
private readonly ILogger<MoonshotClient> _logger;
|
|
||||||
|
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
|
||||||
|
|
||||||
public MoonshotClient(ILogger<MoonshotClient> logger, IHttpClientFactory httpClientFactory)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_httpClientFactory = httpClientFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// list models
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<ModelListResp> ListModels()
|
|
||||||
{
|
|
||||||
var response = await GetAsync("/v1/models");
|
|
||||||
return await ParseResp<ModelListResp>(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Chat
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="requestBody"></param>
|
|
||||||
/// <returns>Return HttpResponseMessage for SSE</returns>
|
|
||||||
public async Task<ChatRes?> Chat(string requestBody)
|
|
||||||
{
|
|
||||||
var chatResp = await PostJsonStreamAsync("/v1/chat/completions", requestBody);
|
|
||||||
return await chatResp.Content.ReadFromJsonAsync<ChatRes>();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ChatSSE[流式传输 更稳定]
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="chatReq"></param>
|
|
||||||
/// <returns>Return HttpResponseMessage for SSE</returns>
|
|
||||||
public async Task<(Usage u, string res)?> ChatSSE(ChatReq chatReq)
|
|
||||||
{
|
|
||||||
chatReq.stream = true;
|
|
||||||
var requestBody = System.Text.Json.JsonSerializer.Serialize(chatReq);
|
|
||||||
var chatResp = await PostJsonStreamAsync("/v1/chat/completions", requestBody);
|
|
||||||
using var stream = await chatResp.Content.ReadAsStreamAsync();
|
|
||||||
using var reader = new StreamReader(stream, Encoding.UTF8);
|
|
||||||
string line;
|
|
||||||
StringBuilder messageBuilder = new StringBuilder();
|
|
||||||
ChatResSSE lastChat = new ChatResSSE();
|
|
||||||
while ((line = await reader.ReadLineAsync()) != null)
|
|
||||||
{
|
|
||||||
if (line.EndsWith("[DONE]"))
|
|
||||||
{
|
|
||||||
// 表示一条消息结束
|
|
||||||
string message = messageBuilder.ToString();
|
|
||||||
messageBuilder.Clear();
|
|
||||||
var u = lastChat?.choices?.FirstOrDefault()?.usage;
|
|
||||||
if (u == null || string.IsNullOrEmpty(message))
|
|
||||||
return null;
|
|
||||||
return (u, message);
|
|
||||||
}
|
|
||||||
else if (line.StartsWith("data:"))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var data = System.Text.Json.JsonSerializer.Deserialize<ChatResSSE>(line.Substring("data:".Length).Trim());
|
|
||||||
lastChat = data;
|
|
||||||
var str = data?.choices.FirstOrDefault()?.delta.content;
|
|
||||||
if (!string.IsNullOrEmpty(str))
|
|
||||||
messageBuilder.Append(str);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine("异常 ChatSSE=>");
|
|
||||||
Console.WriteLine(line);
|
|
||||||
Console.WriteLine(e.Message);
|
|
||||||
Console.WriteLine(e.StackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Chat
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="chatReq"></param>
|
|
||||||
/// <returns>Return HttpResponseMessage for SSE</returns>
|
|
||||||
public async Task<(Usage u, string res)?> Chat(ChatReq chatReq)
|
|
||||||
{
|
|
||||||
var requestBody = System.Text.Json.JsonSerializer.Serialize(chatReq);
|
|
||||||
var chatResp = await PostJsonStreamAsync("/v1/chat/completions", requestBody);
|
|
||||||
var res = await chatResp.Content.ReadFromJsonAsync<ChatRes>();
|
|
||||||
var chatResContent = res?.choices.FirstOrDefault()?.message.content.Trim();
|
|
||||||
|
|
||||||
if (res is null || res.error != null)
|
|
||||||
throw new Exception($"KIMI模型返回异常 Chat 返回参数: " +
|
|
||||||
$" {System.Text.Json.JsonSerializer.Serialize(res)}");
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(chatResContent))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
|
|
||||||
return (res.usage, chatResContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 计算token长度
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="chatReqText">文本</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="Exception"></exception>
|
|
||||||
public async Task<int?> GetAsTiMateTokenCount(string chatReqText)
|
|
||||||
{
|
|
||||||
var reqObject = new
|
|
||||||
{
|
|
||||||
model = "moonshot-v1-128k",
|
|
||||||
messages = new List<MessagesItem>()
|
|
||||||
{
|
|
||||||
new MessagesItem(chatReqText,"system"),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var response = await PostJsonStreamAsync("/v1/tokenizers/estimate-token-count", JsonConvert.SerializeObject(reqObject));
|
|
||||||
var responseText = await response.Content.ReadAsStringAsync();
|
|
||||||
if (response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
var responseObj = JToken.Parse(responseText);
|
|
||||||
return responseObj?["data"]?["total_tokens"]?.ToObject<int>();
|
|
||||||
}
|
|
||||||
var error = JsonConvert.DeserializeObject<ErrorResponse>(responseText);
|
|
||||||
_logger.LogError($"{error?.error?.type}: {error?.error?.message}");
|
|
||||||
throw new Exception($"{error?.error.type}: {error?.error.message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get as timate token count
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="chatReq"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<int?> GetAsTiMateTokenCount(ChatReq chatReq)
|
|
||||||
{
|
|
||||||
var chatReqText = JsonConvert.SerializeObject(chatReq);
|
|
||||||
return await GetAsTiMateTokenCount(chatReqText);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// List files
|
|
||||||
/// </summary>
|
|
||||||
public virtual async Task<FileListResp> ListFiles()
|
|
||||||
{
|
|
||||||
var response = await GetAsync("/v1/files");
|
|
||||||
return await ParseResp<FileListResp>(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Upload file
|
|
||||||
/// </summary>
|
|
||||||
public virtual async Task<FileItem> UploadFile(string filePath)
|
|
||||||
{
|
|
||||||
if (!File.Exists(filePath))
|
|
||||||
{
|
|
||||||
throw new FileNotFoundException($"{filePath} not found");
|
|
||||||
}
|
|
||||||
var client = _httpClientFactory.CreateClient();
|
|
||||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey);
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, $"{Host}/v1/files");
|
|
||||||
var content = new MultipartFormDataContent
|
|
||||||
{
|
|
||||||
{ new StreamContent(File.OpenRead(filePath)), "file", filePath }
|
|
||||||
};
|
|
||||||
request.Content = content;
|
|
||||||
var response = await client.SendAsync(request);
|
|
||||||
return await ParseResp<FileItem>(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Upload file stream
|
|
||||||
/// </summary>
|
|
||||||
public virtual async Task<FileItem> UploadFileStream(Stream stream, string fileName)
|
|
||||||
{
|
|
||||||
var client = _httpClientFactory.CreateClient();
|
|
||||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey);
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, $"{Host}/v1/files");
|
|
||||||
var content = new MultipartFormDataContent
|
|
||||||
{
|
|
||||||
{ new StreamContent(stream), "file", fileName }
|
|
||||||
};
|
|
||||||
request.Content = content;
|
|
||||||
var response = await client.SendAsync(request);
|
|
||||||
return await ParseResp<FileItem>(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get file content
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public virtual async Task<FileContent> GetFileContent(string fileId)
|
|
||||||
{
|
|
||||||
var response = await GetAsync($"/v1/files/{fileId}/content");
|
|
||||||
return await ParseResp<FileContent>(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private async Task<HttpResponseMessage> GetAsync(string path)
|
|
||||||
{
|
|
||||||
var client = _httpClientFactory.CreateClient();
|
|
||||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey);
|
|
||||||
return await client.GetAsync(Host + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<HttpResponseMessage> PostJsonAsync(string path, string json)
|
|
||||||
{
|
|
||||||
var client = _httpClientFactory.CreateClient();
|
|
||||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey);
|
|
||||||
return await client.PostAsync(Host + path, new StringContent(json, Encoding.UTF8, "application/json"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<HttpResponseMessage> PostJsonStreamAsync(string path, string json)
|
|
||||||
{
|
|
||||||
var uriBuilder = new UriBuilder(Host + path);
|
|
||||||
var maxRestart = 4;
|
|
||||||
var errorMSG = new Exception[maxRestart];
|
|
||||||
for (int i = 0; i < maxRestart; i++)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var client = _httpClientFactory.CreateClient();
|
|
||||||
client.Timeout = TimeSpan.FromSeconds(Timeout.Infinite);//超时时间20分钟
|
|
||||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey);
|
|
||||||
client.DefaultRequestVersion = HttpVersion.Version20;
|
|
||||||
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
|
|
||||||
client.DefaultRequestHeaders.ConnectionClose = true;
|
|
||||||
|
|
||||||
//var request = ToHttpRequest(path);
|
|
||||||
//request.Version = HttpVersion.Version20;
|
|
||||||
//request.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
|
|
||||||
//request.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
|
||||||
//return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
|
||||||
|
|
||||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
|
||||||
return await client.PostAsync(uriBuilder.Uri, content);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
errorMSG[i] = e;
|
|
||||||
Console.WriteLine("====================[请求异常,重试]====================");
|
|
||||||
Console.WriteLine(uriBuilder.Uri);
|
|
||||||
Console.WriteLine(e.Message);
|
|
||||||
Console.WriteLine(e.StackTrace);
|
|
||||||
Console.WriteLine("==============================================");
|
|
||||||
|
|
||||||
}
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
}
|
|
||||||
throw errorMSG.Last(s => s != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpRequestMessage ToHttpRequest(string path)
|
|
||||||
{
|
|
||||||
var request = new HttpRequestMessage();
|
|
||||||
var uriBuilder = new UriBuilder(Host + path);
|
|
||||||
request.RequestUri = uriBuilder.Uri;
|
|
||||||
request.Method = new HttpMethod("POST");
|
|
||||||
request.Headers.Host = new Uri(Host).Host;
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parse response
|
|
||||||
/// </summary>
|
|
||||||
private async Task<T> ParseResp<T>(HttpResponseMessage response)
|
|
||||||
{
|
|
||||||
var responseText = await response.Content.ReadAsStringAsync();
|
|
||||||
if (response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
return JsonConvert.DeserializeObject<T>(responseText) ?? default;
|
|
||||||
}
|
|
||||||
var error = JsonConvert.DeserializeObject<ErrorResponse>(responseText);
|
|
||||||
_logger.LogError($"{error?.error.type}: {error?.error.message}");
|
|
||||||
throw new Exception($"{error?.error.type}: {error?.error.message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static string _host = "https://api.moonshot.cn";
|
|
||||||
|
|
||||||
public static string Host
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(_host) && !string.IsNullOrEmpty(AppCommon.Config.ChatGpt.KIMI.Host))
|
|
||||||
{
|
|
||||||
_host = AppCommon.Config.ChatGpt.KIMI.Host ?? "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return _host;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
|
|
||||||
_host = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static string _apiKey = "sk_";
|
|
||||||
|
|
||||||
public static string ApiKey
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(_apiKey) && !string.IsNullOrEmpty(AppCommon.Config.ChatGpt.KIMI.ApiKey))
|
|
||||||
{
|
|
||||||
_apiKey = AppCommon.Config.ChatGpt.KIMI.ApiKey ?? "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return _apiKey;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_apiKey = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,365 +0,0 @@
|
||||||
namespace VideoAnalysisCore.AICore.GPT.KIMI
|
|
||||||
{
|
|
||||||
|
|
||||||
public class MessagesItem
|
|
||||||
{
|
|
||||||
public MessagesItem()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
public MessagesItem(string content, string role = "user", bool partial = false)
|
|
||||||
{
|
|
||||||
this.content = content;
|
|
||||||
this.role = role;
|
|
||||||
this.partial = partial;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string role { get; set; }
|
|
||||||
public bool partial { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string content { get; set; }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// chat请求参数
|
|
||||||
/// </summary>
|
|
||||||
public class ChatReq
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 使用的模型
|
|
||||||
/// <para>例如[ moonshot-v1-8k ]</para>
|
|
||||||
/// </summary>
|
|
||||||
public string model { get; set; } = "moonshot-v1-8k";
|
|
||||||
/// <summary>
|
|
||||||
/// 消息主体
|
|
||||||
/// </summary>
|
|
||||||
public List<MessagesItem> messages { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 使用什么采样温度,介于 0 和 1 之间。较高的值(如 0.7)将使输出更加随机,而较低的值(如 0.2)将使其更加集中和确定性
|
|
||||||
/// <para>默认为 0,如果设置,值域须为 [0, 1] 我们推荐 0.3,以达到较合适的效果</para>
|
|
||||||
/// </summary>
|
|
||||||
public float temperature { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 聊天完成时生成的最大 token 数。如果到生成了最大 token 数个结果仍然没有结束,finish reason 会是 "length", 否则会是 "stop"
|
|
||||||
/// <para>这个值建议按需给个合理的值,如果不给的话,我们会给一个不错的整数比如 1024。特别要注意的是,这个 max_tokens 是指您期待我们返回的 token 长度,而不是输入 + 输出的总长度。比如对一个 moonshot-v1-8k 模型,它的最大输入 + 输出总长度是 8192,当输入 messages 总长度为 4096 的时候,您最多只能设置为 4096,否则我们服务会返回不合法的输入参数( invalid_request_error ),并拒绝回答。如果您希望获得“输入的精确 token 数”,可以使用下面的“计算 Token” API 使用我们的计算器获得计数</para>
|
|
||||||
/// </summary>
|
|
||||||
public int? max_tokens { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 另一种采样方法,即模型考虑概率质量为 top_p 的标记的结果。因此,0.1 意味着只考虑概率质量最高的 10% 的标记。一般情况下,我们建议改变这一点或温度,但不建议 同时改变
|
|
||||||
/// </summary>
|
|
||||||
public float? top_p { get; set; } = 1.0f;
|
|
||||||
/// <summary>
|
|
||||||
/// 为每条输入消息生成多少个结果
|
|
||||||
/// <para>默认为 1,不得大于 5。特别的,当 temperature 非常小靠近 0 的时候,我们只能返回 1 个结果,如果这个时候 n 已经设置并且 > 1,我们的服务会返回不合法的输入参数(invalid_request_error)</para>
|
|
||||||
/// </summary>
|
|
||||||
public int? n { get; set; } = 1;
|
|
||||||
/// <summary>
|
|
||||||
/// 存在惩罚,介于-2.0到2.0之间的数字。正值会根据新生成的词汇是否出现在文本中来进行惩罚,增加模型讨论新话题的可能性
|
|
||||||
/// <para>默认为 0</para>
|
|
||||||
/// </summary>
|
|
||||||
public float? presence_penalty { get; set; } = 0;
|
|
||||||
/// <summary>
|
|
||||||
/// 频率惩罚,介于-2.0到2.0之间的数字。正值会根据新生成的词汇在文本中现有的频率来进行惩罚,减少模型一字不差重复同样话语的可能性
|
|
||||||
/// <para>默认为 0</para>
|
|
||||||
/// </summary>
|
|
||||||
public float? frequency_penalty { get; set; } = 0;
|
|
||||||
/// <summary>
|
|
||||||
/// 停止词,当全匹配这个(组)词后会停止输出,这个(组)词本身不会输出。最多不能超过 5 个字符串,每个字符串不得超过 32 字节
|
|
||||||
/// <para>默认 null</para>
|
|
||||||
/// </summary>
|
|
||||||
public List<string>? stop { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 是否流式返回
|
|
||||||
/// <para>false</para>
|
|
||||||
/// </summary>
|
|
||||||
public bool stream { get; set; } = false;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class PermissionItem
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int created { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string id { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string @object { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string allow_create_engine { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string allow_sampling { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string allow_logprobs { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string allow_search_indices { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string allow_view { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string allow_fine_tuning { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string organization { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string @group { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string is_blocking { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class ChatResError
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 错误信息
|
|
||||||
/// </summary>
|
|
||||||
public string message { get; set; } = string.Empty;
|
|
||||||
/// <summary>
|
|
||||||
/// 错误类型
|
|
||||||
/// </summary>
|
|
||||||
public string type { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
}
|
|
||||||
public class ChatResSSE
|
|
||||||
{
|
|
||||||
public string id { get; set; }
|
|
||||||
public int created { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 模型id
|
|
||||||
/// </summary>
|
|
||||||
public string model { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 对话
|
|
||||||
/// </summary>
|
|
||||||
public ChoiceSSE[] choices { get; set; }
|
|
||||||
}
|
|
||||||
public class ChatRes
|
|
||||||
{
|
|
||||||
public ChatResError? error { get; set; }
|
|
||||||
public string id { get; set; }
|
|
||||||
public int created { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 模型id
|
|
||||||
/// </summary>
|
|
||||||
public string model { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 对话
|
|
||||||
/// </summary>
|
|
||||||
public Choice[] choices { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// token使用情况
|
|
||||||
/// </summary>
|
|
||||||
public Usage usage { get; set; }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// token使用情况
|
|
||||||
/// </summary>
|
|
||||||
public class Usage
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 输入token数量
|
|
||||||
/// </summary>
|
|
||||||
public int prompt_tokens { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 返回token数量
|
|
||||||
/// </summary>
|
|
||||||
public int completion_tokens { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 总计token数量
|
|
||||||
/// </summary>
|
|
||||||
public int total_tokens { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ChoiceSSE
|
|
||||||
{
|
|
||||||
public int index { get; set; }
|
|
||||||
public Message delta { get; set; }
|
|
||||||
public string finish_reason { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// token使用情况
|
|
||||||
/// </summary>
|
|
||||||
public Usage usage { get; set; }
|
|
||||||
}
|
|
||||||
public class Choice
|
|
||||||
{
|
|
||||||
public int index { get; set; }
|
|
||||||
public Message message { get; set; }
|
|
||||||
public string finish_reason { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Message
|
|
||||||
{
|
|
||||||
public string role { get; set; }
|
|
||||||
public string content { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ModelInfo
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int created { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string id { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string @object { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string owned_by { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public List<PermissionItem> permission { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string root { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string parent { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ModelListResp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string @object { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public List<ModelInfo> data { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class FileListResp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string @object { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public List<FileItem> data { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FileContent
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string content { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string file_type { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string filename { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string title { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string type { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class FileItem
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string id { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string @object { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int bytes { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int created_at { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string filename { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string purpose { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string status { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string status_details { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class ErrorMsg
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string message { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string type { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ErrorResponse
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public ErrorMsg error { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -179,11 +179,8 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
.Where(it => it.Id == long.Parse(task))
|
.Where(it => it.Id == long.Parse(task))
|
||||||
.ExecuteCommandAsync();
|
.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.NewTask();
|
RedisExpand.NewTaskAsync();
|
||||||
|
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.ChatModelAnalysis, task);
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,6 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
|
||||||
.SetColumns(it => it.Speaker == speakerStr)
|
.SetColumns(it => it.Speaker == speakerStr)
|
||||||
.Where(it => it.Id == long.Parse(task))
|
.Where(it => it.Id == long.Parse(task))
|
||||||
.ExecuteCommandAsync();
|
.ExecuteCommandAsync();
|
||||||
//加入下一队列
|
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.ChatModelAnalysis, task);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ namespace VideoAnalysisCore.AICore.Whisper
|
||||||
res.Add(new WhisperResDto(segment));
|
res.Add(new WhisperResDto(segment));
|
||||||
}
|
}
|
||||||
RedisExpand.Redis.HMSet(RedisExpandKey.Task(task), "Captions", res);
|
RedisExpand.Redis.HMSet(RedisExpandKey.Task(task), "Captions", res);
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.ParsingSpeaker, task);
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检测语言的方法
|
/// 检测语言的方法
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using FreeRedis;
|
using FreeRedis;
|
||||||
using Microsoft.Extensions.DependencyModel;
|
using Microsoft.Extensions.DependencyModel;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using SqlSugar.IOC;
|
using SqlSugar.IOC;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
|
@ -224,6 +225,24 @@ namespace VideoAnalysisCore.Common
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// 获取下一个枚举值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="current"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
|
public static T? NextEnum<T>(this T current) where T : struct, Enum
|
||||||
|
{
|
||||||
|
if (!typeof(T).IsEnum)
|
||||||
|
throw new ArgumentException("传入类型不是枚举");
|
||||||
|
T[] values = (T[])Enum.GetValues(typeof(T));
|
||||||
|
int currentIndex = Array.IndexOf(values, current);
|
||||||
|
if (currentIndex == values.Length - 1)
|
||||||
|
return null;
|
||||||
|
int nextIndex = (currentIndex + 1) % values.Length;
|
||||||
|
return values[nextIndex];
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
/// 转化枚举
|
/// 转化枚举
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
|
|
@ -232,6 +251,8 @@ namespace VideoAnalysisCore.Common
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if(value is null || string.IsNullOrEmpty(value.ToString()))
|
||||||
|
return null;
|
||||||
if (Enum.TryParse<T>(value.ToString(), true, out var result) && Enum.IsDefined(typeof(T), result))
|
if (Enum.TryParse<T>(value.ToString(), true, out var result) && Enum.IsDefined(typeof(T), result))
|
||||||
return result;
|
return result;
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ namespace VideoAnalysisCore.Common
|
||||||
//获取资源文件 地址
|
//获取资源文件 地址
|
||||||
var taskInfo = await videoTaskDB.AsQueryable()
|
var taskInfo = await videoTaskDB.AsQueryable()
|
||||||
.Where(s => s.Id == taskId).FirstAsync();
|
.Where(s => s.Id == taskId).FirstAsync();
|
||||||
if (taskInfo is null || string.IsNullOrEmpty(taskInfo.MediaName) || taskInfo.MediaName.Contains("教研"))
|
if (taskInfo is null )
|
||||||
throw new Exception($"任务为null/是教研视频/没有视频课程名称");
|
throw new Exception($"任务为null/是教研视频/没有视频课程名称");
|
||||||
var fileUrl = taskInfo.MediaUrl;
|
var fileUrl = taskInfo.MediaUrl;
|
||||||
if (string.IsNullOrEmpty(fileUrl))
|
if (string.IsNullOrEmpty(fileUrl))
|
||||||
|
|
@ -200,9 +200,9 @@ namespace VideoAnalysisCore.Common
|
||||||
(s, e) => RedisExpand.SetTaskProgress(task, "PPT->" + Math.Round(e.ProgressPercentage, 1)
|
(s, e) => RedisExpand.SetTaskProgress(task, "PPT->" + Math.Round(e.ProgressPercentage, 1)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch
|
||||||
{
|
{
|
||||||
await RedisExpand.SetTaskErrorMessage(taskId, e);
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
|
|
@ -210,13 +210,10 @@ namespace VideoAnalysisCore.Common
|
||||||
await Download(fileUrl, localPath, task + fileExtension,
|
await Download(fileUrl, localPath, task + fileExtension,
|
||||||
(s, e) => RedisExpand.SetTaskProgress(task, Math.Round(e.ProgressPercentage,1)
|
(s, e) => RedisExpand.SetTaskProgress(task, Math.Round(e.ProgressPercentage,1)
|
||||||
));
|
));
|
||||||
|
|
||||||
//加入下一队列
|
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.SeparateAudio, task);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch
|
||||||
{
|
{
|
||||||
await RedisExpand.SetTaskErrorMessage(taskId, e);
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,10 +88,14 @@ namespace VideoAnalysisCore.Common.Expand
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var path = item.VideoTaskId.ToString() + "/" + Path.GetFileName(item.FilePath);
|
var isDebug = false;
|
||||||
|
#if DEBUG
|
||||||
|
isDebug = true;
|
||||||
|
#endif
|
||||||
|
var path = (isDebug ?"debug/":string.Empty) + item.VideoTaskId.ToString() + "/" + Path.GetFileName(item.FilePath);
|
||||||
if (cached.Contains(item.FilePath))
|
if (cached.Contains(item.FilePath))
|
||||||
{
|
{
|
||||||
item.ImageUrl = AppCommon.Config.AliyunOSS.BucketDomain + "/" + path;
|
item.PPTImageUrl = AppCommon.Config.AliyunOSS.BucketDomain + "/" + path;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
using var file = File.OpenRead(item.FilePath);
|
using var file = File.OpenRead(item.FilePath);
|
||||||
|
|
@ -100,7 +104,7 @@ namespace VideoAnalysisCore.Common.Expand
|
||||||
AppCommon.Config.AliyunOSS.BucketName,
|
AppCommon.Config.AliyunOSS.BucketName,
|
||||||
path,
|
path,
|
||||||
file);
|
file);
|
||||||
item.ImageUrl = AppCommon.Config.AliyunOSS.BucketDomain + "/" + path;
|
item.PPTImageUrl = AppCommon.Config.AliyunOSS.BucketDomain + "/" + path;
|
||||||
cached.Add(item.FilePath);
|
cached.Add(item.FilePath);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
using FreeRedis;
|
using FreeRedis;
|
||||||
using FreeRedis.Internal;
|
using FreeRedis.Internal;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NetTaste;
|
||||||
using Newtonsoft.Json.Schema;
|
using Newtonsoft.Json.Schema;
|
||||||
using SqlSugar.IOC;
|
using SqlSugar.IOC;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -20,6 +22,7 @@ using VideoAnalysisCore.AICore.Whisper;
|
||||||
using VideoAnalysisCore.Model;
|
using VideoAnalysisCore.Model;
|
||||||
using VideoAnalysisCore.Model.Dto;
|
using VideoAnalysisCore.Model.Dto;
|
||||||
using VideoAnalysisCore.Model.Enum;
|
using VideoAnalysisCore.Model.Enum;
|
||||||
|
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||||
|
|
||||||
namespace VideoAnalysisCore.Common
|
namespace VideoAnalysisCore.Common
|
||||||
{
|
{
|
||||||
|
|
@ -76,11 +79,12 @@ namespace VideoAnalysisCore.Common
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class RedisExpand
|
public static class RedisExpand
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// redis 连接
|
/// redis 连接
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static RedisClient Redis = new RedisClient(AppCommon.Config.Redis.ConnectionString);
|
public static RedisClient Redis = new RedisClient(AppCommon.Config.Redis.ConnectionString);
|
||||||
public static Dictionary<RedisChannelEnum, Action<string>> SubscribeList = new Dictionary<RedisChannelEnum, Action<string>>();
|
public static Dictionary<RedisChannelEnum, Func<string,Task>> SubscribeList = new Dictionary<RedisChannelEnum, Func<string, Task>>();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 队列池
|
/// 队列池
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -148,7 +152,7 @@ namespace VideoAnalysisCore.Common
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="enum">枚举</param>
|
/// <param name="enum">枚举</param>
|
||||||
/// <param name="taskId">任务id</param>
|
/// <param name="taskId">任务id</param>
|
||||||
public static void InsertChannel(RedisChannelEnum @enum, object taskId)
|
public static async Task InsertChannel(RedisChannelEnum @enum, object taskId)
|
||||||
{
|
{
|
||||||
if (taskId is null) throw new Exception("taskId为空");
|
if (taskId is null) throw new Exception("taskId为空");
|
||||||
if (Redis is null) throw new Exception("redis未初始化");
|
if (Redis is null) throw new Exception("redis未初始化");
|
||||||
|
|
@ -156,6 +160,13 @@ namespace VideoAnalysisCore.Common
|
||||||
var startTime = Redis.HMGet<Dictionary<RedisChannelEnum, DateTime>>(RedisExpandKey.Task(taskId), "StartTime").FirstOrDefault();
|
var startTime = Redis.HMGet<Dictionary<RedisChannelEnum, DateTime>>(RedisExpandKey.Task(taskId), "StartTime").FirstOrDefault();
|
||||||
if (startTime is null)
|
if (startTime is null)
|
||||||
startTime = new Dictionary<RedisChannelEnum, DateTime>();
|
startTime = new Dictionary<RedisChannelEnum, DateTime>();
|
||||||
|
if (!SubscribeList.ContainsKey(@enum))
|
||||||
|
throw new Exception(@enum + " 未实现");
|
||||||
|
var tId = taskId.ToString();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
if (!startTime.ContainsKey(@enum))
|
if (!startTime.ContainsKey(@enum))
|
||||||
startTime.Add(@enum, DateTime.Now);
|
startTime.Add(@enum, DateTime.Now);
|
||||||
else
|
else
|
||||||
|
|
@ -163,10 +174,17 @@ namespace VideoAnalysisCore.Common
|
||||||
|
|
||||||
Redis.HMSet(RedisExpandKey.Task(taskId), "StartTime", startTime);
|
Redis.HMSet(RedisExpandKey.Task(taskId), "StartTime", startTime);
|
||||||
|
|
||||||
if (!SubscribeList.ContainsKey(@enum))
|
await SubscribeList[@enum](tId);
|
||||||
throw new Exception(@enum + " 未实现");
|
var e = @enum.NextEnum();
|
||||||
|
if (e is null)
|
||||||
SubscribeList[@enum].Invoke(taskId.ToString());
|
break;
|
||||||
|
@enum = e.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await SetTaskErrorMessage((long)taskId, ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task TaskEnd(string task)
|
public static async Task TaskEnd(string task)
|
||||||
|
|
@ -176,7 +194,8 @@ namespace VideoAnalysisCore.Common
|
||||||
// .HMGetAsync<TaskRes>(RedisExpandKey.Task(task), "ChatAnalysis")).FirstOrDefault();
|
// .HMGetAsync<TaskRes>(RedisExpandKey.Task(task), "ChatAnalysis")).FirstOrDefault();
|
||||||
//if (gptRes is null)
|
//if (gptRes is null)
|
||||||
// throw new Exception("未能读取到GPT处理结果");
|
// throw new Exception("未能读取到GPT处理结果");
|
||||||
|
//删除任务执行状态
|
||||||
|
await Redis.HDelAsync(RedisExpandKey.IDTask,task);
|
||||||
var taskData = await DbScoped.Sugar.Queryable<VideoTask>()
|
var taskData = await DbScoped.Sugar.Queryable<VideoTask>()
|
||||||
.FirstAsync(s => s.Id == tId);
|
.FirstAsync(s => s.Id == tId);
|
||||||
if (taskData.Captions == "[]")
|
if (taskData.Captions == "[]")
|
||||||
|
|
@ -189,7 +208,7 @@ namespace VideoAnalysisCore.Common
|
||||||
//taskData.ChatAnalysis = JsonSerializer.Serialize(gptRes);
|
//taskData.ChatAnalysis = JsonSerializer.Serialize(gptRes);
|
||||||
taskData.ChatAnalysisScore =0;
|
taskData.ChatAnalysisScore =0;
|
||||||
taskData.ErrorMessage = string.Empty;
|
taskData.ErrorMessage = string.Empty;
|
||||||
taskData.LastEnum = RedisChannelEnum.EndTask;
|
taskData.LastEnum = RedisChannelEnum.结束任务;
|
||||||
taskData.EndTime = DateTime.Now;
|
taskData.EndTime = DateTime.Now;
|
||||||
await DbScoped.Sugar.Updateable(taskData)
|
await DbScoped.Sugar.Updateable(taskData)
|
||||||
.UpdateColumns(it => new
|
.UpdateColumns(it => new
|
||||||
|
|
@ -214,30 +233,24 @@ namespace VideoAnalysisCore.Common
|
||||||
{
|
{
|
||||||
if (Redis is null) throw new Exception("redis未初始化");
|
if (Redis is null) throw new Exception("redis未初始化");
|
||||||
|
|
||||||
SubscribeList.Add(RedisChannelEnum.DownloadFile,
|
SubscribeList.Add(RedisChannelEnum.下载文件,
|
||||||
(Action<string>)((msg) => {
|
async (msg) => await TouchChannel(RedisChannelEnum.下载文件, msg,
|
||||||
TouchChannel(RedisChannelEnum.DownloadFile, msg,
|
(task) =>
|
||||||
(Func<string, Task>)((task) =>
|
|
||||||
{
|
{
|
||||||
using var scope = AppCommon.Services?.CreateScope();
|
using var scope = AppCommon.Services?.CreateScope();
|
||||||
if (scope is null || ServiceProviderServiceExtensions.GetService<DownloadFile>(scope.ServiceProvider) is null)
|
if (scope is null || scope.ServiceProvider.GetService<DownloadFile>() is null)
|
||||||
throw new Exception("DownloadFile 未注入");
|
throw new Exception("DownloadFile 未注入");
|
||||||
else
|
else
|
||||||
return (Task)(scope.ServiceProvider.GetService<DownloadFile>()?.RunTask(task) ?? Task.CompletedTask);
|
return scope.ServiceProvider.GetService<DownloadFile>()?.RunTask(task) ?? Task.CompletedTask;
|
||||||
}));
|
}));
|
||||||
}));
|
SubscribeList.Add(RedisChannelEnum.分离音频,
|
||||||
|
async (msg) => await TouchChannel(RedisChannelEnum.分离音频, msg, FFMPGEHandle.RunAsync));
|
||||||
SubscribeList.Add(RedisChannelEnum.SeparateAudio,
|
SubscribeList.Add(RedisChannelEnum.解析字幕,
|
||||||
(msg) => { TouchChannel(RedisChannelEnum.SeparateAudio, msg, FFMPGEHandle.RunAsync); });
|
async (msg) => await TouchChannel(RedisChannelEnum.解析字幕, msg, SenseVoice.RunTask));
|
||||||
|
//SubscribeList.Add(RedisChannelEnum.解析说话人,
|
||||||
SubscribeList.Add(RedisChannelEnum.ParsingCaptions,
|
// async (msg) => await TouchChannel(RedisChannelEnum.解析说话人, msg, Speaker.Run));
|
||||||
(msg) => { TouchChannel(RedisChannelEnum.ParsingCaptions, msg, SenseVoice.RunTask); });
|
SubscribeList.Add(RedisChannelEnum.AI模型分析,
|
||||||
SubscribeList.Add(RedisChannelEnum.ParsingSpeaker,
|
async (msg) => await TouchChannel(RedisChannelEnum.AI模型分析, msg,
|
||||||
(msg) => { TouchChannel(RedisChannelEnum.ParsingSpeaker, msg, Speaker.Run); });
|
|
||||||
SubscribeList.Add(RedisChannelEnum.ChatModelAnalysis,
|
|
||||||
(msg) =>
|
|
||||||
{
|
|
||||||
TouchChannel(RedisChannelEnum.ChatModelAnalysis, msg,
|
|
||||||
(task) =>
|
(task) =>
|
||||||
{
|
{
|
||||||
using var scope = AppCommon.Services?.CreateScope();
|
using var scope = AppCommon.Services?.CreateScope();
|
||||||
|
|
@ -245,63 +258,75 @@ namespace VideoAnalysisCore.Common
|
||||||
throw new Exception("IBserGPT 未注入");
|
throw new Exception("IBserGPT 未注入");
|
||||||
else
|
else
|
||||||
return scope.ServiceProvider.GetService<IBserGPT>()?.GetKnow(task) ?? Task.CompletedTask;
|
return scope.ServiceProvider.GetService<IBserGPT>()?.GetKnow(task) ?? Task.CompletedTask;
|
||||||
});
|
}));
|
||||||
});
|
SubscribeList.Add(RedisChannelEnum.AI分析试题,
|
||||||
SubscribeList.Add(RedisChannelEnum.EndTask,
|
async (msg) => await TouchChannel(RedisChannelEnum.AI分析试题, msg,
|
||||||
(msg) => { TouchChannel(RedisChannelEnum.EndTask, msg, TaskEnd); });
|
(task) =>
|
||||||
|
{
|
||||||
|
using var scope = AppCommon.Services?.CreateScope();
|
||||||
|
if (scope is null || scope.ServiceProvider.GetService<IBserGPT>() is null)
|
||||||
|
throw new Exception("IBserGPT 未注入");
|
||||||
|
else
|
||||||
|
return scope.ServiceProvider.GetService<IBserGPT>()?.GetVideoQuestion(task) ?? Task.CompletedTask;
|
||||||
|
}));
|
||||||
|
SubscribeList.Add(RedisChannelEnum.结束任务,
|
||||||
|
async (msg) => await TouchChannel(RedisChannelEnum.结束任务, msg, TaskEnd));
|
||||||
|
|
||||||
await ReceivingTaskAsync();
|
ReceivingTaskAsync();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 重新执行新任务
|
/// 重新执行新任务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static void NewTask()
|
public static async void NewTaskAsync()
|
||||||
{
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
await Redis.DelAsync(RedisExpandKey.IDTask);
|
await Redis.DelAsync(RedisExpandKey.IDTask);
|
||||||
await ReceivingTaskAsync();
|
ReceivingTaskAsync();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 重新接收新任务
|
/// 重新接收新任务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task ReceivingTaskAsync()
|
public static void ReceivingTaskAsync()
|
||||||
{
|
{
|
||||||
if (AppCommon.Config.TaskSetting.IS_Server)
|
if (AppCommon.Config.TaskSetting.IS_Server)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{DateTime.Now} =>服务端不接收任务");
|
Console.WriteLine($"{DateTime.Now} =>服务端不接收任务");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
var oldTask = await Redis.GetAsync(RedisExpandKey.IDTask);
|
var oldTask = await Redis.GetAsync(RedisExpandKey.IDTask);
|
||||||
if (!string.IsNullOrEmpty(oldTask))
|
if (!string.IsNullOrEmpty(oldTask))
|
||||||
{
|
{
|
||||||
|
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-------------> 接收重试任务 " + oldTask);
|
||||||
|
await ClearTaskError(long.Parse(oldTask));
|
||||||
var lastEnum = (await Redis.HMGetAsync<RedisChannelEnum>(RedisExpandKey.Task(oldTask), "LastEnum")).FirstOrDefault();
|
var lastEnum = (await Redis.HMGetAsync<RedisChannelEnum>(RedisExpandKey.Task(oldTask), "LastEnum")).FirstOrDefault();
|
||||||
await SetTaskErrorMessage(long.Parse(oldTask), null);
|
await InsertChannel(lastEnum, oldTask);
|
||||||
InsertChannel(lastEnum, oldTask);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Subscribe?.IsUnsubscribed == false)//排除重试机制后 多次接收任务导致内存泄露
|
if (Subscribe?.IsUnsubscribed == false)//排除重试机制后 多次接收任务导致内存泄露
|
||||||
return;
|
return;
|
||||||
Subscribe = Redis.SubscribeList(RedisExpandKey.ChannelKey, (taskId) =>
|
Subscribe = Redis.SubscribeList(RedisExpandKey.ChannelKey, async (taskId) =>
|
||||||
{
|
{
|
||||||
if (taskId is null) return;
|
if (taskId is null) return;
|
||||||
Subscribe?.Dispose();
|
Subscribe?.Dispose();//取消接收任务监听
|
||||||
//存储当前机器的任务
|
//存储当前机器的任务
|
||||||
Redis.Set(RedisExpandKey.IDTask, taskId);
|
Redis.Set(RedisExpandKey.IDTask, taskId);
|
||||||
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-------------> 接收到任务 " + taskId);
|
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-------------> 接收到任务 " + taskId);
|
||||||
InsertChannel(RedisChannelEnum.DownloadFile, taskId);
|
await InsertChannel(RedisChannelEnum.下载文件, taskId);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 写入任务异常
|
/// 写入任务异常
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="taskID"></param>
|
/// <param name="taskID"></param>
|
||||||
/// <param name="errorMessage"></param>
|
/// <param name="ex"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task<bool> SetTaskErrorMessage(long taskID, Exception? ex)
|
public static async Task<bool> SetTaskErrorMessage(long taskID, Exception? ex)
|
||||||
{
|
{
|
||||||
|
|
@ -315,12 +340,25 @@ namespace VideoAnalysisCore.Common
|
||||||
Console.WriteLine(ex.StackTrace);
|
Console.WriteLine(ex.StackTrace);
|
||||||
Console.WriteLine("==============================================");
|
Console.WriteLine("==============================================");
|
||||||
//清除失败任务 重新接收任务
|
//清除失败任务 重新接收任务
|
||||||
await Redis.DelAsync(RedisExpandKey.IDTask);
|
NewTaskAsync();
|
||||||
await ReceivingTaskAsync();
|
|
||||||
}
|
}
|
||||||
|
return await SetTaskError(taskID, error);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 清除 任务的错误信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="taskID"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<bool> ClearTaskError(long taskID) =>await SetTaskError(taskID, string.Empty);
|
||||||
|
/// <summary>
|
||||||
|
/// 修改任务的错误信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="taskID"></param>
|
||||||
|
/// <param name="error"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<bool> SetTaskError(long taskID, string? error)
|
||||||
|
{
|
||||||
Redis.HMSet(RedisExpandKey.Task(taskID), "ErrorMessage", error);
|
Redis.HMSet(RedisExpandKey.Task(taskID), "ErrorMessage", error);
|
||||||
|
|
||||||
return await DbScoped.Sugar.Updateable<VideoTask>()
|
return await DbScoped.Sugar.Updateable<VideoTask>()
|
||||||
.SetColumns(it => it.ErrorMessage == error)//SetColumns是可以叠加的 写2个就2个字段赋值
|
.SetColumns(it => it.ErrorMessage == error)//SetColumns是可以叠加的 写2个就2个字段赋值
|
||||||
.Where(it => it.Id == taskID)
|
.Where(it => it.Id == taskID)
|
||||||
|
|
@ -333,14 +371,14 @@ namespace VideoAnalysisCore.Common
|
||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <param name="taskId"></param>
|
/// <param name="taskId"></param>
|
||||||
/// <param name="action"></param>
|
/// <param name="action"></param>
|
||||||
public static async void TouchChannel(RedisChannelEnum key, string taskId, Func<string, Task> action = null)
|
public static async Task TouchChannel(RedisChannelEnum key, string taskId, Func<string, Task> action = null)
|
||||||
{
|
{
|
||||||
if (taskId is null) return;
|
if (taskId is null) return;
|
||||||
var tID = long.Parse(taskId);
|
var tID = long.Parse(taskId);
|
||||||
if (action is not null)
|
if (action is not null)
|
||||||
{
|
{
|
||||||
var errArr = new Exception[3];
|
var tryCount = 1;
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < tryCount; i++)
|
||||||
{
|
{
|
||||||
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-> 开始执行 " + key + " " + taskId);
|
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-> 开始执行 " + key + " " + taskId);
|
||||||
try
|
try
|
||||||
|
|
@ -360,16 +398,16 @@ namespace VideoAnalysisCore.Common
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errArr[i] = ex;
|
|
||||||
Console.WriteLine("====================[出现异常]====================");
|
Console.WriteLine("====================[出现异常]====================");
|
||||||
Console.WriteLine(ex.Message);
|
Console.WriteLine(ex.Message);
|
||||||
Console.WriteLine(ex.StackTrace);
|
Console.WriteLine(ex.StackTrace);
|
||||||
Console.WriteLine("==============================================");
|
Console.WriteLine("==============================================");
|
||||||
Thread.Sleep(1000);
|
Thread.Sleep(1000);
|
||||||
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-> 稍后后重试." + key + " " + taskId);
|
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-> 稍后后重试." + key + " " + taskId);
|
||||||
|
if (i+1== tryCount)
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await SetTaskErrorMessage(tID, errArr.First());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -146,26 +146,12 @@ namespace VideoAnalysisCore.Controllers
|
||||||
task.Subject = subject;
|
task.Subject = subject;
|
||||||
await videoTaskDB.UpdateAsync(task);
|
await videoTaskDB.UpdateAsync(task);
|
||||||
}
|
}
|
||||||
//重新开始执行GPT分析
|
//todo重新开始执行GPT分析
|
||||||
RedisExpand.InsertChannel(RedisChannelEnum.ChatModelAnalysis
|
|
||||||
, task.Id);
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 插入队列
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="enum"></param>
|
|
||||||
/// <param name="msg"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost(Name = "TestInsertChannel")]
|
|
||||||
public IActionResult TestInsertChannel(int @enum = 1, string msg = "1")
|
|
||||||
{
|
|
||||||
RedisExpand.InsertChannel(@enum.ToEnum<RedisChannelEnum>().Value
|
|
||||||
, msg);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ÊÓÆµ´¦Àí
|
/// ÊÓÆµ´¦Àí
|
||||||
|
|
@ -190,7 +176,6 @@ namespace VideoAnalysisCore.Controllers
|
||||||
Subject = req.Subject,
|
Subject = req.Subject,
|
||||||
Tag = req.Tag,
|
Tag = req.Tag,
|
||||||
TagId = req.TagId,
|
TagId = req.TagId,
|
||||||
MediaName = req.Name,
|
|
||||||
PPTVideoCode = req.PPTVideoCode,
|
PPTVideoCode = req.PPTVideoCode,
|
||||||
VideoType=req.VideoType
|
VideoType=req.VideoType
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -46,18 +46,24 @@ namespace VideoAnalysisCore.Controllers.Dto
|
||||||
}
|
}
|
||||||
public class NodePackageReq
|
public class NodePackageReq
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 素材Id 需要返回给我的参数
|
||||||
|
/// </summary>
|
||||||
|
public long MaterialId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 录播结构目录节点编号
|
/// 文件Id 需要返回给我的参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required(ErrorMessage = "目录节点编号是必填项")]
|
public long AttachmentId { get; set; }
|
||||||
public long NodeId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 科目类型
|
/// 需要返回给我的参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required(ErrorMessage = "科目类型是必填项")]
|
public string VideoCode { get; set; }
|
||||||
public SubjectEnum SubjectType { get; set; }
|
/// <summary>
|
||||||
|
/// 视频地址
|
||||||
|
/// </summary>
|
||||||
|
public string VideoUrl { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 任务类型
|
/// 任务类型
|
||||||
|
|
@ -66,10 +72,39 @@ namespace VideoAnalysisCore.Controllers.Dto
|
||||||
public TaskTypeEnum TaskType { get; set; }
|
public TaskTypeEnum TaskType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 视频列表
|
/// 课程类型 0=新课 3=复习 4= 活动 5=班会 7=其他资料
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required(ErrorMessage = "文件数量是必填项")]
|
public AttachmentsInfoType CourseType { get; set; }
|
||||||
public List<StructurePageContentAnalyzeItem> AnalyzeItems { get; set; }
|
/// <summary>
|
||||||
|
/// 视频类型 PPT课件 = 1, 摄像头 = 2
|
||||||
|
/// </summary>
|
||||||
|
public VideoType VideoType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分析完成后的回调地址
|
||||||
|
/// </summary>
|
||||||
|
public string CallBackUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求区域 回调的时候 需要把这个添加到Headers里面 Key=Area value=Area的值
|
||||||
|
/// </summary>
|
||||||
|
public string Area { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 请求区域 回调的时候 需要把这个添加到Headers里面 Key=HostIP value=HostIP的值
|
||||||
|
/// </summary>
|
||||||
|
public string HostIP { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 科目类型
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "科目类型是必填项")]
|
||||||
|
public SubjectEnum SubjectId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内容的Id 当内容id相同的时候 则表示是一组数据
|
||||||
|
/// <para>用于查找视频下对应的PPT资源</para>
|
||||||
|
/// </summary>
|
||||||
|
public long ContentId { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -103,11 +138,6 @@ namespace VideoAnalysisCore.Controllers.Dto
|
||||||
[Required(ErrorMessage = "资源URL是必填项")]
|
[Required(ErrorMessage = "资源URL是必填项")]
|
||||||
public string MediaUrl { get; set; } = string.Empty;
|
public string MediaUrl { get; set; } = string.Empty;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源名称
|
|
||||||
/// </summary>
|
|
||||||
[Required(ErrorMessage = "资源名称是必要的")]
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
/// <summary>
|
|
||||||
/// ApiKey
|
/// ApiKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required(ErrorMessage = "接口Token是必填项")]
|
[Required(ErrorMessage = "接口Token是必填项")]
|
||||||
|
|
|
||||||
|
|
@ -45,79 +45,53 @@ namespace VideoAnalysisCore.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 蓝鲸智库_添加文件节点监控
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="req">请求体</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost(Name = "NodeSubscription")]
|
|
||||||
[NonAction, Obsolete]
|
|
||||||
public async Task<IActionResult> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 蓝鲸智库_文件包订阅
|
/// 蓝鲸智库_文件包订阅
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="req">请求体</param>
|
/// <param name="reqArr">请求体</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost(Name = "NodePackage")]
|
[HttpPost(Name = "NodePackage")]
|
||||||
public async Task<IActionResult> NodePackage(NodePackageReq req)
|
public async Task<IActionResult> NodePackage(NodePackageReq[] reqArr)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{DateTime.Now} 文件包订阅请求 req=" + JsonSerializer.Serialize(req));
|
Console.WriteLine($"{DateTime.Now} 文件包订阅请求 req=" + JsonSerializer.Serialize(reqArr));
|
||||||
if (req.AnalyzeItems is null || req.AnalyzeItems.Count() == 0)
|
if (reqArr is null || reqArr.Count() == 0)
|
||||||
return BadRequest("无效视频列表数据");
|
return BadRequest("无效视频列表数据");
|
||||||
var videos = new List<VideoTask>(req.AnalyzeItems.Count);
|
var videos = new List<VideoTask>(reqArr.Count());
|
||||||
var nodePackages = new List<NodePackageInfo>(req.AnalyzeItems.Count);
|
var nodePackages = new List<NodePackageInfo>(reqArr.Count());
|
||||||
var videoIdArr = videoTaskDB.AsQueryable().Select(v => v.TagId).Distinct().ToArray();
|
var videoIdArr = videoTaskDB.AsQueryable().Select(v => v.TagId).Distinct().ToArray();
|
||||||
foreach (var s in req.AnalyzeItems)
|
foreach (var sGroup in reqArr.GroupBy(s=>s.ContentId))
|
||||||
{
|
{
|
||||||
|
var s= sGroup.First(s=>s.VideoType==VideoType.摄像头);
|
||||||
|
var sPPT= sGroup.FirstOrDefault(s=>s.VideoType==VideoType.PPT课件);
|
||||||
var np = new NodePackageInfo()
|
var np = new NodePackageInfo()
|
||||||
{
|
{
|
||||||
VideoCode = s.VideoCode,
|
VideoCode = s.VideoCode,
|
||||||
AttachmentsInfoType = s.AttachmentsInfoType,
|
|
||||||
MaterialId = s.MaterialId,
|
MaterialId = s.MaterialId,
|
||||||
StructurePageContentId = s.StructurePageContentId,
|
AttachmentId = s.AttachmentId,
|
||||||
VideoName = s.VideoName,
|
TaskType = s.TaskType,
|
||||||
NodeId = req.NodeId,
|
SubjectType = s.SubjectId,
|
||||||
TaskType = req.TaskType,
|
|
||||||
SubjectType = req.SubjectType,
|
|
||||||
VideoUrl =s.VideoUrl,
|
VideoUrl =s.VideoUrl,
|
||||||
|
CourseType = s.CourseType,
|
||||||
CallBackUrl=s.CallBackUrl,
|
CallBackUrl=s.CallBackUrl,
|
||||||
|
Area = s.Area,
|
||||||
|
HostIP = s.HostIP,
|
||||||
};
|
};
|
||||||
nodePackages.Add(np);
|
nodePackages.Add(np);
|
||||||
if (s.AttachmentsInfoType == AttachmentsInfoType.PPT)
|
|
||||||
continue;
|
|
||||||
if (videoIdArr.Contains(s.VideoCode))
|
if (videoIdArr.Contains(s.VideoCode))
|
||||||
continue;
|
continue;
|
||||||
var pptInfo = req.AnalyzeItems //获取ppt videoCode
|
var pptCode = sPPT!=null ? sPPT.VideoCode : string.Empty;
|
||||||
.FirstOrDefault(x => x.AttachmentsInfoType == AttachmentsInfoType.PPT
|
|
||||||
&& s.StructurePageContentId == x.StructurePageContentId);
|
|
||||||
var pptCode = pptInfo?.VideoCode ?? (pptInfo?.VideoUrl??string.Empty);
|
|
||||||
videos.Add(new VideoTask()
|
videos.Add(new VideoTask()
|
||||||
{
|
{
|
||||||
Id = YitIdHelper.NextId(),
|
Id = YitIdHelper.NextId(),
|
||||||
ComeFrom = "127.0.0.1",
|
ComeFrom = GetClientIpAddress(),
|
||||||
ApiToken = "",
|
ApiToken = "",
|
||||||
Type = req.TaskType,
|
Type = s.TaskType,
|
||||||
Subject = req.SubjectType,
|
Subject = s.SubjectId,
|
||||||
TagId = s.VideoCode,
|
TagId = s.VideoCode,
|
||||||
MediaUrl =s.VideoUrl,
|
MediaUrl =s.VideoUrl,
|
||||||
MediaName = s.VideoName,
|
|
||||||
PPTVideoCode = pptCode,
|
PPTVideoCode = pptCode,
|
||||||
VideoType =s.AttachmentsInfoType
|
VideoType =s.CourseType
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await nodePackageInfoDB.InsertRangeAsync(nodePackages);
|
await nodePackageInfoDB.InsertRangeAsync(nodePackages);
|
||||||
|
|
@ -129,6 +103,16 @@ namespace VideoAnalysisCore.Controllers
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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地址");
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取任务类型
|
/// 获取任务类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ using System.Net;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Policy;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using UserCenter.Model.Enum;
|
using UserCenter.Model.Enum;
|
||||||
using VideoAnalysisCore.Common;
|
using VideoAnalysisCore.Common;
|
||||||
|
|
@ -31,6 +33,7 @@ namespace VideoAnalysisCore.Job
|
||||||
private readonly Repository<NodePackageInfo> nodePackageInfoDB;
|
private readonly Repository<NodePackageInfo> nodePackageInfoDB;
|
||||||
private readonly Repository<Attachments> attachmentsDB;
|
private readonly Repository<Attachments> attachmentsDB;
|
||||||
private readonly Repository<VideoTask> videoTaskDB;
|
private readonly Repository<VideoTask> videoTaskDB;
|
||||||
|
private readonly IHttpClientFactory _clientFactory;
|
||||||
public NodePackageJob(Repository<Attachments> videoTaskDB,
|
public NodePackageJob(Repository<Attachments> videoTaskDB,
|
||||||
Repository<NodePackageInfo> nodePackageInfoDB, Repository<VideoTask> videotaskDB)
|
Repository<NodePackageInfo> nodePackageInfoDB, Repository<VideoTask> videotaskDB)
|
||||||
{
|
{
|
||||||
|
|
@ -65,8 +68,6 @@ namespace VideoAnalysisCore.Job
|
||||||
Console.WriteLine($"{DateTime.Now} 执行=>文件包任务 任务回调 数量{postData.Count} ...");
|
Console.WriteLine($"{DateTime.Now} 执行=>文件包任务 任务回调 数量{postData.Count} ...");
|
||||||
if (postData.Count() == 0)
|
if (postData.Count() == 0)
|
||||||
return;
|
return;
|
||||||
//var responseMessage = await new HttpClient()
|
|
||||||
// .PostAsJsonAsync(AppCommon.Config.Subsystem.蓝鲸智库.APIUrl + "/api/callback/platform/videosAnalyze", postData);
|
|
||||||
foreach (var item in taskArr)
|
foreach (var item in taskArr)
|
||||||
{
|
{
|
||||||
HttpResponseMessage responseMessage = null;
|
HttpResponseMessage responseMessage = null;
|
||||||
|
|
@ -75,8 +76,14 @@ namespace VideoAnalysisCore.Job
|
||||||
var postUrl = !string.IsNullOrWhiteSpace(item.CallBackUrl)
|
var postUrl = !string.IsNullOrWhiteSpace(item.CallBackUrl)
|
||||||
? item.CallBackUrl
|
? item.CallBackUrl
|
||||||
: AppCommon.Config.Subsystem.蓝鲸智库.APIUrl + "/api/callback/platform/videosAnalyze";
|
: AppCommon.Config.Subsystem.蓝鲸智库.APIUrl + "/api/callback/platform/videosAnalyze";
|
||||||
responseMessage = await new HttpClient()
|
|
||||||
.PostAsJsonAsync(postUrl, postData);
|
var apiClent = _clientFactory.CreateClient();
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, postUrl);
|
||||||
|
request.Headers.Add("Area", item.Area); // 直接添加到本次请求头
|
||||||
|
request.Headers.Add("HostIP", item.HostIP); // 直接添加到本次请求头
|
||||||
|
request.Content = new StringContent(JsonSerializer.Serialize(postData), Encoding.UTF8, "application/json");
|
||||||
|
responseMessage = await apiClent.SendAsync(request);
|
||||||
|
|
||||||
if (responseMessage.IsSuccessStatusCode)
|
if (responseMessage.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var res = await responseMessage.Content.ReadAsStringAsync();
|
var res = await responseMessage.Content.ReadAsStringAsync();
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,6 @@ namespace VideoAnalysisCore.Job
|
||||||
Tag = item.NodeId.ToString(),
|
Tag = item.NodeId.ToString(),
|
||||||
TagId = s.VideoCode,
|
TagId = s.VideoCode,
|
||||||
MediaUrl = string.Empty,
|
MediaUrl = string.Empty,
|
||||||
MediaName = s.Name
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var maxId = data.Max(s => s.Id);
|
var maxId = data.Max(s => s.Id);
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ namespace VideoAnalysisCore.Job
|
||||||
// 查询 2 天前任务执行完成的记录
|
// 查询 2 天前任务执行完成的记录
|
||||||
var completedTasks = videotaskDB.AsQueryable()
|
var completedTasks = videotaskDB.AsQueryable()
|
||||||
.Where(t =>
|
.Where(t =>
|
||||||
t.LastEnum == Model.Enum.RedisChannelEnum.EndTask
|
t.LastEnum == Model.Enum.RedisChannelEnum.结束任务
|
||||||
&& t.EndTime < twoDaysAgo
|
&& t.EndTime < twoDaysAgo
|
||||||
&& t.EndTime > endDaysAgo)
|
&& t.EndTime > endDaysAgo)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,22 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace VideoAnalysisCore.Model.Enum
|
namespace VideoAnalysisCore.Model.Enum
|
||||||
{
|
{
|
||||||
|
public enum VideoType
|
||||||
|
{
|
||||||
|
PPT课件 = 1,
|
||||||
|
摄像头 = 2
|
||||||
|
}
|
||||||
public enum AttachmentsInfoType
|
public enum AttachmentsInfoType
|
||||||
{
|
{
|
||||||
[Description("常规课程")]
|
新课 = 0,
|
||||||
None = 0,
|
[Description("复习/习题课")]
|
||||||
[Description("教研")]
|
复习 = 3,
|
||||||
TeachingResearch = 1,
|
|
||||||
[Description("PPT")]
|
|
||||||
PPT = 2,
|
|
||||||
[Description("复习")]
|
|
||||||
Review = 3,
|
|
||||||
[Description("活动")]
|
[Description("活动")]
|
||||||
Activities = 4,
|
活动 = 4,
|
||||||
[Description("班会")]
|
[Description("班会")]
|
||||||
Meeting = 5,
|
班会 = 5,
|
||||||
[Description("行为分析")]
|
[Description("其他资料")]
|
||||||
Behavior = 6,
|
其他资料 = 7
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,33 +5,37 @@
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum RedisChannelEnum
|
public enum RedisChannelEnum
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 等待中
|
||||||
|
/// </summary>
|
||||||
|
排队中 = 0,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下载文件
|
/// 下载文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
DownloadFile,
|
下载文件 = 5,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分离音频
|
/// 分离音频
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SeparateAudio,
|
分离音频 = 10,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解析字幕
|
/// 解析字幕
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ParsingCaptions,
|
解析字幕 = 20,
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 解析说话人
|
///// 解析说话人
|
||||||
/// </summary>
|
///// </summary>
|
||||||
ParsingSpeaker,
|
//解析说话人 = 30,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Chat模型分析
|
/// Chat模型分析
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ChatModelAnalysis,
|
AI模型分析 = 40,
|
||||||
///// <summary>
|
/// <summary>
|
||||||
///// 回调三方系统
|
/// 分析试题
|
||||||
///// </summary>
|
/// </summary>
|
||||||
//CallBackSystem,
|
AI分析试题 = 50,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 结束任务
|
/// 结束任务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
EndTask,
|
结束任务 = 60,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,18 +30,9 @@ namespace VideoAnalysisCore.Model
|
||||||
[SugarColumn(Length = 32)]
|
[SugarColumn(Length = 32)]
|
||||||
public string VideoCode { get; set; }
|
public string VideoCode { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 视频文件名称
|
/// 文件Id 需要返回给我的参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length = 32)]
|
public long AttachmentId { get; set; }
|
||||||
public string VideoName { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 录播结构目录节点编号
|
|
||||||
/// </summary>
|
|
||||||
public long NodeId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 录播内容编号
|
|
||||||
/// </summary>
|
|
||||||
public long StructurePageContentId { get; set; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 素材ID
|
/// 素材ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -49,7 +40,7 @@ namespace VideoAnalysisCore.Model
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内容类型
|
/// 内容类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AttachmentsInfoType AttachmentsInfoType { get; set; }
|
public AttachmentsInfoType CourseType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 科目类型
|
/// 科目类型
|
||||||
|
|
@ -80,8 +71,17 @@ namespace VideoAnalysisCore.Model
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通知回调地址
|
/// 通知回调地址
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
[SugarColumn(Length = 500)]
|
[SugarColumn(Length = 500)]
|
||||||
public string CallBackUrl { get; set; }
|
public string CallBackUrl { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 请求区域
|
||||||
|
/// <para>回调添加到Headers</para>
|
||||||
|
/// </summary>
|
||||||
|
public string Area { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 请求区域
|
||||||
|
/// <para>回调添加到Headers</para>
|
||||||
|
/// </summary>
|
||||||
|
public string HostIP { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,10 @@ namespace VideoAnalysisCore.Model
|
||||||
public long TopicId { get; set; }
|
public long TopicId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 图片地址
|
/// PPT切图地址
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(Length =100)]
|
[SugarColumn(Length =100)]
|
||||||
public string? ImageUrl { get; set; }
|
public string? PPTImageUrl { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 题干
|
/// 题干
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -53,6 +53,12 @@ namespace VideoAnalysisCore.Model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Question { get; set; }
|
public string? Question { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// 试题类型
|
||||||
|
/// <para>[填空题/判断题/选择题/解答题]</para>
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(Length = 10,IsNullable =true)]
|
||||||
|
public string? Type { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// 试题开始时间
|
/// 试题开始时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int StartTime { get; set; }
|
public int StartTime { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,6 @@ namespace VideoAnalysisCore.Model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string MediaUrl { get; set; } = string.Empty;
|
public string MediaUrl { get; set; } = string.Empty;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 媒体文件名称
|
|
||||||
/// </summary>
|
|
||||||
public string MediaName { get; set; } = string.Empty;
|
|
||||||
/// <summary>
|
|
||||||
/// 下载后本地媒体目录
|
/// 下载后本地媒体目录
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string LocalMediaPath { get; set; } = string.Empty;
|
public string LocalMediaPath { get; set; } = string.Empty;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue