优化 复习课切题流程/预览UI
This commit is contained in:
parent
ad0f415ef6
commit
2bdd77380c
|
|
@ -11,9 +11,31 @@
|
|||
@for (int i = 0; i < videoKnows.Length; i++)
|
||||
{
|
||||
var item = videoKnows[i];
|
||||
<button class="kBtn" onclick="spClick(@i,this)" title="@item.Content">
|
||||
<span>@getF(item) @item.Theme</span>
|
||||
<br /><span class="kSpan">#@item.KnowPointId @item.KnowPoint</span></button>
|
||||
<div class="knowDiv" onclick="spClick(@i,this)">
|
||||
|
||||
<div class="knowTtile">
|
||||
<div class="knowTtileTheme">@getF(item) @item.Theme</div>
|
||||
<span class="kSpan">#@item.KnowPointId @item.KnowPoint</span>
|
||||
<div>概览: @item.Content</div>
|
||||
<br />
|
||||
@foreach (var q in item.QuestionArr)
|
||||
{
|
||||
<h3>问题: </h3>
|
||||
<div class="knowQuestion">@q.Question</div>
|
||||
<img style="text-align:center" src="@q.ImageUrl" width="320" height="180" />
|
||||
<br />
|
||||
<br />
|
||||
}
|
||||
<br />
|
||||
<br />
|
||||
|
||||
</div>
|
||||
<button class="kBtn" >
|
||||
<span>@getF(item) @item.Theme</span>
|
||||
<br /><span class="kSpan textEllipsis">#@item.KnowPointId @item.KnowPoint</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
@ -28,9 +50,15 @@
|
|||
"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['$', '$'], ['\(', '\)']],
|
||||
displayMath: [['$$', '$$'], ['\[', '\]']]
|
||||
}
|
||||
};
|
||||
window.b = []
|
||||
window.subtitles = []
|
||||
window.subtitles1 = []
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ namespace Learn.VideoAnalysis.Components.Pages
|
|||
[Inject] private ConfirmService ComfirmService { get; set; } = default!;
|
||||
[Inject] private IHttpContextAccessor HttpContext { get; set; } = default!;
|
||||
[Inject] private Repository<VideoTask> taskDB { get; set; } = default!;
|
||||
[Inject] private Repository<VideoQuestion> videoQuestionDB { get; set; } = default!;
|
||||
[Inject] private Repository<VideoQuestionKonw> videoQuestionKonwDB { get; set; } = default!;
|
||||
[Inject] private Repository<VideoKonwPoint> videoKonwPointDB { get; set; } = default!;
|
||||
[Inject] private IJSRuntime JSRuntime { get; set; } = default!;
|
||||
|
||||
|
|
@ -88,10 +90,31 @@ namespace Learn.VideoAnalysis.Components.Pages
|
|||
StartTime = s.First().StartTime,
|
||||
EndTime = s.First().EndTime,
|
||||
Theme = s.First().Theme,
|
||||
StageId = s.First().StageId,
|
||||
KnowPoint = string.Join(',', s.Select(x => x.KnowPoint))
|
||||
}).ToArray();
|
||||
videoPath = AppCommon.GetVideoPath(nowTask.Id.ToString());
|
||||
await JSRuntime.InvokeVoidAsync("setDB", captionsArr, captionsArr1, videoKnows, videoPath);
|
||||
|
||||
if (nowTask.VideoType == AttachmentsInfoType.Review)
|
||||
{
|
||||
var questionArr = await videoQuestionDB
|
||||
.AsQueryable().Where(s => s.VideoTaskId == nowTask.Id)
|
||||
.Select<VideoQuestionShowDto>()
|
||||
.ToArrayAsync();
|
||||
|
||||
var konwDic = (await videoQuestionKonwDB
|
||||
.AsQueryable().Where(s => s.VideoTaskId == nowTask.Id)
|
||||
.ToArrayAsync()).GroupBy(s=>s.VideoQuestionId)
|
||||
.ToDictionary(s=>s.Key);
|
||||
foreach (var item in questionArr.Where(s=> konwDic.ContainsKey(s.Id)))
|
||||
item.KonwArr = konwDic[item.Id].ToArray();
|
||||
foreach (var item in videoKnows)
|
||||
item.QuestionArr = questionArr
|
||||
.Where(s => s.StageId == item.StageId).ToArray();
|
||||
}
|
||||
|
||||
await JSRuntime
|
||||
.InvokeVoidAsync("setDB", captionsArr, captionsArr1, videoKnows, videoPath);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,14 @@
|
|||
overflow-x: hidden;
|
||||
}
|
||||
.kSpan {
|
||||
color:rgba(120, 120, 120,0.66);
|
||||
font-size:0.8rem;
|
||||
color: rgba(120, 120, 120,0.66);
|
||||
font-size: 0.8rem;
|
||||
width: 330px;
|
||||
}
|
||||
.textEllipsis {
|
||||
white-space: nowrap; /* 禁止换行 */
|
||||
overflow: hidden; /* 隐藏溢出内容 */
|
||||
text-overflow: ellipsis; /* 显示省略号 */
|
||||
}
|
||||
video {
|
||||
width: 94%;
|
||||
|
|
@ -36,7 +42,6 @@ video {
|
|||
font-size: 18px;
|
||||
}
|
||||
#segmentsContainer:is(:hover) {
|
||||
width: 400px;
|
||||
right: 0px!important;
|
||||
}
|
||||
#segmentsContainer {
|
||||
|
|
@ -51,18 +56,17 @@ video {
|
|||
width: 400px;
|
||||
height: 750px;
|
||||
gap: 10px;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
float: left;
|
||||
flex-wrap: nowrap;
|
||||
padding: 10px;
|
||||
align-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
align-items: flex-end
|
||||
}
|
||||
|
||||
.kBtn {
|
||||
width: 100%;
|
||||
width: 340px;
|
||||
height: 60px;
|
||||
font-size: 1.3rem;
|
||||
text-align: left;
|
||||
|
|
@ -71,8 +75,30 @@ video {
|
|||
background-color: rgb(240, 249, 235);
|
||||
border: 1px solid rgb(179, 225, 157);
|
||||
}
|
||||
.knowTtileTheme {
|
||||
font-size: 1.3rem;
|
||||
cursor: pointer;
|
||||
color: rgb(103, 194, 58);
|
||||
}
|
||||
|
||||
.kBtn:hover {
|
||||
background-color: rgb(248, 230, 191) !important;
|
||||
border: 1px solid rgb(206, 187, 81);
|
||||
}
|
||||
.knowDiv {
|
||||
cursor: pointer;
|
||||
}
|
||||
.knowQuestion {
|
||||
cursor: pointer;
|
||||
}
|
||||
.knowDiv:hover .knowTtile {
|
||||
width: 340px;
|
||||
display:block;
|
||||
background-color: rgb(240, 249, 235);
|
||||
border: 1px solid rgb(179, 225, 157);
|
||||
}
|
||||
.knowTtile {
|
||||
position: absolute;
|
||||
text-align: left;
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
"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",
|
||||
"SqlType": "MySql",
|
||||
"UpdateTable": true
|
||||
"UpdateTable": false
|
||||
},
|
||||
"AlibabaCloudVod": {
|
||||
"AccessKeyId": "LTAI5tDC6p9h747B7FHbgwkH",
|
||||
|
|
|
|||
|
|
@ -302,82 +302,6 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取知识点
|
||||
/// </summary>
|
||||
/// <param name="task">任务id</param>
|
||||
/// <returns></returns>
|
||||
public async Task<TaskRes> GetKnow(string task)
|
||||
{
|
||||
var taskId = long.Parse(task);
|
||||
var taskInfo = await videoTaskDB.AsQueryable()
|
||||
.Where(s => s.Id == taskId)
|
||||
.FirstAsync();
|
||||
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;
|
||||
}
|
||||
if(taskInfo.VideoType == AttachmentsInfoType.Review)
|
||||
await AnalysisVideoQuestions(taskInfo);
|
||||
|
||||
|
||||
var captionsArr = JsonSerializer.Deserialize<SenseVoiceRes[]>(taskInfo.Captions);
|
||||
//处理视频授课章节
|
||||
var sections = await GetSections(taskInfo, Course_Id);
|
||||
//AI优化字幕
|
||||
captionsArr = await OptimizeSubtitles(taskInfo, captionsArr, sections);
|
||||
//合并字幕
|
||||
var captions = ExpandFunction.GetSpeakerCaptions(captionsArr);
|
||||
var maxVideoTime = captions?.TimeBase?.LastOrDefault()?.End ?? 0;
|
||||
|
||||
var questionRes = new List<VideoKnowRes>();
|
||||
while (true)
|
||||
{
|
||||
questionRes = new List<VideoKnowRes>();
|
||||
//视频字幕分析
|
||||
await Analytics(questionRes, taskInfo, captionsArr, sections);
|
||||
|
||||
if (questionRes.Count == 0) continue;
|
||||
//处理分段 知识点
|
||||
var insertData = await GetVideoKnow(questionRes, taskInfo, sections, Course_Id);
|
||||
//校验结果质量
|
||||
var checkRes = await VerifySpanQuality(questionRes, taskInfo, captions, sections, Course_Id);
|
||||
|
||||
if (checkRes != null && checkRes.Score >= 80)
|
||||
{
|
||||
//写入知识点
|
||||
await videoKonwPointDB.DeleteAsync(s => s.VideoTaskId == taskInfo.Id);
|
||||
await videoKonwPointDB.InsertRangeAsync(insertData);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(DateTime.Now + $"=>{task} 得分过低/分段长度不匹配 得分{checkRes?.Score} ");
|
||||
Console.WriteLine(checkRes.Evaluation);
|
||||
Console.WriteLine();
|
||||
}
|
||||
if (questionRes.Any(s => s.KeepTime < 30))
|
||||
{
|
||||
Console.WriteLine(DateTime.Now + "=>视频分段过短!! 重新进行AI分析");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await RedisExpand.Redis
|
||||
.HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes);
|
||||
|
||||
RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task);
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<T> ChatAsync<T>(string task, string postMessages, string title, string model = "deepseek-reasoner")
|
||||
{
|
||||
|
|
@ -452,7 +376,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
|||
/// <returns></returns>
|
||||
private async Task<SenseVoiceRes[]> AnalysisVideoQuestions(VideoTask taskInfo)
|
||||
{
|
||||
|
||||
Console.WriteLine(DateTime.Now + $"=>{taskInfo.Id} 提取试题");
|
||||
if (taskInfo is null || string.IsNullOrEmpty(taskInfo.PPTKeyFrame))
|
||||
return null;
|
||||
var farmeArr = JsonSerializer.Deserialize<int[]>(taskInfo.PPTKeyFrame);
|
||||
|
|
@ -462,7 +386,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
|||
foreach (var item in farmeArr)
|
||||
{
|
||||
var knowInfoArr = videoKnowArr
|
||||
.Where(s => item >= s.StartTime && item <= s.EndTime)
|
||||
.Where(s => item+5 >= s.StartTime && item+5 <= s.EndTime)
|
||||
.ToArray();
|
||||
if (knowInfoArr is null || knowInfoArr.Count() ==0)
|
||||
return null;
|
||||
|
|
@ -477,12 +401,12 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
|||
.ProcessImageAsync(new SimpleTexOcrRequest(filePath));
|
||||
if (!sRes.Success)
|
||||
continue;
|
||||
var knowArr=string.Join(',', knowInfoArr.Select(s => s.KnowPoint + "|"+s.KnowPointId));
|
||||
var knowArr=string.Join(',', knowInfoArr.Select(s => s.KnowPointId + "|" + s.KnowPoint ));
|
||||
var resFormat = """[{"TopicStem":string(试题题干),"Question:string(问题)","KnowPointId":(string)知识点ID}]""";
|
||||
var postMessages =
|
||||
$"提供一段内容是md格式的试题内容字符串。" +
|
||||
$"请提取出其中的试题内容。并且为每个试题关联上在我限定范围内的知识点(多个则用逗号分割)。" +
|
||||
$"知识点范围[{knowArr}]。" +
|
||||
$"知识点格式(知识点ID|知识点名称)范围[{knowArr}]。" +
|
||||
$"排除不是试题内容的文字,优化公式排版并且去除题号。" +
|
||||
$"如果存在多道大题,请帮忙拆分开!" +
|
||||
$"输出内容只返回json格式为({resFormat})" +
|
||||
|
|
@ -491,6 +415,7 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
|||
var resData = await ChatAsync<VideoQuestionOSSDto[]>(taskInfo.Id.ToString(), postMessages, "提取试题", "deepseek-chat");
|
||||
foreach (var q in resData)
|
||||
{
|
||||
q.Id = YitIdHelper.NextId();
|
||||
q.FilePath = filePath;
|
||||
q.VideoTaskId = taskInfo.Id;
|
||||
q.StageId = knowInfoArr.First().StageId;
|
||||
|
|
@ -502,15 +427,16 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
|||
KnowPointId= kid,
|
||||
StageId =q.StageId,
|
||||
VideoTaskId = q.VideoTaskId,
|
||||
VideoQuestionId = q.Id,
|
||||
});
|
||||
}
|
||||
}
|
||||
insertData.AddRange(resData);
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Console.WriteLine(DateTime.Now + $"=>{taskInfo.Id} 提取{knowInfoArr.First().StartTime}秒试题出现错误 {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -521,12 +447,96 @@ namespace VideoAnalysisCore.AICore.GPT.DeepSeek
|
|||
foreach (var s in item)
|
||||
s.TopicId = keyId;
|
||||
}
|
||||
//上传oss
|
||||
//上传oss 并更新imageUrl
|
||||
ossClient.AddVideoQuestionUrl(insertData);
|
||||
|
||||
await videoQuestionDB.AsDeleteable()
|
||||
.Where(s=>s.VideoTaskId== taskInfo.Id)
|
||||
.ExecuteCommandAsync();
|
||||
await videoQuestionKonwDB.AsDeleteable()
|
||||
.Where(s => s.VideoTaskId == taskInfo.Id)
|
||||
.ExecuteCommandAsync();
|
||||
|
||||
await videoQuestionDB.InsertRangeAsync(insertData.Adapt<VideoQuestion[]>());
|
||||
await videoQuestionKonwDB.InsertRangeAsync(insertQuestionKonw);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取知识点
|
||||
/// </summary>
|
||||
/// <param name="task">任务id</param>
|
||||
/// <returns></returns>
|
||||
public async Task<TaskRes> GetKnow(string task)
|
||||
{
|
||||
var taskId = long.Parse(task);
|
||||
var taskInfo = await videoTaskDB.AsQueryable()
|
||||
.Where(s => s.Id == taskId)
|
||||
.FirstAsync();
|
||||
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);
|
||||
//AI优化字幕
|
||||
captionsArr = await OptimizeSubtitles(taskInfo, captionsArr, sections);
|
||||
//合并字幕
|
||||
var captions = ExpandFunction.GetSpeakerCaptions(captionsArr);
|
||||
var maxVideoTime = captions?.TimeBase?.LastOrDefault()?.End ?? 0;
|
||||
var questionRes = new List<VideoKnowRes>();
|
||||
while (true)
|
||||
{
|
||||
questionRes = new List<VideoKnowRes>();
|
||||
//视频字幕分析
|
||||
await Analytics(questionRes, taskInfo, captionsArr, sections);
|
||||
|
||||
if (questionRes.Count == 0) continue;
|
||||
//处理分段 知识点
|
||||
var insertData = await GetVideoKnow(questionRes, taskInfo, sections, Course_Id);
|
||||
//校验结果质量
|
||||
var checkRes = await VerifySpanQuality(questionRes, taskInfo, captions, sections, Course_Id);
|
||||
|
||||
if (checkRes != null && checkRes.Score >= 80)
|
||||
{
|
||||
//写入知识点
|
||||
await videoKonwPointDB.DeleteAsync(s => s.VideoTaskId == taskInfo.Id);
|
||||
await videoKonwPointDB.InsertRangeAsync(insertData);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(DateTime.Now + $"=>{task} 得分过低/分段长度不匹配 得分{checkRes?.Score} ");
|
||||
Console.WriteLine(checkRes.Evaluation);
|
||||
Console.WriteLine();
|
||||
}
|
||||
if (questionRes.Any(s => s.KeepTime < 30))
|
||||
{
|
||||
Console.WriteLine(DateTime.Now + "=>视频分段过短!! 重新进行AI分析");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await RedisExpand.Redis
|
||||
.HMSetAsync(RedisExpandKey.Task(task), "VideoKnows", questionRes);
|
||||
|
||||
if (taskInfo.VideoType == AttachmentsInfoType.Review)
|
||||
await AnalysisVideoQuestions(taskInfo);
|
||||
|
||||
RedisExpand.InsertChannel(RedisChannelEnum.EndTask, task);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Text;
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
using VideoAnalysisCore.Model.Dto;
|
||||
using VideoAnalysisCore.Model.Enum;
|
||||
|
||||
namespace VideoAnalysisCore.AICore.GPT.Dto
|
||||
|
|
@ -46,6 +47,8 @@ namespace VideoAnalysisCore.AICore.GPT.Dto
|
|||
/// 主题
|
||||
/// </summary>
|
||||
public virtual string? Theme { get; set; }
|
||||
public virtual long? StageId { get; set; }
|
||||
public virtual VideoQuestionShowDto[]? QuestionArr { get; set; }
|
||||
/// <summary>
|
||||
/// 知识点
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ namespace VideoAnalysisCore.Common
|
|||
AccessKeyId = AppCommon.Config.AliyunOSS.AccessKeyId,
|
||||
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
|
||||
AccessKeySecret = AppCommon.Config.AliyunOSS.AccessKeySecret,
|
||||
Endpoint = AppCommon.Config.AliyunOSS.Endpoint
|
||||
Endpoint = AppCommon.Config.AliyunOSS.Endpoint,
|
||||
Region= AppCommon.Config.AliyunOSS.Region,
|
||||
};// 创建ClientConfiguration实例,按照您的需要修改默认参数。
|
||||
var conf = new ClientConfiguration();
|
||||
// 设置v4签名。
|
||||
|
|
@ -79,7 +80,7 @@ namespace VideoAnalysisCore.Common
|
|||
/// 上传文件
|
||||
/// </summary>
|
||||
/// <param name="oss"></param>
|
||||
/// <param name="fileArr">视频尸体片段</param>
|
||||
/// <param name="fileArr">视频实体片段</param>
|
||||
public static void AddVideoQuestionUrl(this OssClient oss, List<VideoQuestionOSSDto> fileArr )
|
||||
{
|
||||
var cached = new HashSet<string>();
|
||||
|
|
|
|||
|
|
@ -11,4 +11,8 @@ namespace VideoAnalysisCore.Model.Dto
|
|||
public string FilePath { get; set; }
|
||||
public string KnowPointId { get; set; }
|
||||
}
|
||||
public class VideoQuestionShowDto : VideoQuestion
|
||||
{
|
||||
public VideoQuestionKonw[] KonwArr { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace VideoAnalysisCore.Model
|
|||
/// <summary>
|
||||
/// id
|
||||
/// </summary>
|
||||
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
|
||||
[SugarColumn(IsPrimaryKey = true)]
|
||||
public long Id { get; set; }
|
||||
/// <summary>
|
||||
/// 视频任务id
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ namespace VideoAnalysisCore.Model
|
|||
/// </summary>
|
||||
public string KnowPointId { get; set; }
|
||||
/// <summary>
|
||||
/// 视频试题ID
|
||||
/// <para>隶属于<see cref="VideoQuestion.Id"/></para>
|
||||
/// </summary>
|
||||
public long VideoQuestionId { get; set; }
|
||||
/// <summary>
|
||||
/// 视频阶段id
|
||||
/// <para>隶属于<see cref="VideoKonwPoint.StageId"/></para>
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue