接入 oss

调试 simpLetex API
This commit is contained in:
小肥羊 2025-05-27 17:59:53 +08:00
parent 2d1c3de69b
commit ad0f415ef6
13 changed files with 304 additions and 25 deletions

View File

@ -66,6 +66,7 @@ namespace Learn.VideoAnalysis
builder.Services.AddSimpleTexOcrClient(); builder.Services.AddSimpleTexOcrClient();
builder.Services.AddDownloadFileExpand(); builder.Services.AddDownloadFileExpand();
builder.Services.AddAlibabaCloudVod(); builder.Services.AddAlibabaCloudVod();
builder.Services.AddAliyunOSS();
builder.Services.AddRedisExpand(); builder.Services.AddRedisExpand();
builder.Services.AddSpeakerAI(); builder.Services.AddSpeakerAI();
builder.Services.AddCoravel(); builder.Services.AddCoravel();

View File

@ -58,13 +58,21 @@
"DB": { "DB": {
"ConnectionString": "AllowLoadLocalInfile=true;Server=192.168.2.9;User ID=root;Password=qwe123!@#;Port=3306;Database=learn.videoanalysis;CharSet=utf8mb4;pooling=true;SslMode=None", "ConnectionString": "AllowLoadLocalInfile=true;Server=192.168.2.9;User ID=root;Password=qwe123!@#;Port=3306;Database=learn.videoanalysis;CharSet=utf8mb4;pooling=true;SslMode=None",
"SqlType": "MySql", "SqlType": "MySql",
"UpdateTable": false "UpdateTable": true
}, },
"AlibabaCloudVod": { "AlibabaCloudVod": {
"AccessKeyId": "LTAI5tDC6p9h747B7FHbgwkH", "AccessKeyId": "LTAI5tDC6p9h747B7FHbgwkH",
"AccessKeySecret": "vRKgmbp1LB05LaGOjh3ZrZxbHSLYLF", "AccessKeySecret": "vRKgmbp1LB05LaGOjh3ZrZxbHSLYLF",
"EndPoint": "vod.cn-shanghai.aliyuncs.com" // "EndPoint": "vod.cn-shanghai.aliyuncs.com" //
}, },
"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" //
},
"OtherDBArr": [ "OtherDBArr": [
{ {
"ConfigId": 1001, //ResourceBank "ConfigId": 1001, //ResourceBank

View File

@ -16,6 +16,8 @@ using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using static System.Collections.Specialized.BitVector32; using static System.Collections.Specialized.BitVector32;
using FFmpeg.NET.Services; using FFmpeg.NET.Services;
using Aliyun.OSS;
using Yitter.IdGenerator;
namespace VideoAnalysisCore.AICore.GPT.DeepSeek namespace VideoAnalysisCore.AICore.GPT.DeepSeek
{ {
@ -29,14 +31,16 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
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;
private readonly Repository<VideoQuestionKonw> videoQuestionKonwDB;
private readonly Repository<KnowledgeInfo> knowledgeInfoDB; private readonly Repository<KnowledgeInfo> knowledgeInfoDB;
private readonly SimpLetexClient simpLetexClient; private readonly SimpLetexClient simpLetexClient;
private readonly OssClient ossClient;
/// <summary> /// <summary>
/// 初始化 /// 初始化
/// </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) 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)
{ {
chatClient = moonshotClient; chatClient = moonshotClient;
criteriaDB = criteria; criteriaDB = criteria;
@ -45,6 +49,8 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
this.videoKonwPointDB = videoKonwPointDB; this.videoKonwPointDB = videoKonwPointDB;
this.simpLetexClient = simpLetexClient; this.simpLetexClient = simpLetexClient;
this.videoQuestionDB = videoQuestionDB; this.videoQuestionDB = videoQuestionDB;
this.ossClient = ossClient;
this.videoQuestionKonwDB = videoQuestionKonwDB;
} }
/// <summary> /// <summary>
/// 获取内容对应的章节 /// 获取内容对应的章节
@ -95,6 +101,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
s => s =>
{ {
var ks = s.KnowPoint.Split(",").Distinct(); var ks = s.KnowPoint.Split(",").Distinct();
var StageId=Yitter.IdGenerator.YitIdHelper.NextId();
return ks.Where(x => knowDic.ContainsKey(x)) return ks.Where(x => knowDic.ContainsKey(x))
.Select(x => new VideoKonwPoint() .Select(x => new VideoKonwPoint()
{ {
@ -102,6 +109,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
Theme = s.Theme, Theme = s.Theme,
StartTime = s.StartTime, StartTime = s.StartTime,
EndTime = s.EndTime, EndTime = s.EndTime,
StageId = StageId,
KnowPoint = x, KnowPoint = x,
KnowPointId = knowDic[x].ToString(), KnowPointId = knowDic[x].ToString(),
TagId = taskInfo.TagId, TagId = taskInfo.TagId,
@ -445,11 +453,12 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
private async Task<SenseVoiceRes[]> AnalysisVideoQuestions(VideoTask taskInfo) private async Task<SenseVoiceRes[]> AnalysisVideoQuestions(VideoTask taskInfo)
{ {
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 videoKnowArr = await videoKonwPointDB.GetListAsync(s => s.VideoTaskId == taskInfo.Id); var videoKnowArr = await videoKonwPointDB.GetListAsync(s => s.VideoTaskId == taskInfo.Id);
var insertData =new List<VideoQuestion>(); var insertData =new List<VideoQuestionOSSDto>();
var insertQuestionKonw = new List<VideoQuestionKonw>();
foreach (var item in farmeArr) foreach (var item in farmeArr)
{ {
var knowInfoArr = videoKnowArr var knowInfoArr = videoKnowArr
@ -457,33 +466,47 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
.ToArray(); .ToArray();
if (knowInfoArr is null || knowInfoArr.Count() ==0) if (knowInfoArr is null || knowInfoArr.Count() ==0)
return null; return null;
while (true) var tryCount = 50;
while (tryCount>1)
{ {
tryCount--;
try try
{ {
var filePath = taskInfo.FramePath(item);
var sRes = await simpLetexClient var sRes = await simpLetexClient
.ProcessImageAsync(new SimpleTexOcrRequest(taskInfo.FramePath(item))); .ProcessImageAsync(new SimpleTexOcrRequest(filePath));
if (!sRes.Success) if (!sRes.Success)
continue; continue;
var knowArr=string.Join(',', knowInfoArr.Select(s => s.KnowPoint + "|"+s.KnowPointId)); var knowArr=string.Join(',', knowInfoArr.Select(s => s.KnowPoint + "|"+s.KnowPointId));
var resFormat = """[{"TopicStem":string(试题题干),"Question:string()","KnowPointId":(string)知识点ID(多个使用逗号拼接)}]"""; var resFormat = """[{"TopicStem":string(试题题干),"Question:string()","KnowPointId":(string)知识点ID}]""";
var postMessages = var postMessages =
$"提供一段内容是md格式的试题内容字符串。" + $"提供一段内容是md格式的试题内容字符串。" +
$"请提取出其中的试题内容。并且为他们关联上在我限定范围内的知识点。" + $"请提取出其中的试题内容。并且为每个试题关联上在我限定范围内的知识点(多个则用逗号分割)。" +
$"知识点范围[{knowArr}]。" + $"知识点范围[{knowArr}]。" +
$"排除不是试题内容的文字,优化公式排版并且去除题号。" + $"排除不是试题内容的文字,优化公式排版并且去除题号。" +
$"如果存在多道大题,请帮忙拆分开!" + $"如果存在多道大题,请帮忙拆分开!" +
$"输出内容只返回json格式为({resFormat})" + $"输出内容只返回json格式为({resFormat})" +
$"以下是试题内容" + $"以下是试题内容" +
$"`{sRes.Result.res.info.markdown}`"; $"`{sRes.Result.res.info.markdown}`";
var resData = await ChatAsync<VideoQuestion[]>(taskInfo.Id.ToString(), postMessages, "提取试题", "deepseek-chat"); var resData = await ChatAsync<VideoQuestionOSSDto[]>(taskInfo.Id.ToString(), postMessages, "提取试题", "deepseek-chat");
foreach (var q in resData) foreach (var q in resData)
{ {
q.FilePath = filePath;
q.VideoTaskId = taskInfo.Id; q.VideoTaskId = taskInfo.Id;
q.VideoKonwPoint = knowInfoArr.First().Id; q.StageId = knowInfoArr.First().StageId;
foreach (var kid in q.KnowPointId.Split(","))
{
insertQuestionKonw.Add(new VideoQuestionKonw()
{
KnowPoint = knowInfoArr.First(s => s.KnowPointId == kid).KnowPoint,
KnowPointId= kid,
StageId =q.StageId,
VideoTaskId = q.VideoTaskId,
});
}
} }
//处理知识点
insertData.AddRange(resData); insertData.AddRange(resData);
break;
} }
catch (Exception) catch (Exception)
{ {
@ -491,9 +514,17 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
} }
} }
} }
//分组共同题干试题ID
foreach (var item in insertData.GroupBy(x => x.TopicStem))
await videoQuestionDB.InsertRangeAsync(insertData); {
var keyId=YitIdHelper.NextId();
foreach (var s in item)
s.TopicId = keyId;
}
//上传oss
ossClient.AddVideoQuestionUrl(insertData);
await videoQuestionDB.InsertRangeAsync(insertData.Adapt<VideoQuestion[]>());
await videoQuestionKonwDB.InsertRangeAsync(insertQuestionKonw);
return null; return null;
} }

View File

@ -0,0 +1,57 @@
{
"Env": [],
"IIsConfig": {
"SdkType": null,
"WebSiteName": "",
"LastEnvName": null,
"EnvPairList": []
},
"WindowsServiveConfig": {
"ServiceName": "",
"SdkType": null,
"LastEnvName": null,
"EnvPairList": []
},
"LinuxServiveConfig": {
"ServiceName": "",
"EnvParam": "",
"LastEnvName": null,
"EnvPairList": []
},
"DockerConfig": {
"Prot": "",
"AspNetCoreEnv": "",
"LastEnvName": null,
"RemoveDaysFromPublished": "10",
"WorkDir": "",
"Volume": "",
"Other": "",
"EnvPairList": []
},
"DockerImageConfig": {
"BaseHttpProxy": "",
"BaseImage": "",
"BaseImageCredential": {
"UserName": "",
"Password": ""
},
"TargetImage": "",
"TargetHttpProxy": "",
"TargetTags": [
""
],
"TargetImageCredential": {
"UserName": "",
"Password": ""
},
"ImageFormat": "Docker",
"Entrypoint": [
""
],
"Cmd": [
""
],
"IgnoreList": [],
"SkipExistingImages": false
}
}

View File

@ -99,7 +99,7 @@ namespace VideoAnalysisCore.Common
/// <returns></returns> /// <returns></returns>
public static string FramePath(this VideoTask task, int fTime) public static string FramePath(this VideoTask task, int fTime)
{ {
return Path.Combine(task.Id.ToString().LocalPath(), $"{FrameName}{fTime / 5}.jpg"); return Path.Combine(task.Id.ToString().LocalPath(), $"{FrameName}{(fTime / 5).ToString().PadLeft(3,'0')}.jpg");
} }
/// <summary> /// <summary>

View File

@ -44,6 +44,7 @@ namespace VideoAnalysisCore.Common
/// 阿里云视频点播配置 /// 阿里云视频点播配置
/// </summary> /// </summary>
public AlibabaCloudVodConfig AlibabaCloudVod { get; set; } = new AlibabaCloudVodConfig(); public AlibabaCloudVodConfig AlibabaCloudVod { get; set; } = new AlibabaCloudVodConfig();
public AliyunOSSConfig AliyunOSS { get; set; } = new AliyunOSSConfig();
/// <summary> /// <summary>

View File

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using AlibabaCloud.OpenApiClient.Models;
using AlibabaCloud.SDK.Vod20170321;
using AlibabaCloud.SDK.Vod20170321.Models;
using AlibabaCloud.TeaUtil.Models;
using Aliyun.OSS.Common;
using Aliyun.OSS;
using Microsoft.Extensions.DependencyInjection;
using VideoAnalysisCore.Job;
using VideoAnalysisCore.Model;
using System.Security.AccessControl;
using Aliyun.Credentials.Models;
using VideoAnalysisCore.Model.Dto;
using System.IO;
namespace VideoAnalysisCore.Common
{
public class AliyunOSSConfig
{
/// <summary>
/// id
/// </summary>
public string AccessKeyId { get; set; }
/// <summary>
///密钥
/// </summary>
public string AccessKeySecret { get; set; }
/// <summary>
/// 区域Url
/// </summary>
public string Region { get; set; }
/// <summary>
/// 筒域名
/// </summary>
public string BucketDomain { get; set; }
/// <summary>
/// 桶名称
/// </summary>
public string BucketName { get; set; }
public string Endpoint { get; set; } = "oss-cn-chengdu.aliyuncs.com";
}
/// <summary>
/// 阿里云 视频点播拓展
/// </summary>
public static class AliyunOSSExpand
{
/// <summary>
/// 使用阿里云 vod拓展
/// </summary>
/// <param name="service"></param>
/// <returns></returns>
public static void AddAliyunOSS(this IServiceCollection service)
{
Console.WriteLine($"{DateTime.Now}=>初始化 阿里云OSS");
AliyunOSSConfig config = new()
{
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
AccessKeyId = AppCommon.Config.AliyunOSS.AccessKeyId,
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
AccessKeySecret = AppCommon.Config.AliyunOSS.AccessKeySecret,
Endpoint = AppCommon.Config.AliyunOSS.Endpoint
};// 创建ClientConfiguration实例按照您的需要修改默认参数。
var conf = new ClientConfiguration();
// 设置v4签名。
conf.SignatureVersion = SignatureVersion.V4;
// 创建OssClient实例。
var oss = new OssClient(config.Endpoint, config.AccessKeyId, config.AccessKeySecret, conf);
oss.SetRegion(config.Region);
service.AddSingleton(oss);
}
/// <summary>
/// 上传文件
/// </summary>
/// <param name="oss"></param>
/// <param name="fileArr">视频尸体片段</param>
public static void AddVideoQuestionUrl(this OssClient oss, List<VideoQuestionOSSDto> fileArr )
{
var cached = new HashSet<string>();
foreach (var item in fileArr)
{
try
{
var path = item.VideoTaskId.ToString() + "/" + Path.GetFileName(item.FilePath);
if (cached.Contains(item.FilePath))
{
item.ImageUrl = AppCommon.Config.AliyunOSS.BucketDomain + "/" + path;
continue;
}
using var file = File.OpenRead(item.FilePath);
var result = oss
.PutObject(
AppCommon.Config.AliyunOSS.BucketName,
path,
file);
item.ImageUrl = AppCommon.Config.AliyunOSS.BucketDomain + "/" + path;
cached.Add(item.FilePath);
continue;
}
catch (Exception)
{
}
}
}
}
}

View File

@ -58,7 +58,7 @@ namespace VideoAnalysisCore.Common
/// 用以指定识别图片的类型如果使用auto则会自动检测使用document会返回markdown文档结果使用formula会返回LaTeX结果 /// 用以指定识别图片的类型如果使用auto则会自动检测使用document会返回markdown文档结果使用formula会返回LaTeX结果
/// <para>"auto", "document", "formula"</para> /// <para>"auto", "document", "formula"</para>
/// </summary> /// </summary>
public string rec_mode { get; set; } = "auto"; public string rec_mode { get; set; } = "document";
/// <summary> /// <summary>
/// 开启后模型将基于0°90°, 180°, 270°自动矫正上传图片的方向默认不开启 /// 开启后模型将基于0°90°, 180°, 270°自动矫正上传图片的方向默认不开启
/// </summary> /// </summary>
@ -159,8 +159,9 @@ namespace VideoAnalysisCore.Common
try try
{ {
var response = await client.SendAsync(requestMessage); var response = await client.SendAsync(requestMessage);
var resStr = await response.Content.ReadAsStringAsync();
var responseContent = await response.Content.ReadFromJsonAsync<SimpleTexOcrResponseData>(); var responseContent = await response.Content.ReadFromJsonAsync<SimpleTexOcrResponseData>();
request.file.Dispose();
return new SimpleTexOcrResponse return new SimpleTexOcrResponse
{ {
Success = response.IsSuccessStatusCode, Success = response.IsSuccessStatusCode,

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VideoAnalysisCore.Model.Dto
{
public class VideoQuestionOSSDto: VideoQuestion
{
public string FilePath { get; set; }
public string KnowPointId { get; set; }
}
}

View File

@ -20,13 +20,17 @@ namespace VideoAnalysisCore.Model
/// <summary> /// <summary>
/// id /// id
/// </summary> /// </summary>
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)] [SugarColumn(IsPrimaryKey = true)]
public long Id { get; set; } public long Id { get; set; }
/// <summary> /// <summary>
/// 视频任务id /// 视频任务id
/// </summary> /// </summary>
public long VideoTaskId { get; set; } public long VideoTaskId { get; set; }
/// <summary> /// <summary>
/// 视频阶段id
/// </summary>
public long StageId { get; set; }
/// <summary>
/// 自定义Id [任务视频自定义id] /// 自定义Id [任务视频自定义id]
/// <see cref="VideoTask.TagId"/> /// <see cref="VideoTask.TagId"/>
/// </summary> /// </summary>
@ -67,6 +71,7 @@ namespace VideoAnalysisCore.Model
/// <summary> /// <summary>
/// 课程阶段 /// 课程阶段
/// </summary> /// </summary>
[SugarColumn(IsIgnore = true)]
public virtual StageEnum? Stage { get; set; } public virtual StageEnum? Stage { get; set; }
} }
} }

View File

@ -27,22 +27,26 @@ namespace VideoAnalysisCore.Model
/// </summary> /// </summary>
public long VideoTaskId { get; set; } public long VideoTaskId { get; set; }
/// <summary> /// <summary>
/// 视频片段ID /// 视频阶段id
/// <para>隶属于<see cref="VideoKonwPoint.Id"/></para> /// <para>隶属于<see cref="VideoKonwPoint.StageId"/></para>
/// </summary> /// </summary>
public long VideoKonwPoint { get; set; } public long StageId { get; set; }
/// <summary> /// <summary>
/// 知识点 /// 题干id
/// <para>[用于多个一个题干多个问题]</para>
/// </summary> /// </summary>
public string? KnowPoint { get; set; } public long TopicId { get; set; }
/// <summary> /// <summary>
/// 知识点ID /// 图片地址
/// </summary> /// </summary>
public string? KnowPointId { get; set; } [SugarColumn(Length =100)]
public string? ImageUrl { get; set; }
/// <summary> /// <summary>
/// 题干 /// 题干
/// </summary> /// </summary>
[SugarColumn(ColumnDataType ="text")]
public string? TopicStem { get; set; } public string? TopicStem { get; set; }
/// <summary> /// <summary>
/// 问题(设问) /// 问题(设问)

View File

@ -0,0 +1,43 @@
using SqlSugar;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Text.Json;
using UserCenter.Model.Enum;
using VideoAnalysisCore.AICore.GPT.Dto;
using VideoAnalysisCore.AICore.SherpaOnnx;
using VideoAnalysisCore.Model.Enum;
using VideoAnalysisCore.Model.Interface;
using Whisper.net;
namespace VideoAnalysisCore.Model
{
/// <summary>
/// 视频片段试题知识点关系表
/// </summary>
[SugarTable("videoquestionkonw")]
public class VideoQuestionKonw : IDB
{
/// <summary>
/// id
/// </summary>
[SugarColumn(IsPrimaryKey = true)]
public long Id { get; set; }
/// <summary>
/// 视频任务id
/// </summary>
public long VideoTaskId { get; set; }
/// <summary>
/// 知识点名称
/// </summary>
public string KnowPoint { get; set; }
/// <summary>
/// 知识点ID
/// </summary>
public string KnowPointId { get; set; }
/// <summary>
/// 视频阶段id
/// <para>隶属于<see cref="VideoKonwPoint.StageId"/></para>
/// </summary>
public long StageId { get; set; }
}
}

View File

@ -59,6 +59,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AlibabaCloud.SDK.Vod20170321" Version="3.6.1" /> <PackageReference Include="AlibabaCloud.SDK.Vod20170321" Version="3.6.1" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="Coravel" Version="6.0.2" /> <PackageReference Include="Coravel" Version="6.0.2" />
<PackageReference Include="FreeRedis" Version="1.3.2" /> <PackageReference Include="FreeRedis" Version="1.3.2" />
<PackageReference Include="Downloader" Version="3.2.1" /> <PackageReference Include="Downloader" Version="3.2.1" />