优化 各个模块的注入方式

修复 初始化时 之前未执行完的任务 导致的死循环
This commit is contained in:
小肥羊 2025-09-29 17:32:08 +08:00
parent 94cde3af70
commit 0edf143f8a
18 changed files with 350 additions and 239 deletions

View File

@ -2,7 +2,9 @@
using Learn.VideoAnalysis.API.Expand; using Learn.VideoAnalysis.API.Expand;
using Mapster; using Mapster;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using VideoAnalysisCore.AICore.FFMPGE;
using VideoAnalysisCore.AICore.GPT.DeepSeek; using VideoAnalysisCore.AICore.GPT.DeepSeek;
using VideoAnalysisCore.AICore.SherpaOnnx;
using VideoAnalysisCore.Common; using VideoAnalysisCore.Common;
using VideoAnalysisCore.Common.Expand; using VideoAnalysisCore.Common.Expand;
@ -37,8 +39,12 @@ namespace Learn.VideoAnalysis.API
builder.Services.AddHttpClient(); builder.Services.AddHttpClient();
builder.Services.AddSqlSugarExpand(); builder.Services.AddSqlSugarExpand();
builder.Services.AddRedisExpand(); builder.Services.AddRedisExpand();
builder.Services.AddCoravel(); builder.Services.AddCoravel();
builder.Services.AddCorsExpand(); builder.Services.AddCorsExpand();
builder.Services.AddDownloadFileExpand();
builder.Services.AddFFMPGEExpand();
builder.Services.AddSenseVoiceExpand();
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
builder.Services.AddControllersWithViews(options => builder.Services.AddControllersWithViews(options =>
{ {

View File

@ -23,6 +23,8 @@ namespace Learn.VideoAnalysis.Components.Pages
[Inject] private ConfirmService ComfirmService { get; set; } = default!; [Inject] private ConfirmService ComfirmService { get; set; } = default!;
[Inject] private Repository<VideoTask> taskDB { get; set; } = default!; [Inject] private Repository<VideoTask> taskDB { get; set; } = default!;
[Inject] private NavigationManager NavigationManager { get; set; } = default!; [Inject] private NavigationManager NavigationManager { get; set; } = default!;
[Inject] private RedisManager redisManager { get; set; } = default!;
[Inject] private INotificationService _notice { get; set; } = default!; [Inject] private INotificationService _notice { get; set; } = default!;
@ -54,7 +56,7 @@ namespace Learn.VideoAnalysis.Components.Pages
async void ReStartClick(VideoTaskDto query) async void ReStartClick(VideoTaskDto query)
{ {
selectDefaultValue = selectDefaultValue =
(await RedisExpand.Redis.HMGetAsync<int>(RedisExpandKey.Task(query.Id), "LastEnum")).FirstOrDefault(); (await redisManager.Redis.HMGetAsync<int>(RedisExpandKey.Task(query.Id), "LastEnum")).FirstOrDefault();
selectEnum = selectDefaultValue; selectEnum = selectDefaultValue;
reStartTask = query; reStartTask = query;
modalShow = true; modalShow = true;
@ -69,9 +71,9 @@ namespace Learn.VideoAnalysis.Components.Pages
/// <param name="query"></param> /// <param name="query"></param>
async void ReStart() async void ReStart()
{ {
await RedisExpand.ClearTaskError(reStartTask.Id); await redisManager.ClearTaskError(reStartTask.Id);
_=Task.Run(() => _=Task.Run(() =>
RedisExpand.InsertChannel((RedisChannelEnum)selectEnum, reStartTask.Id) redisManager.InsertChannel((RedisChannelEnum)selectEnum, reStartTask.Id)
); );
modalShow = false; modalShow = false;
} }
@ -125,7 +127,7 @@ namespace Learn.VideoAnalysis.Components.Pages
var item = rowData.Data; var item = rowData.Data;
if (item is null) if (item is null)
return; return;
var data = RedisExpand.Redis.HMGet<string>(RedisExpandKey.Task(item.Id), var data = redisManager.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] == null ?default:data[1].ToEnum<RedisChannelEnum>() ?? default; item.LastEnum = data[1] == null ?default:data[1].ToEnum<RedisChannelEnum>() ?? default;

View File

@ -32,6 +32,7 @@ namespace Learn.VideoAnalysis.Components.Pages
[Inject] private Repository<VideoQuestion> videoQuestionDB { get; set; } = default!; [Inject] private Repository<VideoQuestion> videoQuestionDB { get; set; } = default!;
[Inject] private Repository<VideoQuestionKonw> videoQuestionKonwDB { get; set; } = default!; [Inject] private Repository<VideoQuestionKonw> videoQuestionKonwDB { get; set; } = default!;
[Inject] private Repository<VideoKonwPoint> videoKonwPointDB { get; set; } = default!; [Inject] private Repository<VideoKonwPoint> videoKonwPointDB { get; set; } = default!;
[Inject] private RedisManager redisManager { get; set; } = default!;
[Inject] private IJSRuntime JSRuntime { get; set; } = default!; [Inject] private IJSRuntime JSRuntime { get; set; } = default!;
private VideoTask nowTask { get; set; } = default!; private VideoTask nowTask { get; set; } = default!;
@ -76,7 +77,7 @@ namespace Learn.VideoAnalysis.Components.Pages
return; return;
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(nowTask.Captions); var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(nowTask.Captions);
var captionsArr1 = JsonSerializer.Deserialize<SenseVoiceRes[]>(nowTask.CaptionsAI??"[]") ; var captionsArr1 = JsonSerializer.Deserialize<SenseVoiceRes[]>(nowTask.CaptionsAI??"[]") ;
RedisExpand.Redis.HMGet<SenseVoiceRes[]>(RedisExpandKey.Task(taskId), "Captions").FirstOrDefault(); redisManager.Redis.HMGet<SenseVoiceRes[]>(RedisExpandKey.Task(taskId), "Captions").FirstOrDefault();
var konwArr = await videoKonwPointDB.AsQueryable() var konwArr = await videoKonwPointDB.AsQueryable()
.Where(s => s.VideoTaskId == nowTask.Id) .Where(s => s.VideoTaskId == nowTask.Id)

View File

@ -68,13 +68,17 @@ namespace Learn.VideoAnalysis
builder.Configuration.AddAppConfig(args); builder.Configuration.AddAppConfig(args);
builder.Services.AddSqlSugarExpand(); builder.Services.AddSqlSugarExpand();
builder.Services.AddRedisExpand();
builder.Services.AddSimpleTexOcrClient(); builder.Services.AddSimpleTexOcrClient();
builder.Services.AddDownloadFileExpand(); builder.Services.AddDownloadFileExpand();
builder.Services.AddFFMPGEExpand();
builder.Services.AddAlibabaCloudVod(); builder.Services.AddAlibabaCloudVod();
builder.Services.AddAliyunOSS(); builder.Services.AddAliyunOSS();
builder.Services.AddRedisExpand(); builder.Services.AddSenseVoiceExpand();
builder.Services.AddSpeakerAI(); //builder.Services.AddSpeakerAI();
builder.Services.AddCoravel(); builder.Services.AddCoravel();
//SenseVoice.Init(); //SenseVoice.Init();
//Òì³£¹ýÂËÆ÷ //Òì³£¹ýÂËÆ÷
builder.Services.AddControllersWithViews(options => builder.Services.AddControllersWithViews(options =>
@ -110,6 +114,7 @@ namespace Learn.VideoAnalysis
//builder.Services.AddSingleton<IBserGPT, Chat_GPT>(); //builder.Services.AddSingleton<IBserGPT, Chat_GPT>();
builder.Services.AddSingleton<IBserGPT, DeepSeek_GPT>(); builder.Services.AddSingleton<IBserGPT, DeepSeek_GPT>();
builder.Services.AddTaskSubscribe();
var app = builder.Build(); var app = builder.Build();
@ -119,6 +124,7 @@ namespace Learn.VideoAnalysis
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
_ = app.Services.GetRequiredService<RedisInit>();
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI();
app.UseExceptionHandler("/Error"); app.UseExceptionHandler("/Error");

View File

@ -14,9 +14,22 @@ using SixLabors.ImageSharp.Processing;
using System.Text.Json; using System.Text.Json;
using System; using System;
using Microsoft.VisualBasic.FileIO; using Microsoft.VisualBasic.FileIO;
using Microsoft.Extensions.DependencyInjection;
namespace VideoAnalysisCore.AICore.FFMPGE namespace VideoAnalysisCore.AICore.FFMPGE
{ {
public static class FFMPGEExpand
{
/// <summary>
/// 添加跨域拓展
/// </summary>
/// <param name="services"></param>
public static void AddFFMPGEExpand(this IServiceCollection services)
{
services.AddSingleton<FFMPGEHandle>();
}
}
/// <summary> /// <summary>
/// Ffmpeg处理程序 /// Ffmpeg处理程序
/// </summary> /// </summary>
@ -28,21 +41,27 @@ namespace VideoAnalysisCore.AICore.FFMPGE
public static string FFmpegPath = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) public static string FFmpegPath = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
? $"/usr/bin/ffmpeg" ? $"/usr/bin/ffmpeg"
: Path.Combine(AppCommon.AIModelFile, "ffmpeg.exe"); : Path.Combine(AppCommon.AIModelFile, "ffmpeg.exe");
private Repository<VideoTask> videoTaskDB { get; set; }
private RedisManager redisManager { get; set; }
public FFMPGEHandle(RedisManager redisManager, Repository<VideoTask> videoTaskDB)
{
this.redisManager = redisManager;
this.videoTaskDB = videoTaskDB;
}
/// <summary> /// <summary>
/// 识别视频关键帧 /// 识别视频关键帧
/// </summary> /// </summary>
/// <param name="task">任务id</param> /// <param name="task">任务id</param>
/// <returns></returns> /// <returns></returns>
public static async Task VideoKeyFrames(string task) public async Task VideoKeyFrames(string task)
{ {
var taskID = long.Parse(task); var taskID = long.Parse(task);
//间隔秒 //间隔秒
var intervalSec = 5; var intervalSec = 5;
var threshold = 8.15; var threshold = 8.15;
var ssimThreshold = 0.9; var ssimThreshold = 0.9;
var taskInfo = await DbScoped.Sugar var taskInfo = await videoTaskDB.AsQueryable()
.Queryable<VideoTask>()
.Where(s => s.Id == long.Parse(task)).FirstAsync(); .Where(s => s.Id == long.Parse(task)).FirstAsync();
if (string.IsNullOrEmpty(taskInfo.PPTVideoCode) || string.IsNullOrEmpty(taskInfo.PPTVideoUrl)) return; if (string.IsNullOrEmpty(taskInfo.PPTVideoCode) || string.IsNullOrEmpty(taskInfo.PPTVideoUrl)) return;
//视频切帧 //视频切帧
@ -55,11 +74,11 @@ namespace VideoAnalysisCore.AICore.FFMPGE
} }
var ffmpeg = new Engine(FFmpegPath); var ffmpeg = new Engine(FFmpegPath);
var cToken = new CancellationToken(); var cToken = new CancellationToken();
RedisExpand.SetTaskProgress(task, "Frame=>10%"); redisManager.SetTaskProgress(task, "Frame=>10%");
foreach (string jpgFile in Directory.GetFiles(localPath, "*.jpg")) foreach (string jpgFile in Directory.GetFiles(localPath, "*.jpg"))
FileSystem.DeleteFile(jpgFile, UIOption.OnlyErrorDialogs, RecycleOption.DeletePermanently); FileSystem.DeleteFile(jpgFile, UIOption.OnlyErrorDialogs, RecycleOption.DeletePermanently);
RedisExpand.SetTaskProgress(task, "Frame=>20%"); redisManager.SetTaskProgress(task, "Frame=>20%");
await ffmpeg.ExecuteAsync($"-i {filePath} -vf \"fps=1/{intervalSec},scale=960:540\" {localPath}/{ExpandFunction.FrameName}%03d.jpg", cToken); await ffmpeg.ExecuteAsync($"-i {filePath} -vf \"fps=1/{intervalSec},scale=960:540\" {localPath}/{ExpandFunction.FrameName}%03d.jpg", cToken);
@ -67,7 +86,7 @@ namespace VideoAnalysisCore.AICore.FFMPGE
var frameFiles = Directory.GetFiles(localPath, "*.jpg") var frameFiles = Directory.GetFiles(localPath, "*.jpg")
.OrderBy(f => f) .OrderBy(f => f)
.ToList(); .ToList();
RedisExpand.SetTaskProgress(task, "Frame=>80%"); redisManager.SetTaskProgress(task, "Frame=>80%");
Image<Rgb24> prevFrame = null; Image<Rgb24> prevFrame = null;
var keyFrames = new List<int>(10) { 5}; var keyFrames = new List<int>(10) { 5};
foreach (var frameFile in frameFiles) foreach (var frameFile in frameFiles)
@ -103,8 +122,7 @@ namespace VideoAnalysisCore.AICore.FFMPGE
} }
//写入数据库 //写入数据库
var keyFramStr = keyFrames.Where(s => s != -1).ToJson(); var keyFramStr = keyFrames.Where(s => s != -1).ToJson();
await DbScoped.Sugar await videoTaskDB.AsUpdateable()
.Updateable<VideoTask>()
.SetColumns(it => it.PPTKeyFrame == keyFramStr) .SetColumns(it => it.PPTKeyFrame == keyFramStr)
.Where(it => it.Id == taskID) .Where(it => it.Id == taskID)
.ExecuteCommandAsync(); .ExecuteCommandAsync();
@ -117,7 +135,7 @@ namespace VideoAnalysisCore.AICore.FFMPGE
/// <param name="img1"></param> /// <param name="img1"></param>
/// <param name="img2"></param> /// <param name="img2"></param>
/// <returns></returns> /// <returns></returns>
static double CalculateFrameDifference(Image<Rgb24> img1, Image<Rgb24> img2) double CalculateFrameDifference(Image<Rgb24> img1, Image<Rgb24> img2)
{ {
// 统一调整为64x64 // 统一调整为64x64
var resized1 = img1.Clone(x => x.Grayscale()); var resized1 = img1.Clone(x => x.Grayscale());
@ -136,7 +154,7 @@ namespace VideoAnalysisCore.AICore.FFMPGE
return diff / (double)(resized1.Width * resized1.Height); return diff / (double)(resized1.Width * resized1.Height);
} }
static double GetTimestampFromFileName(string filePath) double GetTimestampFromFileName(string filePath)
{ {
string fileName = Path.GetFileNameWithoutExtension(filePath); string fileName = Path.GetFileNameWithoutExtension(filePath);
return double.Parse(fileName.Split('_')[1]); return double.Parse(fileName.Split('_')[1]);
@ -146,7 +164,7 @@ namespace VideoAnalysisCore.AICore.FFMPGE
/// </summary> /// </summary>
/// <param name="task"></param> /// <param name="task"></param>
/// <returns></returns> /// <returns></returns>
public static async Task RunAsync(string task) public async Task RunAsync(string task)
{ {
await VideoKeyFrames(task); await VideoKeyFrames(task);
await Audio2WAV16KAsync(task); await Audio2WAV16KAsync(task);
@ -156,10 +174,9 @@ namespace VideoAnalysisCore.AICore.FFMPGE
/// </summary> /// </summary>
/// <param name="task">任务id</param> /// <param name="task">任务id</param>
/// <returns></returns> /// <returns></returns>
public static async Task Audio2WAV16KAsync(string task) public async Task Audio2WAV16KAsync(string task)
{ {
var filePath = await DbScoped.Sugar var filePath = await videoTaskDB.AsQueryable()
.Queryable<VideoTask>()
.Where(s => s.Id == long.Parse(task)) .Where(s => s.Id == long.Parse(task))
.Select(s=>s.LocalMediaPath).FirstAsync(); .Select(s=>s.LocalMediaPath).FirstAsync();
if (string.IsNullOrEmpty(filePath)) if (string.IsNullOrEmpty(filePath))

View File

@ -23,17 +23,19 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
private readonly Repository<CourseGradingCriteria> criteriaDB; private readonly Repository<CourseGradingCriteria> criteriaDB;
private readonly Repository<VideoTask> videoTaskDB; private readonly Repository<VideoTask> videoTaskDB;
private readonly Repository<KnowledgeInfo> knowledgeInfoDB; private readonly Repository<KnowledgeInfo> knowledgeInfoDB;
private readonly RedisManager redisManager;
/// <summary> /// <summary>
/// 初始化 /// 初始化
/// </summary> /// </summary>
/// <param name="moonshotClient"></param> /// <param name="moonshotClient"></param>
/// <param name="logger"></param> /// <param name="logger"></param>
public Chat_GPT(ChatGPTClient moonshotClient, Repository<CourseGradingCriteria> criteria, Repository<VideoTask> videoTaskDB, Repository<KnowledgeInfo> knowledgeInfoDB) public Chat_GPT(ChatGPTClient moonshotClient, Repository<CourseGradingCriteria> criteria, Repository<VideoTask> videoTaskDB, Repository<KnowledgeInfo> knowledgeInfoDB, RedisManager redisManager)
{ {
this.chatClient = moonshotClient; this.chatClient = moonshotClient;
criteriaDB = criteria; criteriaDB = criteria;
this.videoTaskDB = videoTaskDB; this.videoTaskDB = videoTaskDB;
this.knowledgeInfoDB = knowledgeInfoDB; this.knowledgeInfoDB = knowledgeInfoDB;
this.redisManager = redisManager;
} }
private static List<VideoKnowRes> MergeRes(VideoKnowRes[] timeBases) private static List<VideoKnowRes> MergeRes(VideoKnowRes[] timeBases)
@ -162,7 +164,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
item.EndTime = (int)(questionRes[i + 1]?.StartTime ?? 0) - 1; item.EndTime = (int)(questionRes[i + 1]?.StartTime ?? 0) - 1;
} }
await RedisExpand.Redis await redisManager.Redis
.HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes); .HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes);
//var postMessages1 = //var postMessages1 =
@ -181,7 +183,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
////questionRes1 = MergeRes(questionRes1).ToArray(); ////questionRes1 = MergeRes(questionRes1).ToArray();
var gptRes = new TaskRes(captions); var gptRes = new TaskRes(captions);
await RedisExpand.Redis await redisManager.Redis
.HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes); .HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes);
return gptRes; return gptRes;
} }
@ -203,13 +205,13 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
}; };
var time = DateTime.Now.ToString("MMddHHmmss"); var time = DateTime.Now.ToString("MMddHHmmss");
RedisExpand.SetTaskGPTCached(task, time,chatRep); redisManager.SetTaskGPTCached(task, time,chatRep);
var chatResp = await chatClient.Chat(chatRep); var chatResp = await chatClient.Chat(chatRep);
var chatResContent = chatResp?.res; var chatResContent = chatResp?.res;
if (string.IsNullOrEmpty(chatResContent)) if (string.IsNullOrEmpty(chatResContent))
throw new Exception("GPT返回message无效结果"); throw new Exception("GPT返回message无效结果");
if (chatResp != null) if (chatResp != null)
RedisExpand.SetTaskGPTCached(task, time, new object[] { chatResp.Value.res, chatResp.Value.u }); redisManager.SetTaskGPTCached(task, time, new object[] { chatResp.Value.res, chatResp.Value.u });
chatResContent = chatResContent?.Replace("字幕内容", "课堂情况"); chatResContent = chatResContent?.Replace("字幕内容", "课堂情况");
chatResContent = chatResContent?.Replace("\n", ""); chatResContent = chatResContent?.Replace("\n", "");
@ -307,13 +309,13 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
}; };
var time = DateTime.Now.ToString("MMddHHmmss"); var time = DateTime.Now.ToString("MMddHHmmss");
RedisExpand.SetTaskGPTCached(task, time, chatRep); redisManager.SetTaskGPTCached(task, time, chatRep);
var chatResp = await chatClient.Chat(chatRep); var chatResp = await chatClient.Chat(chatRep);
var chatResContent = chatResp?.res; var chatResContent = chatResp?.res;
if (string.IsNullOrEmpty(chatResContent)) if (string.IsNullOrEmpty(chatResContent))
throw new Exception("GPT返回message无效结果"); throw new Exception("GPT返回message无效结果");
if (chatResp != null) if (chatResp != null)
RedisExpand.SetTaskGPTCached(task, time, new object[] { chatResp.Value.res, chatResp.Value.u }); redisManager.SetTaskGPTCached(task, time, new object[] { chatResp.Value.res, chatResp.Value.u });
chatResContent = chatResContent?.Replace("字幕内容", "课堂情况"); chatResContent = chatResContent?.Replace("字幕内容", "课堂情况");
chatResContent = chatResContent?.Replace("\n", ""); chatResContent = chatResContent?.Replace("\n", "");
@ -413,7 +415,7 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
.ExecuteCommandAsync(); .ExecuteCommandAsync();
} }
await RedisExpand.Redis await redisManager.Redis
.HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes); .HMSetAsync(RedisExpandKey.Task(task), "ChatAnalysis", gptRes);
return gptRes; return gptRes;

View File

@ -33,10 +33,13 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
//private readonly string Path = "v1/chat/completions"; //private readonly string Path = "v1/chat/completions";
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly RedisManager redisManager;
public DeepSeekGPTClient(IHttpClientFactory httpClientFactory)
public DeepSeekGPTClient(IHttpClientFactory httpClientFactory, RedisManager redisManager)
{ {
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
this.redisManager = redisManager;
} }
@ -192,7 +195,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
messageBuilder1.Append(strReasoning); messageBuilder1.Append(strReasoning);
var steamCount = messageBuilder.Length + messageBuilder1.Length; var steamCount = messageBuilder.Length + messageBuilder1.Length;
if (++threshold%30==0) if (++threshold%30==0)
RedisExpand.SetTaskProgress(chatReq.taskId, "steam=>"+ steamCount); redisManager.SetTaskProgress(chatReq.taskId, "steam=>"+ steamCount);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -14,7 +14,6 @@ using VideoAnalysisCore.Model.Enum;
using Mapster; using Mapster;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using static System.Collections.Specialized.BitVector32;
using FFmpeg.NET.Services; using FFmpeg.NET.Services;
using Aliyun.OSS; using Aliyun.OSS;
using Yitter.IdGenerator; using Yitter.IdGenerator;
@ -31,6 +30,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
{ {
private readonly DeepSeekGPTClient chatClient; private readonly DeepSeekGPTClient chatClient;
private readonly Repository<CourseGradingCriteria> criteriaDB; private readonly Repository<CourseGradingCriteria> criteriaDB;
private readonly RedisManager redisManager;
private readonly Repository<VideoTask> videoTaskDB; private readonly Repository<VideoTask> videoTaskDB;
private readonly Repository<VideoKonwPoint> videoKonwPointDB; private readonly Repository<VideoKonwPoint> videoKonwPointDB;
private readonly Repository<VideoQuestion> videoQuestionDB; private readonly Repository<VideoQuestion> videoQuestionDB;
@ -43,7 +43,9 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
/// </summary> /// </summary>
/// <param name="moonshotClient"></param> /// <param name="moonshotClient"></param>
/// <param name="logger"></param> /// <param name="logger"></param>
public DeepSeek_GPT(DeepSeekGPTClient moonshotClient, Repository<CourseGradingCriteria> criteria, Repository<VideoTask> videoTaskDB, Repository<KnowledgeInfo> knowledgeInfoDB, Repository<VideoKonwPoint> videoKonwPointDB, SimpLetexClient simpLetexClient, Repository<VideoQuestion> videoQuestionDB, OssClient ossClient, Repository<VideoQuestionKonw> videoQuestionKonwDB) public DeepSeek_GPT(DeepSeekGPTClient moonshotClient, Repository<CourseGradingCriteria> criteria, Repository<VideoTask> videoTaskDB,
Repository<KnowledgeInfo> knowledgeInfoDB, Repository<VideoKonwPoint> videoKonwPointDB, SimpLetexClient simpLetexClient,
Repository<VideoQuestion> videoQuestionDB, OssClient ossClient, Repository<VideoQuestionKonw> videoQuestionKonwDB, RedisManager redisManager)
{ {
chatClient = moonshotClient; chatClient = moonshotClient;
criteriaDB = criteria; criteriaDB = criteria;
@ -54,6 +56,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
this.videoQuestionDB = videoQuestionDB; this.videoQuestionDB = videoQuestionDB;
this.ossClient = ossClient; this.ossClient = ossClient;
this.videoQuestionKonwDB = videoQuestionKonwDB; this.videoQuestionKonwDB = videoQuestionKonwDB;
this.redisManager = redisManager;
} }
/// <summary> /// <summary>
/// 获取内容对应的章节 /// 获取内容对应的章节
@ -151,7 +154,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
.SetColumns(it => it.Sections == fileNameInfoRes.) .SetColumns(it => it.Sections == fileNameInfoRes.)
.Where(it => it.Id == taskInfo.Id) .Where(it => it.Id == taskInfo.Id)
.ExecuteCommandAsync(); .ExecuteCommandAsync();
await RedisExpand.Redis await redisManager.Redis
.HMSetAsync(RedisExpandKey.Task(task), "学科章节", fileNameInfoRes.); .HMSetAsync(RedisExpandKey.Task(task), "学科章节", fileNameInfoRes.);
return fileNameInfoRes?.; return fileNameInfoRes?.;
@ -354,7 +357,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
{ {
var time = title + DateTime.Now.ToString("MMddHHmmss"); var time = title + DateTime.Now.ToString("MMddHHmmss");
var redisCached = new object[2] { chatRep, null }; var redisCached = new object[2] { chatRep, null };
RedisExpand.SetTaskGPTCached(task, time, chatRep); redisManager.SetTaskGPTCached(task, time, chatRep);
var chatResp = await chatClient.Chat(chatRep); var chatResp = await chatClient.Chat(chatRep);
var chatResContent = chatResp?.res; var chatResContent = chatResp?.res;
if (string.IsNullOrEmpty(chatResContent)) if (string.IsNullOrEmpty(chatResContent))
@ -362,7 +365,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
if (chatResp != null) if (chatResp != null)
{ {
redisCached[1] = new object[] { chatResp.Value.res, chatResp.Value.u, chatResp.Value.reasoning }; redisCached[1] = new object[] { chatResp.Value.res, chatResp.Value.u, chatResp.Value.reasoning };
RedisExpand.SetTaskGPTCached(task, time, redisCached); redisManager.SetTaskGPTCached(task, time, redisCached);
} }
chatResContent = chatResContent?.ExtractJsonStrings()?.FirstOrDefault(); chatResContent = chatResContent?.ExtractJsonStrings()?.FirstOrDefault();
chatResContent = chatResContent?.Replace("\n", ""); chatResContent = chatResContent?.Replace("\n", "");
@ -604,7 +607,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
{ {
throw new Exception("重试次数过多!"); throw new Exception("重试次数过多!");
} }
await RedisExpand.Redis await redisManager.Redis
.HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes); .HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes);
return null; return null;

View File

@ -1,4 +1,5 @@
using Microsoft.Extensions.Options; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using SherpaOnnx; using SherpaOnnx;
using SqlSugar.IOC; using SqlSugar.IOC;
using System; using System;
@ -17,18 +18,40 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
namespace VideoAnalysisCore.AICore.SherpaOnnx namespace VideoAnalysisCore.AICore.SherpaOnnx
{ {
public static class SenseVoice public static class SenseVoiceExpand
{
/// <summary>
/// 添加 SenseVoice 语音转文字
/// </summary>
/// <param name="services"></param>
public static void AddSenseVoiceExpand(this IServiceCollection services)
{
services.AddSingleton<SenseVoice>();
}
}
public class SenseVoice
{ {
const string TransducerStr = "sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20"; const string TransducerStr = "sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20";
static OfflineRecognizer OR = default!; static OfflineRecognizer OR = default!;
//static VoiceActivityDetector VAD = default!; //static VoiceActivityDetector VAD = default!;
static VadModelConfig VADModelConfig = default!; static VadModelConfig VADModelConfig = default!;
public Repository<VideoTask> videoTaskDB { get; set; }
private readonly RedisManager redisManager;
public SenseVoice(Repository<VideoTask> videoTaskDB, RedisManager redisManager)
{
this.videoTaskDB = videoTaskDB;
this.redisManager = redisManager;
}
/// <summary> /// <summary>
/// 初始化 SenseVoice /// 初始化 SenseVoice
/// </summary> /// </summary>
/// <param name="numThreads">默认6线程</param> /// <param name="numThreads">默认6线程</param>
/// <param name="useGPU">是否使用gpu 报错请看安装CUDA环境<see cref="https://k2-fsa.github.io/sherpa/onnx/pretrained_models/whisper/large-v3.html#run-with-gpu-float32"/></param> /// <param name="useGPU">是否使用gpu 报错请看安装CUDA环境<see cref="https://k2-fsa.github.io/sherpa/onnx/pretrained_models/whisper/large-v3.html#run-with-gpu-float32"/></param>
public static void Init(int numThreads = 6, bool useGPU = false, bool useHotwords = false) public void Init(int numThreads = 6, bool useGPU = false, bool useHotwords = false)
{ {
Console.WriteLine("初始化 SenseVoice"); Console.WriteLine("初始化 SenseVoice");
OfflineRecognizerConfig config = new OfflineRecognizerConfig(); OfflineRecognizerConfig config = new OfflineRecognizerConfig();
@ -102,24 +125,19 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
/// </summary> /// </summary>
/// <param name="s"></param> /// <param name="s"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<List<SenseVoiceRes>> RunTask(Stream s) public async Task<List<SenseVoiceRes>> RunTask(Stream s)
{ {
if (s is null) if (s is null)
throw new Exception("音频路径 is null"); throw new Exception("音频路径 is null");
return await TaskHandle(new WaveReader(s)); return await TaskHandle(new WaveReader(s), null);
} }
/// <summary> /// <summary>
/// 获取语音字幕 /// 获取语音字幕
/// </summary> /// </summary>
/// <param name="task"></param> /// <param name="task"></param>
/// <returns></returns> /// <returns></returns>
public static async Task RunTask(string task) public async Task RunTask(string task)
{ {
var filePath = Path.Combine(task.LocalPath(), "task.wav"); var filePath = Path.Combine(task.LocalPath(), "task.wav");
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
@ -134,7 +152,7 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
/// <param name="task">任务id [默认Null]</param> /// <param name="task">任务id [默认Null]</param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="Exception"></exception> /// <exception cref="Exception"></exception>
public static async Task<List<SenseVoiceRes>> TaskHandle(WaveReader reader, string? task = null) public async Task<List<SenseVoiceRes>> TaskHandle(WaveReader reader, string? task )
{ {
if (OR is null) if (OR is null)
Init(); Init();
@ -162,25 +180,30 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
{ {
//获取最新的发言片段 //获取最新的发言片段
while (!VAD.IsEmpty()) while (!VAD.IsEmpty())
await VAD.ReadNext(res, totalSecond, task); {
var p = await ReadNext(VAD,res, totalSecond);
if (p != null) redisManager.SetTaskProgress(task, p + "%");
}
} }
} }
VAD.Flush(); VAD.Flush();
while (!VAD.IsEmpty()) while (!VAD.IsEmpty())
await VAD.ReadNext(res, totalSecond, task); {
var p = await ReadNext(VAD, res, totalSecond);
if(p!= null) redisManager.SetTaskProgress(task, p + "%");
}
//如果携带任务ID //如果携带任务ID
if (!string.IsNullOrEmpty(task)) if (!string.IsNullOrEmpty(task))
{ {
Console.WriteLine(DateTime.Now + "=> SenseVoice 字幕数量" + res.Count); Console.WriteLine(DateTime.Now + "=> SenseVoice 字幕数量" + res.Count);
var captionsStr = res.ToJson(); var captionsStr = res.ToJson();
await DbScoped.Sugar await videoTaskDB.AsUpdateable()
.Updateable<VideoTask>()
.SetColumns(it => it.Captions == captionsStr) .SetColumns(it => it.Captions == captionsStr)
.Where(it => it.Id == long.Parse(task)) .Where(it => it.Id == long.Parse(task))
.ExecuteCommandAsync(); .ExecuteCommandAsync();
await RedisExpand.Redis.HMSetAsync(RedisExpandKey.Task(task), "Captions", res); await redisManager.Redis.HMSetAsync(RedisExpandKey.Task(task), "Captions", res);
//分析完成视频字幕后继续接收任务 //分析完成视频字幕后继续接收任务
RedisExpand.NewTask(); redisManager.NewTask();
} }
return res; return res;
} }
@ -190,9 +213,9 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
/// <param name="VAD"></param> /// <param name="VAD"></param>
/// <param name="res">字幕处理后写入数组</param> /// <param name="res">字幕处理后写入数组</param>
/// <param name="totalSecond">总时长</param> /// <param name="totalSecond">总时长</param>
/// <param name="task">所属任务id</param> /// <param name="progressCallback">任务回调</param>
/// <returns></returns> /// <returns></returns>
public static async Task ReadNext(this VoiceActivityDetector VAD, List<SenseVoiceRes> res, float totalSecond, string? task = null) public async Task<double?> ReadNext(VoiceActivityDetector VAD, List<SenseVoiceRes> res, float totalSecond)
{ {
var segment = VAD.Front(); var segment = VAD.Front();
var sampleRate = VADModelConfig.SampleRate; var sampleRate = VADModelConfig.SampleRate;
@ -202,13 +225,14 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
using var stream = OR.CreateStream(); using var stream = OR.CreateStream();
stream.AcceptWaveform(sampleRate, segment.Samples); stream.AcceptWaveform(sampleRate, segment.Samples);
OR.Decode(stream); OR.Decode(stream);
double? resP =null;
if (!string.IsNullOrEmpty(stream.Result.Text)) if (!string.IsNullOrEmpty(stream.Result.Text))
{ {
var text = stream.Result.Text.Trim(); var text = stream.Result.Text.Trim();
if (text.Length == 1 && text == "。")// 检查字符是否只有一个句号 if (text.Length == 1 && text == "。")// 检查字符是否只有一个句号
{ {
VAD.Pop(); VAD.Pop();
return; return resP;
} }
res.Add(new() res.Add(new()
{ {
@ -216,10 +240,10 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
Start = (float)Math.Round(startTime, 2, MidpointRounding.AwayFromZero), Start = (float)Math.Round(startTime, 2, MidpointRounding.AwayFromZero),
End = (float)Math.Round(startTime + duration, 2, MidpointRounding.AwayFromZero), End = (float)Math.Round(startTime + duration, 2, MidpointRounding.AwayFromZero),
}); });
if (!string.IsNullOrEmpty(task)) resP = Math.Round((double)(startTime + duration) / (totalSecond) * 100, 2);
RedisExpand.SetTaskProgress(task, Math.Round((double)(startTime + duration) / (totalSecond) * 100,2)+"%");
} }
VAD.Pop(); VAD.Pop();
return resP;
} }
} }
} }

View File

@ -10,6 +10,7 @@ using System.Text.Json;
using VideoAnalysisCore.Model.Enum; using VideoAnalysisCore.Model.Enum;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using UserCenter.Model.Enum; using UserCenter.Model.Enum;
using VideoAnalysisCore.Model.Dto;
namespace VideoAnalysisCore.AICore.SherpaOnnx namespace VideoAnalysisCore.AICore.SherpaOnnx
{ {
@ -73,17 +74,16 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
if(i%20 !=0) if(i%20 !=0)
return 1; return 1;
var progress = (float)numProcessedChunks / numTotalChunks * 100; var progress = (float)numProcessedChunks / numTotalChunks * 100;
RedisExpand.SetTaskProgress(task, progress); //RedisExpand.SetTaskProgress(task, progress);
return 1; return 1;
}, nint.Zero); }, nint.Zero);
var res = segments.Select(s => new OfflineSpeakerRes(s)); var res = segments.Select(s => new OfflineSpeakerRes(s));
await RedisExpand.Redis.HSetAsync(RedisExpandKey.Task(task), "Speaker", res); //await RedisExpand.Redis.HSetAsync(RedisExpandKey.Task(task), "Speaker", res);
var speakerStr = res.ToJson(); var speakerStr = res.ToJson();
await DbScoped.Sugar //await videoTaskDB.Updateable()
.Updateable<VideoTask>() // .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();
} }
} }

View File

@ -45,7 +45,7 @@ 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);
} }
/// <summary> /// <summary>
/// 检测语言的方法 /// 检测语言的方法

View File

@ -93,14 +93,16 @@ namespace VideoAnalysisCore.Common
private readonly Repository<VideoTask> videoTaskDB; private readonly Repository<VideoTask> videoTaskDB;
private readonly Repository<NodePackageInfo> packageInfoTaskDB; private readonly Repository<NodePackageInfo> packageInfoTaskDB;
private readonly Client vodClient; private readonly Client vodClient;
private readonly RedisManager redisManager;
readonly string taskVideoName = "task.mp4"; readonly string taskVideoName = "task.mp4";
readonly string taskPPTVideoName = "ppt.mp4"; readonly string taskPPTVideoName = "ppt.mp4";
public DownloadFile(Repository<VideoTask> videoTaskDB, Client vodClient, Repository<NodePackageInfo> nackageInfoTaskDB) public DownloadFile(Repository<VideoTask> videoTaskDB, Client vodClient, Repository<NodePackageInfo> nackageInfoTaskDB, RedisManager redisManager)
{ {
this.videoTaskDB = videoTaskDB; this.videoTaskDB = videoTaskDB;
this.vodClient = vodClient; this.vodClient = vodClient;
this.packageInfoTaskDB = nackageInfoTaskDB; this.packageInfoTaskDB = nackageInfoTaskDB;
this.redisManager = redisManager;
} }
// 根据 Content-Type 映射文件后缀 // 根据 Content-Type 映射文件后缀
@ -187,7 +189,7 @@ namespace VideoAnalysisCore.Common
if (!string.IsNullOrEmpty(taskInfo.PPTVideoUrl)) if (!string.IsNullOrEmpty(taskInfo.PPTVideoUrl))
{ {
await Download(taskInfo.PPTVideoUrl, localPath, taskPPTVideoName, await Download(taskInfo.PPTVideoUrl, localPath, taskPPTVideoName,
(s, e) => RedisExpand.SetTaskProgress(task, "PPT->" + Math.Round(e.ProgressPercentage, 1) (s, e) => redisManager.SetTaskProgress(task, "PPT->" + Math.Round(e.ProgressPercentage, 1)
)); ));
//try //try
//{ //{
@ -217,7 +219,7 @@ namespace VideoAnalysisCore.Common
{ {
//下载原视频 //下载原视频
await Download(fileUrl, localPath, taskVideoName, await Download(fileUrl, localPath, taskVideoName,
(s, e) => RedisExpand.SetTaskProgress(task, Math.Round(e.ProgressPercentage,1) (s, e) => redisManager.SetTaskProgress(task, Math.Round(e.ProgressPercentage,1)
)); ));
} }
catch catch

View File

@ -44,7 +44,7 @@ namespace VideoAnalysisCore.Common.Expand
DbType = s.SqlType, DbType = s.SqlType,
IsAutoCloseConnection = true IsAutoCloseConnection = true
})); }));
services.AddScoped(typeof(Repository<>)); services.AddTransient(typeof(Repository<>));
//注入SqlSugar 主库 //注入SqlSugar 主库
services.AddSqlSugar(dbList); services.AddSqlSugar(dbList);

View File

@ -1,6 +1,7 @@
using FreeRedis; using FreeRedis;
using FreeRedis.Internal; using FreeRedis.Internal;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using NetTaste; using NetTaste;
using Newtonsoft.Json.Schema; using Newtonsoft.Json.Schema;
using SqlSugar.IOC; using SqlSugar.IOC;
@ -22,7 +23,6 @@ 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
{ {
@ -31,6 +31,7 @@ namespace VideoAnalysisCore.Common
/// </summary> /// </summary>
public static class RedisExpandKey public static class RedisExpandKey
{ {
/// <summary> /// <summary>
/// 基础key /// 基础key
/// </summary> /// </summary>
@ -72,45 +73,112 @@ namespace VideoAnalysisCore.Common
public static string Task(object taskId) => BaseKey + "Task:" + taskId; public static string Task(object taskId) => BaseKey + "Task:" + taskId;
public static string IDTask => BaseKey + "Services:" + AppCommon.Config.ID; public static string IDTask => BaseKey + "Services:" + AppCommon.Config.ID;
public static string TaskGPT(object taskId) => Task(taskId) + ":GPTCached"; public static string TaskGPT(object taskId) => Task(taskId) + ":GPTCached";
/// <summary>
/// 初始化 redis
/// <para>需要在初始化配置文件时候调用</para>
/// </summary>
public static void AddTaskSubscribe(this IServiceCollection service)
{
Console.WriteLine($"{DateTime.Now}=>初始化 Redis任务队列");
service.AddSingleton<RedisInit>();
}
public static void AddRedisExpand(this IServiceCollection service)
{
Console.WriteLine($"{DateTime.Now}=>初始化 Redis");
var redis = new RedisClient(AppCommon.Config.Redis.ConnectionString);
redis.Serialize = obj => JsonSerializer.Serialize(obj);
redis.Deserialize = (json, type) => JsonSerializer.Deserialize(json, type);
service.AddSingleton(redis);
service.AddSingleton<RedisManager>();
}
}
public class RedisInit
{
public FFMPGEHandle FFMPGE { get; set; }
public SenseVoice senseVoice { get; set; }
public RedisManager redisManager { get; set; }
public RedisInit(FFMPGEHandle fFMPGE, SenseVoice senseVoice, RedisManager redisManager)
{
FFMPGE = fFMPGE;
this.senseVoice = senseVoice;
this.redisManager = redisManager;
Init();
redisManager.InitChannel();
}
public void Init()
{
var SubscribeList = RedisManager.SubscribeList;
SubscribeList.Add(RedisChannelEnum., (task) =>
{
using var scope = AppCommon.Services?.CreateScope();
if (scope is null || scope.ServiceProvider.GetService<DownloadFile>() is null)
throw new Exception("DownloadFile 未注入");
else
return scope.ServiceProvider.GetService<DownloadFile>()?.RunTask(task) ?? Task.CompletedTask;
});
SubscribeList.Add(RedisChannelEnum., FFMPGE.RunAsync);
SubscribeList.Add(RedisChannelEnum., senseVoice.RunTask);
//SubscribeList.Add(RedisChannelEnum.解析说话人,Speaker.Run);
SubscribeList.Add(RedisChannelEnum.AI课程类型,
(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>()?.GetVideoType(task) ?? Task.CompletedTask;
});
SubscribeList.Add(RedisChannelEnum.AI模型分析, (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>()?.GetKnow(task) ?? Task.CompletedTask;
});
SubscribeList.Add(RedisChannelEnum.AI分析试题, (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., redisManager.TaskEnd);
}
} }
/// <summary> /// <summary>
/// redis拓展 /// redis拓展
/// </summary> /// </summary>
public static class RedisExpand public class RedisManager
{ {
public static bool StopTask { get; set; } = false;
/// <summary>
/// redis 连接
/// </summary>
public static RedisClient Redis = new RedisClient(AppCommon.Config.Redis.ConnectionString);
public static Dictionary<RedisChannelEnum, Func<string,Task>> SubscribeList = new Dictionary<RedisChannelEnum, Func<string, Task>>(); public static Dictionary<RedisChannelEnum, Func<string,Task>> SubscribeList = new Dictionary<RedisChannelEnum, Func<string, Task>>();
/// <summary> /// <summary>
/// 队列池 /// 队列池
/// </summary> /// </summary>
static SubscribeListObject? Subscribe; static SubscribeListObject? Subscribe;
public RedisClient Redis { get; set; }
public Repository<VideoTask> videoTaskDB { get; set; }
/// <summary> public RedisManager(RedisClient redis, Repository<VideoTask> videoTaskDB)
/// 初始化 redis
/// <para>需要在初始化配置文件时候调用</para>
/// </summary>
public static void AddRedisExpand(this IServiceCollection service)
{ {
Console.WriteLine($"{DateTime.Now}=>初始化 Redis"); Redis = redis;
Redis.Serialize = obj => JsonSerializer.Serialize(obj); this.videoTaskDB = videoTaskDB;
Redis.Deserialize = (json, type) => JsonSerializer.Deserialize(json, type);
Task.Run(() =>
{
Thread.Sleep(1000 * 10);
InitChannel();
});
} }
/// <summary> /// <summary>
/// 缓存GPT任务缓存 /// 缓存GPT任务缓存
/// </summary> /// </summary>
/// <param name="taskId"></param> /// <param name="taskId"></param>
public static void SetTaskGPTCached(object taskId,string time, object? data) public void SetTaskGPTCached(object taskId,string time, object? data)
{ {
Redis.Set(RedisExpandKey.TaskGPT(taskId) + ":" + time, data, 3600 * 24); Redis.Set(RedisExpandKey.TaskGPT(taskId) + ":" + time, data, 3600 * 24);
} }
@ -118,7 +186,7 @@ namespace VideoAnalysisCore.Common
/// 加入到消费队列 /// 加入到消费队列
/// </summary> /// </summary>
/// <param name="taskIds"></param> /// <param name="taskIds"></param>
public static void JoinQueue(params long[] taskIds) public void JoinQueue(params long[] taskIds)
{ //事务 { //事务
if (taskIds is null || taskIds.Length == 0) if (taskIds is null || taskIds.Length == 0)
return; return;
@ -133,7 +201,7 @@ namespace VideoAnalysisCore.Common
/// 获取任务进度 /// 获取任务进度
/// </summary> /// </summary>
/// <param name="taskId"></param> /// <param name="taskId"></param>
public static float GetTaskProgress(object taskId) public float GetTaskProgress(object taskId)
{ {
return Redis.HMGet<float>(RedisExpandKey.Task(taskId), "Progress")[0]; return Redis.HMGet<float>(RedisExpandKey.Task(taskId), "Progress")[0];
} }
@ -142,7 +210,7 @@ namespace VideoAnalysisCore.Common
/// </summary> /// </summary>
/// <param name="p">进度百分比</param> /// <param name="p">进度百分比</param>
/// <param name="taskId"></param> /// <param name="taskId"></param>
public static void SetTaskProgress(object taskId, object p) public void SetTaskProgress(object taskId, object p)
{ {
Redis.HMSet(RedisExpandKey.Task(taskId), "Progress", p.ToString()); Redis.HMSet(RedisExpandKey.Task(taskId), "Progress", p.ToString());
@ -152,7 +220,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 async Task InsertChannel(RedisChannelEnum @enum, object taskId) public 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未初始化");
@ -188,7 +256,7 @@ namespace VideoAnalysisCore.Common
} }
} }
public static async Task TaskEnd(string task) public async Task TaskEnd(string task)
{ {
var tId = long.Parse(task); var tId = long.Parse(task);
//var gptRes = (await Redis //var gptRes = (await Redis
@ -197,8 +265,8 @@ namespace VideoAnalysisCore.Common
// throw new Exception("未能读取到GPT处理结果"); // throw new Exception("未能读取到GPT处理结果");
//删除任务执行状态 //删除任务执行状态
await Redis.LRemAsync(RedisExpandKey.IDTask,1,task); await Redis.LRemAsync(RedisExpandKey.IDTask,1,task);
var taskData = await DbScoped.Sugar.Queryable<VideoTask>() var taskData = await videoTaskDB
.FirstAsync(s => s.Id == tId); .GetFirstAsync(s => s.Id == tId);
if (taskData.Captions == "[]") if (taskData.Captions == "[]")
taskData.Captions = (await Redis.HMGetAsync(RedisExpandKey.Task(task), "Captions")).First(); taskData.Captions = (await Redis.HMGetAsync(RedisExpandKey.Task(task), "Captions")).First();
//if (taskData.Speaker == "[]") //if (taskData.Speaker == "[]")
@ -211,7 +279,7 @@ namespace VideoAnalysisCore.Common
taskData.ErrorMessage = string.Empty; taskData.ErrorMessage = string.Empty;
taskData.LastEnum = RedisChannelEnum.; taskData.LastEnum = RedisChannelEnum.;
taskData.EndTime = DateTime.Now; taskData.EndTime = DateTime.Now;
await DbScoped.Sugar.Updateable(taskData) await videoTaskDB.AsUpdateable(taskData)
.UpdateColumns(it => new .UpdateColumns(it => new
{ {
//it.ChatAnalysis, //it.ChatAnalysis,
@ -230,56 +298,36 @@ namespace VideoAnalysisCore.Common
/// <summary> /// <summary>
/// 初始化 队列 任务 /// 初始化 队列 任务
/// </summary> /// </summary>
public static async void InitChannel() public async void InitChannel()
{ {
if (Redis is null) throw new Exception("redis未初始化"); if (Redis is null) throw new Exception("redis未初始化");
SubscribeList.Add(RedisChannelEnum., (task) =>
{
using var scope = AppCommon.Services?.CreateScope();
if (scope is null || scope.ServiceProvider.GetService<DownloadFile>() is null)
throw new Exception("DownloadFile 未注入");
else
return scope.ServiceProvider.GetService<DownloadFile>()?.RunTask(task) ?? Task.CompletedTask;
});
SubscribeList.Add(RedisChannelEnum., FFMPGEHandle.RunAsync);
SubscribeList.Add(RedisChannelEnum., SenseVoice.RunTask);
//SubscribeList.Add(RedisChannelEnum.解析说话人,Speaker.Run);
SubscribeList.Add(RedisChannelEnum.AI课程类型,
(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>()?.GetVideoType(task) ?? Task.CompletedTask;
});
SubscribeList.Add(RedisChannelEnum.AI模型分析, (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>()?.GetKnow(task) ?? Task.CompletedTask;
});
SubscribeList.Add(RedisChannelEnum.AI分析试题, (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., TaskEnd);
//处理之前程序结束前未能执行完的情况
var oldTaskCount = Redis.LLen(RedisExpandKey.IDTask);
if (oldTaskCount > 0)
{
var oldTaskArr = Redis.LRange(RedisExpandKey.IDTask, 0, oldTaskCount);
Redis.LTrim(RedisExpandKey.IDTask, 1, 0);//删除 redis 列表
foreach (var oldTask in oldTaskArr)
{
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();
await InsertChannel(lastEnum, oldTask);
}
}
else
{
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-------------> 接收新任务!");
ReceivingTaskAsync(); ReceivingTaskAsync();
}
} }
public static bool StopTask { get; set; } = false;
/// <summary> /// <summary>
/// 停止接收新任务 /// 停止接收新任务
/// </summary> /// </summary>
public static void StopTaskAsync() public void StopTaskAsync()
{ {
StopTask = true; StopTask = true;
try try
@ -297,7 +345,7 @@ namespace VideoAnalysisCore.Common
/// <summary> /// <summary>
/// 开始接收新任务 /// 开始接收新任务
/// </summary> /// </summary>
public static void RestartTask() public void RestartTask()
{ {
StopTask = false; StopTask = false;
NewTask(); NewTask();
@ -306,7 +354,7 @@ namespace VideoAnalysisCore.Common
/// 重新执行新任务 /// 重新执行新任务
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static void NewTask() public void NewTask()
{ {
// 取消 消费机的任务订阅 // 取消 消费机的任务订阅
if (StopTask) if (StopTask)
@ -320,7 +368,7 @@ namespace VideoAnalysisCore.Common
/// <summary> /// <summary>
/// 重新接收新任务 /// 重新接收新任务
/// </summary> /// </summary>
public static void ReceivingTaskAsync() public void ReceivingTaskAsync()
{ {
if (AppCommon.Config.TaskSetting.IS_Server) if (AppCommon.Config.TaskSetting.IS_Server)
{ {
@ -329,15 +377,6 @@ namespace VideoAnalysisCore.Common
} }
Task.Run(async () => Task.Run(async () =>
{ {
var oldTask = await Redis.LPopAsync(RedisExpandKey.IDTask);
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();
await InsertChannel(lastEnum, oldTask);
return;
}
lock (Redis) lock (Redis)
{ {
if (Subscribe?.IsUnsubscribed == false)//排除重试机制后 多次接收任务导致内存泄露 if (Subscribe?.IsUnsubscribed == false)//排除重试机制后 多次接收任务导致内存泄露
@ -363,7 +402,7 @@ namespace VideoAnalysisCore.Common
/// <param name="taskID"></param> /// <param name="taskID"></param>
/// <param name="ex"></param> /// <param name="ex"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<bool> SetTaskErrorMessage(long taskID, Exception? ex) public async Task<bool> SetTaskErrorMessage(long taskID, Exception? ex)
{ {
var error = string.Empty; var error = string.Empty;
if (ex != null) if (ex != null)
@ -386,17 +425,17 @@ namespace VideoAnalysisCore.Common
/// </summary> /// </summary>
/// <param name="taskID"></param> /// <param name="taskID"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<bool> ClearTaskError(long taskID) =>await SetTaskError(taskID, string.Empty); public async Task<bool> ClearTaskError(long taskID) =>await SetTaskError(taskID, string.Empty);
/// <summary> /// <summary>
/// 修改任务的错误信息 /// 修改任务的错误信息
/// </summary> /// </summary>
/// <param name="taskID"></param> /// <param name="taskID"></param>
/// <param name="error"></param> /// <param name="error"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<bool> SetTaskError(long taskID, string? error) public 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 videoTaskDB.AsUpdateable()
.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)
.ExecuteCommandAsync() == 1; .ExecuteCommandAsync() == 1;
@ -408,7 +447,7 @@ 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 Task TouchChannel(RedisChannelEnum key, string taskId, Func<string, Task> action = null) public 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);
@ -424,7 +463,7 @@ namespace VideoAnalysisCore.Common
Redis.HMSet(RedisExpandKey.Task(taskId), "Progress", 0); Redis.HMSet(RedisExpandKey.Task(taskId), "Progress", 0);
lock (Redis) lock (Redis)
{ {
DbScoped.SugarScope.Updateable<VideoTask>() videoTaskDB.AsUpdateable()
.SetColumns(it => it.LastEnum == key) .SetColumns(it => it.LastEnum == key)
.Where(it => it.Id == tID) .Where(it => it.Id == tID)
.ExecuteCommand(); .ExecuteCommand();

View File

@ -24,16 +24,16 @@ namespace VideoAnalysisCore.Common
if (CID.ContainsKey(t)) if (CID.ContainsKey(t))
{ {
base.Context = CID[t]!=null base.Context = CID[t]!=null
? DbScoped.SugarScope.GetConnectionScope(CID[t]) ? DbScoped.Sugar.GetConnectionScope(CID[t])
: DbScoped.SugarScope; : DbScoped.Sugar;
return; return;
} }
var c = t.GetCustomAttribute<TenantAttribute>(); var c = t.GetCustomAttribute<TenantAttribute>();
if (!CID.ContainsKey(t)) if (!CID.ContainsKey(t))
CID.Add(t, c?.configId); CID.Add(t, c?.configId);
base.Context = c != null base.Context = c != null
? DbScoped.SugarScope.GetConnectionScope(c.configId) ? DbScoped.Sugar.GetConnectionScope(c.configId)
: DbScoped.SugarScope; : DbScoped.Sugar;
} }
} }
} }

View File

@ -28,15 +28,19 @@ namespace VideoAnalysisCore.Controllers
{ {
private readonly IMapper mp; private readonly IMapper mp;
private readonly Repository<VideoTask> videoTaskDB; private readonly Repository<VideoTask> videoTaskDB;
private readonly RedisManager redisManager;
private readonly Repository<VideoKonwPoint> videoKonwDB; private readonly Repository<VideoKonwPoint> videoKonwDB;
private readonly Repository<CourseInfo> courseInfoDB; private readonly Repository<CourseInfo> courseInfoDB;
public readonly SenseVoice senseVoice;
public ApiController(Repository<VideoTask> videoTaskDB, public ApiController(Repository<VideoTask> videoTaskDB,
IMapper mp, Repository<VideoKonwPoint> videoKonwDB, Repository<CourseInfo> courseInfoDB) IMapper mp, Repository<VideoKonwPoint> videoKonwDB, Repository<CourseInfo> courseInfoDB, RedisManager redisManager, SenseVoice senseVoice)
{ {
this.videoTaskDB = videoTaskDB; this.videoTaskDB = videoTaskDB;
this.mp = mp; this.mp = mp;
this.videoKonwDB = videoKonwDB; this.videoKonwDB = videoKonwDB;
this.courseInfoDB = courseInfoDB; this.courseInfoDB = courseInfoDB;
this.redisManager = redisManager;
this.senseVoice = senseVoice;
} }
private string GetClientIpAddress() private string GetClientIpAddress()
@ -82,9 +86,9 @@ namespace VideoAnalysisCore.Controllers
public IActionResult StartTask(bool task) public IActionResult StartTask(bool task)
{ {
if(task) if(task)
RedisExpand.RestartTask(); redisManager.RestartTask();
else else
RedisExpand.StopTaskAsync(); redisManager.StopTaskAsync();
return Ok(); return Ok();
} }
/// <summary> /// <summary>
@ -100,7 +104,7 @@ namespace VideoAnalysisCore.Controllers
using HttpClient client = new HttpClient(); using HttpClient client = new HttpClient();
// 发送GET请求获取网络文件流 // 发送GET请求获取网络文件流
using var networkStream = await client.GetStreamAsync(url); using var networkStream = await client.GetStreamAsync(url);
var res = await SenseVoice.RunTask(networkStream); var res = await senseVoice.RunTask(networkStream);
return Ok(res); return Ok(res);
} }
catch (Exception ex) catch (Exception ex)
@ -117,7 +121,7 @@ namespace VideoAnalysisCore.Controllers
public async Task<IActionResult> AudioRecognition(IFormFile file) public async Task<IActionResult> AudioRecognition(IFormFile file)
{ {
using var s = file.OpenReadStream(); using var s = file.OpenReadStream();
var res = await SenseVoice.RunTask(s); var res = await senseVoice.RunTask(s);
return Ok(res); return Ok(res);
} }
@ -204,8 +208,8 @@ namespace VideoAnalysisCore.Controllers
.GetProperties(BindingFlags.Public | BindingFlags.Instance) .GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(s => s.Name, s => s.GetValue(task)); .ToDictionary(s => s.Name, s => s.GetValue(task));
await videoTaskDB.InsertAsync(task); await videoTaskDB.InsertAsync(task);
RedisExpand.Redis.HMSet(RedisExpandKey.Task(task.Id), hashEntries); redisManager.Redis.HMSet(RedisExpandKey.Task(task.Id), hashEntries);
RedisExpand.Redis.LPush(RedisExpandKey.ChannelKey, task.Id); redisManager.Redis.LPush(RedisExpandKey.ChannelKey, task.Id);
return Ok(task.Id); return Ok(task.Id);
} }

View File

@ -38,10 +38,11 @@ namespace VideoAnalysisCore.Controllers
private readonly Repository<NodePackageInfo> nodePackageInfoDB; private readonly Repository<NodePackageInfo> nodePackageInfoDB;
private readonly Repository<VideoQuestion> videoQuestionDB; private readonly Repository<VideoQuestion> videoQuestionDB;
private readonly Repository<VideoQuestionKonw> videoQuestionKonwDB; private readonly Repository<VideoQuestionKonw> videoQuestionKonwDB;
private readonly RedisManager redisManager;
public LJZK_Controller(IMapper mp, Repository<NodeSubscription> nodesubscriptionDB, public LJZK_Controller(IMapper mp, Repository<NodeSubscription> nodesubscriptionDB,
Repository<VideoTask> videoTaskDB = null, Repository<VideoKonwPoint> videoKonwPointDB = null Repository<VideoTask> videoTaskDB = null, Repository<VideoKonwPoint> videoKonwPointDB = null
, Repository<NodePackageInfo> nodePackageInfoDB = null, Repository<VideoQuestion> videoQuestionDB = null, Repository<VideoQuestionKonw> videoQuestionKonwDB = null, Repository<CourseInfo> courseInfoDB = null) , Repository<NodePackageInfo> nodePackageInfoDB = null, Repository<VideoQuestion> videoQuestionDB = null, Repository<VideoQuestionKonw> videoQuestionKonwDB = null, Repository<CourseInfo> courseInfoDB = null, RedisManager redisManager = null)
{ {
this.mp = mp; this.mp = mp;
this.nodesubscriptionDB = nodesubscriptionDB; this.nodesubscriptionDB = nodesubscriptionDB;
@ -51,6 +52,7 @@ namespace VideoAnalysisCore.Controllers
this.videoQuestionDB = videoQuestionDB; this.videoQuestionDB = videoQuestionDB;
this.videoQuestionKonwDB = videoQuestionKonwDB; this.videoQuestionKonwDB = videoQuestionKonwDB;
this.courseInfoDB = courseInfoDB; this.courseInfoDB = courseInfoDB;
this.redisManager = redisManager;
} }
@ -125,7 +127,7 @@ namespace VideoAnalysisCore.Controllers
if (videos is null || videos.Count == 0) if (videos is null || videos.Count == 0)
return Ok(); return Ok();
var ids = videos.Select(s => s.Id).ToArray(); var ids = videos.Select(s => s.Id).ToArray();
RedisExpand.JoinQueue(ids); redisManager.JoinQueue(ids);
return Ok(); return Ok();
} }

View File

@ -36,76 +36,76 @@ namespace VideoAnalysisCore.Job
public async Task Invoke() public async Task Invoke()
{ {
throw new Exception("已经弃用"); throw new Exception("已经弃用");
Console.WriteLine($"{DateTime.Now} 执行=>文件节点订阅"); // Console.WriteLine($"{DateTime.Now} 执行=>文件节点订阅");
var videoIdArr = videotaskDB.AsQueryable().Select(v => v.TagId).Distinct().ToArray(); // var videoIdArr = videotaskDB.AsQueryable().Select(v => v.TagId).Distinct().ToArray();
var tasks = await nodesubscriptionDB.GetListAsync(s => s.Enable && s.Subject ==SubjectEnum.); // var tasks = await nodesubscriptionDB.GetListAsync(s => s.Enable && s.Subject ==SubjectEnum.数学);
foreach (var item in tasks) // foreach (var item in tasks)
{
var fileNodeId = item.NodeId;
var data = attachmentsDB.Context.Ado
.SqlQuery<Attachments>($"""
SELECT top {TopLength}
*
FROM
Attachments
WHERE
Id IN (
SELECT
AttachmentsId
FROM
Material
WHERE
id IN (
SELECT
MaterialId
FROM
FileContentMaterial
WHERE
FileContentId IN ( SELECT id FROM FileContent WHERE BagId IN ( SELECT Id FROM FileDirectory WHERE Id={fileNodeId} AND types = 1 AND DeleteState = 0 ) )
AND DeleteState = 0
AND ( MaterialName NOT LIKE '%PPT%' OR MaterialName NOT LIKE '%ppt%' )
)
)
AND Type = ''
AND (
NAME NOT LIKE '%PPT%'
OR NAME NOT LIKE '%ppt%'
) and id>{item.LastId}
""");
if (data is null || data.Count == 0)
continue;
Console.WriteLine($"{DateTime.Now} 视频订阅=>Node:{item.NodeId} 数量{data.Count}");
var videos = new List<VideoTask>(data.Count);
foreach (var s in data)
{
if (videoIdArr.Contains(s.VideoCode))
continue;
//videos.Add(new VideoTask()
// { // {
// Id = YitIdHelper.NextId(), // var fileNodeId = item.NodeId;
// ComeFrom = "127.0.0.1", // var data = attachmentsDB.Context.Ado
// ApiToken = "", // .SqlQuery<Attachments>($"""
// //Type = item.TaskType, //SELECT top {TopLength}
// Subject = item.Subject, // *
// Tag = item.NodeId.ToString(), //FROM
// TagId = s.VideoCode, // Attachments
// MediaUrl = string.Empty, //WHERE
//}); // Id IN (
} // SELECT
var maxId = data.Max(s => s.Id); // AttachmentsId
//入库 更新最后扫描记录 // FROM
await nodesubscriptionDB.AsUpdateable() // Material
.SetColumns(it => it.LastId == maxId) // WHERE
.Where(it => it.Id == item.Id) // id IN (
.ExecuteCommandAsync(); // SELECT
await videotaskDB.InsertRangeAsync(videos); // MaterialId
var ids = videos.Select(s => s.Id).ToArray(); // FROM
RedisExpand.JoinQueue(ids); // FileContentMaterial
// WHERE
// FileContentId IN ( SELECT id FROM FileContent WHERE BagId IN ( SELECT Id FROM FileDirectory WHERE Id={fileNodeId} AND types = 1 AND DeleteState = 0 ) )
// AND DeleteState = 0
// AND ( MaterialName NOT LIKE '%PPT%' OR MaterialName NOT LIKE '%ppt%' )
// )
// )
// AND Type = '录播视频'
// AND (
// NAME NOT LIKE '%PPT%'
// OR NAME NOT LIKE '%ppt%'
// ) and id>{item.LastId}
//""");
// if (data is null || data.Count == 0)
// continue;
// Console.WriteLine($"{DateTime.Now} 视频订阅=>Node:{item.NodeId} 数量{data.Count}");
} // var videos = new List<VideoTask>(data.Count);
// foreach (var s in data)
// {
// if (videoIdArr.Contains(s.VideoCode))
// continue;
// //videos.Add(new VideoTask()
// //{
// // Id = YitIdHelper.NextId(),
// // ComeFrom = "127.0.0.1",
// // ApiToken = "",
// // //Type = item.TaskType,
// // Subject = item.Subject,
// // Tag = item.NodeId.ToString(),
// // TagId = s.VideoCode,
// // MediaUrl = string.Empty,
// //});
// }
// var maxId = data.Max(s => s.Id);
// //入库 更新最后扫描记录
// await nodesubscriptionDB.AsUpdateable()
// .SetColumns(it => it.LastId == maxId)
// .Where(it => it.Id == item.Id)
// .ExecuteCommandAsync();
// await videotaskDB.InsertRangeAsync(videos);
// var ids = videos.Select(s => s.Id).ToArray();
// RedisExpand.JoinQueue(ids);
// }
} }
} }
} }