From 489aff9087ef47b872018ff83472cf3546c8edea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E8=82=A5=E7=BE=8A?= <1048382248@qq.com>
Date: Tue, 5 Nov 2024 17:19:20 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20VideoTaskPage.razor=20?=
=?UTF-8?q?=E9=A1=B5=E9=9D=A2=20=E4=BC=98=E5=8C=96=20=E4=BB=BB=E5=8A=A1?=
=?UTF-8?q?=E6=89=A7=E8=A1=8C=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Components/Layouts/BasicLayout.razor.cs | 17 +++-
.../Components/Pages/EvaluationProject.razor | 2 +-
.../Components/Pages/VideoTaskPage.razor | 22 +++++
.../Components/Pages/VideoTaskPage.razor.cs | 94 +++++++++++++++++++
.../Components/Pages/VideoTaskPage.razor.css | 3 +
VideoAnalysis/Learn.VideoAnalysis.csproj | 1 +
VideoAnalysis/Program.cs | 2 +
VideoAnalysis/appsettings.json | 2 +-
.../AICore/SherpaOnnx/SenseVoice.cs | 10 +-
.../AICore/SherpaOnnx/Speaker.cs | 6 +-
VideoAnalysisCore/Common/DownloadFile.cs | 6 +-
VideoAnalysisCore/Common/RedisExpand.cs | 64 +++++++++++--
VideoAnalysisCore/Common/Repository.cs | 1 +
VideoAnalysisCore/Enum/RedisChannelEnum.cs | 6 +-
VideoAnalysisCore/Model/Dto/VideoTaskDto.cs | 73 ++++++++++++++
VideoAnalysisCore/Model/VideoTask.cs | 48 ++++++++--
VideoAnalysisCore/VideoAnalysisCore.csproj | 1 +
17 files changed, 329 insertions(+), 29 deletions(-)
create mode 100644 VideoAnalysis/Components/Pages/VideoTaskPage.razor
create mode 100644 VideoAnalysis/Components/Pages/VideoTaskPage.razor.cs
create mode 100644 VideoAnalysis/Components/Pages/VideoTaskPage.razor.css
create mode 100644 VideoAnalysisCore/Model/Dto/VideoTaskDto.cs
diff --git a/VideoAnalysis/Components/Layouts/BasicLayout.razor.cs b/VideoAnalysis/Components/Layouts/BasicLayout.razor.cs
index 49d3bbb..af76e51 100644
--- a/VideoAnalysis/Components/Layouts/BasicLayout.razor.cs
+++ b/VideoAnalysis/Components/Layouts/BasicLayout.razor.cs
@@ -19,15 +19,22 @@ namespace VideoAnalysisRazor.Layouts
}
protected override async Task OnInitializedAsync()
{
- _menuData = new[] {
+ _menuData = [
+ new MenuDataItem
+ {
+ Path = "/Project",
+ Name = "课堂指标",
+ Key = "EvaluationProject",
+ Icon = "question-circle",
+ },
new MenuDataItem
{
Path = "/",
- Name = "课堂指标",
- Key = "EvaluationProject",
- Icon = "EvaluationProject",
+ Name = "任务队列",
+ Key = "VideoTaskPage",
+ Icon = "unordered-list",
}
- };
+ ];
}
void Reload()
diff --git a/VideoAnalysis/Components/Pages/EvaluationProject.razor b/VideoAnalysis/Components/Pages/EvaluationProject.razor
index 09d1f3b..e9a99cb 100644
--- a/VideoAnalysis/Components/Pages/EvaluationProject.razor
+++ b/VideoAnalysis/Components/Pages/EvaluationProject.razor
@@ -1,4 +1,4 @@
-@page "/"
+@page "/Project"
@using AntDesign
@using AntDesign.TableModels
@using System.ComponentModel.DataAnnotations
diff --git a/VideoAnalysis/Components/Pages/VideoTaskPage.razor b/VideoAnalysis/Components/Pages/VideoTaskPage.razor
new file mode 100644
index 0000000..d298a67
--- /dev/null
+++ b/VideoAnalysis/Components/Pages/VideoTaskPage.razor
@@ -0,0 +1,22 @@
+@page "/"
+@using AntDesign
+@using AntDesign.TableModels
+@using System.ComponentModel.DataAnnotations
+@using SqlSugar
+@using VideoAnalysisCore.Model
+@using VideoAnalysisCore.Model.Dto
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VideoAnalysis/Components/Pages/VideoTaskPage.razor.cs b/VideoAnalysis/Components/Pages/VideoTaskPage.razor.cs
new file mode 100644
index 0000000..7cdd79c
--- /dev/null
+++ b/VideoAnalysis/Components/Pages/VideoTaskPage.razor.cs
@@ -0,0 +1,94 @@
+using AntDesign;
+using AntDesign.TableModels;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.DataProtection.KeyManagement;
+using SqlSugar;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+using VideoAnalysisCore.Common;
+using VideoAnalysisCore.Enum;
+using VideoAnalysisCore.Model;
+using VideoAnalysisCore.Model.Dto;
+
+namespace Learn.VideoAnalysis.Components.Pages
+{
+ public partial class VideoTaskPage : ComponentBase
+ {
+
+ [Inject] private ConfirmService ComfirmService { get; set; } = default!;
+ [Inject] private Repository taskDB { get; set; } = default!;
+
+
+ IEnumerable _selectedRows = [];
+ ITable _table;
+
+ List _dataSource = null;
+ RefAsync _total = 0;
+
+ bool tableLoading = false;
+
+ ///
+ /// 重试
+ ///
+ ///
+ async void ReStart(VideoTaskDto query)
+ {
+ var lastEnum = (await RedisExpand.Redis.HMGetAsync(RedisExpandKey.Task(query.Id), "LastEnum")).FirstOrDefault() ;
+ RedisExpand.InsertChannel(lastEnum, query.Id);
+ }
+ ///
+ /// 分页 查询 筛选 时
+ ///
+ ///
+ async void OnChange(QueryModel query)
+ {
+ tableLoading = true;
+ List where = default!;
+ if (query.FilterModel != null && ((query.FilterModel?.Count() ?? 0) > 0))
+ {
+ where = query.ToSqlSugerWhere();
+ }
+ _dataSource = await taskDB.AsQueryable()
+ .Where(where)
+ .Select()
+ .ToPageListAsync(query.PageIndex - 1, query.PageSize, _total);
+
+ foreach (var item in _dataSource)
+ {
+ if (!string.IsNullOrEmpty(item.ErrorMessage) || item.LastEnum == RedisChannelEnum.EndTask)
+ continue;
+ item.Progress = RedisExpand.Redis.HMGet(RedisExpandKey.Task(item.Id), "Progress").FirstOrDefault();
+ }
+
+ tableLoading = false;
+ StateHasChanged();
+
+ }
+ ///
+ /// 初始化
+ ///
+ protected override void OnInitialized()
+ {
+ var timer = new Timer(_ =>
+ {
+ InvokeAsync(() =>
+ {
+ foreach (var item in _dataSource)
+ {
+ if (!string.IsNullOrEmpty(item.ErrorMessage) || item.LastEnum == RedisChannelEnum.EndTask)
+ continue;
+ item.Progress = RedisExpand.Redis.HMGet(RedisExpandKey.Task(item.Id), "Progress").FirstOrDefault();
+ }
+ } );
+ InvokeAsync(StateHasChanged);
+ }, null, dueTime: 500, period: 3000);
+ }
+
+ private async Task Comfirm(string message)
+ {
+ return await ComfirmService.Show(message, "提示", ConfirmButtons.YesNo, ConfirmIcon.Warning) == ConfirmResult.Yes;
+ }
+
+ }
+
+}
diff --git a/VideoAnalysis/Components/Pages/VideoTaskPage.razor.css b/VideoAnalysis/Components/Pages/VideoTaskPage.razor.css
new file mode 100644
index 0000000..aca1ed1
--- /dev/null
+++ b/VideoAnalysis/Components/Pages/VideoTaskPage.razor.css
@@ -0,0 +1,3 @@
+input[aria-hidden="true"] {
+ display: none !important;
+}
diff --git a/VideoAnalysis/Learn.VideoAnalysis.csproj b/VideoAnalysis/Learn.VideoAnalysis.csproj
index 384c983..ea4259f 100644
--- a/VideoAnalysis/Learn.VideoAnalysis.csproj
+++ b/VideoAnalysis/Learn.VideoAnalysis.csproj
@@ -17,6 +17,7 @@
+
diff --git a/VideoAnalysis/Program.cs b/VideoAnalysis/Program.cs
index cf4e036..a4669c5 100644
--- a/VideoAnalysis/Program.cs
+++ b/VideoAnalysis/Program.cs
@@ -7,6 +7,7 @@ using VideoAnalysisCore.AICore.ChatGPT;
using VideoAnalysisCore.AICore.ChatGPT.KIMI;
using VideoAnalysisCore.AICore.SherpaOnnx;
using SqlSugar;
+using Mapster;
@@ -69,6 +70,7 @@ namespace Learn.VideoAnalysis
builder.Services.AddAntDesign();
builder.Services.InitSqlSugar();
+ builder.Services.AddMapster();
builder.Services.Configure(builder.Configuration.GetSection("ProSettings"));
diff --git a/VideoAnalysis/appsettings.json b/VideoAnalysis/appsettings.json
index c84b063..5bc6be3 100644
--- a/VideoAnalysis/appsettings.json
+++ b/VideoAnalysis/appsettings.json
@@ -26,7 +26,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": false,
+ "UpdateTable": true,
}
}
}
diff --git a/VideoAnalysisCore/AICore/SherpaOnnx/SenseVoice.cs b/VideoAnalysisCore/AICore/SherpaOnnx/SenseVoice.cs
index 1ac7864..22e9a23 100644
--- a/VideoAnalysisCore/AICore/SherpaOnnx/SenseVoice.cs
+++ b/VideoAnalysisCore/AICore/SherpaOnnx/SenseVoice.cs
@@ -77,9 +77,7 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
throw new Exception("task 音频路径未找到");
- string testWaveFilename = filePath;
- WaveReader reader = new WaveReader(testWaveFilename);
-
+ WaveReader reader = new WaveReader(filePath);
int numSamples = reader.Samples.Length;
int windowSize = VADModelConfig.SileroVad.WindowSize;
int sampleRate = VADModelConfig.SampleRate;
@@ -110,7 +108,11 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
{
Text = stream.Result.Text,
Start= startTime,
- End = startTime + duration });
+ End = startTime + duration
+ });
+ var progress = (double)(startTime + duration) / numSamples * 100;
+ RedisExpand.SetTaskProgress(task, progress);
+
}
VAD.Pop();
}
diff --git a/VideoAnalysisCore/AICore/SherpaOnnx/Speaker.cs b/VideoAnalysisCore/AICore/SherpaOnnx/Speaker.cs
index 203ce1f..2ddf81b 100644
--- a/VideoAnalysisCore/AICore/SherpaOnnx/Speaker.cs
+++ b/VideoAnalysisCore/AICore/SherpaOnnx/Speaker.cs
@@ -50,11 +50,15 @@ namespace VideoAnalysisCore.AICore.SherpaOnnx
if (SD.SampleRate != waveReader.SampleRate)
throw new Exception($"预期采样率:{SD.SampleRate}. 传入: {waveReader.SampleRate}");
+ var i = 0;
var segments = SD.ProcessWithCallback(waveReader.Samples,
(numProcessedChunks, numTotalChunks, arg) =>
{
+ i++;
+ if(i%50 !=0)
+ return 1;
var progress = (double)numProcessedChunks / numTotalChunks * 100;
- Console.WriteLine($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}=> {task} 说话人日志: {progress:F2}%");
+ RedisExpand.SetTaskProgress(task, progress);
return 1;
}, nint.Zero);
var res = segments.Select(s => new OfflineSpeakerRes(s));
diff --git a/VideoAnalysisCore/Common/DownloadFile.cs b/VideoAnalysisCore/Common/DownloadFile.cs
index 492b6e0..f31ef05 100644
--- a/VideoAnalysisCore/Common/DownloadFile.cs
+++ b/VideoAnalysisCore/Common/DownloadFile.cs
@@ -58,6 +58,8 @@ namespace VideoAnalysisCore.Common
// 获取文件大小
var totalBytes = response.Content.Headers.ContentLength;
+ if (!totalBytes.HasValue)
+ throw new Exception(task+" 未能获取到下载文件的 ContentLength ");
var localPath = task.LocalPath();
var outputPath = Path.Combine(localPath, task + fileExtension);
if (!Directory.Exists(localPath)) Directory.CreateDirectory(localPath);
@@ -80,10 +82,10 @@ namespace VideoAnalysisCore.Common
totalBytesRead += bytesRead;
// 计算下载进度
- if (totalBytes.HasValue && count % 3 == 0)
+ if (count % 50 == 0)
{
var progress = (double)totalBytesRead / totalBytes.Value * 100;
- Console.WriteLine($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}=> {task} 下载进度: {progress:F2}%");
+ RedisExpand.SetTaskProgress(task, progress);
}
}
diff --git a/VideoAnalysisCore/Common/RedisExpand.cs b/VideoAnalysisCore/Common/RedisExpand.cs
index a6ff907..b00d9d1 100644
--- a/VideoAnalysisCore/Common/RedisExpand.cs
+++ b/VideoAnalysisCore/Common/RedisExpand.cs
@@ -1,5 +1,6 @@
using FreeRedis;
using Microsoft.Extensions.DependencyInjection;
+using SqlSugar.IOC;
using System;
using System.Threading.Channels;
using System.Threading.Tasks;
@@ -11,6 +12,7 @@ using VideoAnalysisCore.AICore.FFMPGE;
using VideoAnalysisCore.AICore.SherpaOnnx;
using VideoAnalysisCore.AICore.Whisper;
using VideoAnalysisCore.Enum;
+using VideoAnalysisCore.Model;
namespace VideoAnalysisCore.Common
{
@@ -89,6 +91,25 @@ namespace VideoAnalysisCore.Common
Redis.Deserialize = (json, type) => System.Text.Json.JsonSerializer.Deserialize(json, type);
InitChannel();
}
+
+ ///
+ /// 获取任务进度
+ ///
+ ///
+ public static double SetTaskProgress(object taskId)
+ {
+ return Redis.HMGet(RedisExpandKey.Task(taskId), "Progress")[0];
+ }
+ ///
+ /// 设置任务进度
+ ///
+ /// 进度百分比
+ ///
+ public static void SetTaskProgress(object taskId,double p)
+ {
+ Redis.HMSet(RedisExpandKey.Task(taskId), "Progress", p);
+
+ }
///
/// 将任务 插入 队列
///
@@ -106,7 +127,6 @@ namespace VideoAnalysisCore.Common
else
startTime[@enum] = DateTime.Now;
-
Redis.HMSet(RedisExpandKey.Task(taskId), "StartTime", startTime);
Redis.LPush(RedisExpandKey.EnumKey(@enum), taskId);
@@ -148,19 +168,47 @@ namespace VideoAnalysisCore.Common
}
///
- ///
+ /// 写入任务异常
+ ///
+ ///
+ ///
+ ///
+ public static async Task SetTaskErrorMessage(long taskID, string errorMessage)
+ {
+ return await DbScoped.SugarScope.Updateable()
+ .SetColumns(it => it.ErrorMessage == errorMessage)//SetColumns是可以叠加的 写2个就2个字段赋值
+ .Where(it => it.Id == taskID)
+ .ExecuteCommandAsync() == 1;
+ }
+ ///
+ /// 触发
///
///
- ///
+ ///
///
- public static async void TouchChannel(RedisChannelEnum key, string msg, Func action = null)
+ public static async void TouchChannel(RedisChannelEnum key, string taskId, Func action = null)
{
- if (msg is null) return;
- Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-> " + key + " " + msg);
+ if (taskId is null) return;
+ Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-> " + key + " " + taskId);
if (action is not null)
{
- Redis.HMSet(RedisExpandKey.Task(msg), "LastEnum", key);
- await action(msg);
+ var tID = long.Parse(taskId);
+ Redis.HMSet(RedisExpandKey.Task(taskId), "LastEnum", key);
+ await DbScoped.SugarScope.Updateable()
+ .SetColumns(it => it.LastEnum == key)
+ .Where(it => it.Id == tID)
+ .ExecuteCommandAsync();
+ try
+ {
+ await action(taskId);
+ }
+ catch (Exception ex)
+ {
+ //执行任务时出现异常
+ var error = ex.Message + ex.StackTrace;
+ await SetTaskErrorMessage(long.Parse(taskId), error);
+ Redis.HMSet(RedisExpandKey.Task(taskId), "Error", error);
+ }
}
else
{
diff --git a/VideoAnalysisCore/Common/Repository.cs b/VideoAnalysisCore/Common/Repository.cs
index 6b463cf..d1afcce 100644
--- a/VideoAnalysisCore/Common/Repository.cs
+++ b/VideoAnalysisCore/Common/Repository.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using VideoAnalysisCore.Model;
namespace VideoAnalysisCore.Common
{
diff --git a/VideoAnalysisCore/Enum/RedisChannelEnum.cs b/VideoAnalysisCore/Enum/RedisChannelEnum.cs
index 66a2441..f8e1a68 100644
--- a/VideoAnalysisCore/Enum/RedisChannelEnum.cs
+++ b/VideoAnalysisCore/Enum/RedisChannelEnum.cs
@@ -28,6 +28,10 @@
///
/// 回调三方系统
///
- CallBackSystem = 6
+ CallBackSystem = 6,
+ ///
+ /// 结束任务
+ ///
+ EndTask = 6,
}
}
diff --git a/VideoAnalysisCore/Model/Dto/VideoTaskDto.cs b/VideoAnalysisCore/Model/Dto/VideoTaskDto.cs
new file mode 100644
index 0000000..20a98fa
--- /dev/null
+++ b/VideoAnalysisCore/Model/Dto/VideoTaskDto.cs
@@ -0,0 +1,73 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using VideoAnalysisCore.Enum;
+
+namespace VideoAnalysisCore.Model.Dto
+{
+ public class VideoTaskDto
+ {
+ ///
+ /// 任务id
+ /// 视频音频文件地址都使用taskID能获取
+ ///
+ [DisplayName("任务id")]
+ public long Id { get; set; }
+ ///
+ /// ApiKey
+ ///
+ [DisplayName("ApiKey")]
+ public string ApiToken { get; set; } = string.Empty;
+ ///
+ /// 请求来自哪个ip地址
+ ///
+ [DisplayName("请求IP")]
+ public string ComeFrom { get; set; } = string.Empty;
+ ///
+ /// 上一次执行的枚举
+ ///
+ [DisplayName("最后执行")]
+ public RedisChannelEnum LastEnum { get; set; }
+ ///
+ /// 错误信息
+ ///
+ [DisplayName("错误信息")]
+ public string? ErrorMessage { get; set; }
+
+ ///
+ /// 执行进度
+ ///
+ [DisplayName("进度")]
+ public double Progress { get; set; }
+ ///
+ /// 媒体路径
+ ///
+ [DisplayName("媒体路径")]
+ public string MediaUrl { get; set; } = string.Empty;
+ ///
+ /// 自定义值 任务完成后附带通知
+ ///
+ [DisplayName("自定义值")]
+ public string? Tag { get; set; }
+ ///
+ /// 消耗token
+ ///
+ [DisplayName("消耗Token")]
+ public int TotalTokens { get; set; }
+ ///
+ ///回调Api地址
+ ///
+ [DisplayName("回调地址")]
+ public string? CallBackUrl { get; set; }
+ ///
+ /// 创建时间
+ ///
+ [DisplayName("创建时间")]
+ public DateTime CreateTime { get; set; } = DateTime.Now;
+ }
+}
diff --git a/VideoAnalysisCore/Model/VideoTask.cs b/VideoAnalysisCore/Model/VideoTask.cs
index 97bf0a4..f280a4d 100644
--- a/VideoAnalysisCore/Model/VideoTask.cs
+++ b/VideoAnalysisCore/Model/VideoTask.cs
@@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Text.Json;
using VideoAnalysisCore.AICore.SherpaOnnx;
+using VideoAnalysisCore.AICore.Whisper;
using VideoAnalysisCore.Enum;
using Whisper.net;
@@ -53,23 +54,58 @@ namespace VideoAnalysisCore.Model
///
/// 字幕缓存
///
- [SugarColumn(ColumnDataType = "longtext", IsNullable = true)]
- public SegmentData[]? Captions { get; set; }
+ [SugarColumn(ColumnName = "Captions", ColumnDataType = "longtext", IsNullable = true)]
+ public string _Captions { get; set; } = "[]";
+ ///
+ /// 字幕缓存
+ ///
+ [SugarColumn(IsIgnore = true)]
+ public SenseVoiceRes[]? Captions
+ {
+ get=> JsonSerializer.Deserialize(_Captions ?? "[]");
+ set => _Captions = JsonSerializer.Serialize(value);
+ }
///
/// 说话人日志解析缓存
///
- [SugarColumn(ColumnDataType = "longtext", IsNullable = true)]
- public OfflineSpeakerRes[]? Speaker { get; set; }
+ [SugarColumn(ColumnName = "Speaker", ColumnDataType = "longtext", IsNullable = true)]
+ public string _Speaker { get; set; } = "[]";
+ ///
+ /// 说话人日志解析缓存
+ ///
+ [SugarColumn(IsIgnore = true)]
+ public OfflineSpeakerRes[]? Speaker
+ {
+ get => JsonSerializer.Deserialize(_Speaker ?? "[]");
+ set => _Speaker = JsonSerializer.Serialize(value);
+ }
///
/// Chat模型分析缓存
///
- [SugarColumn(ColumnDataType = "longtext", IsNullable = true)]
- public object[]? ChatAnalysis { get; set; }
+ [SugarColumn(ColumnName = "ChatAnalysis", ColumnDataType = "longtext", IsNullable = true)]
+ public string _ChatAnalysis { get; set; } = "[]";
+ ///
+ /// Chat模型分析缓存
+ ///
+ [SugarColumn(IsIgnore = true)]
+ public object[]? ChatAnalysis
+ {
+ get => JsonSerializer.Deserialize