319 lines
12 KiB
C#
319 lines
12 KiB
C#
using FreeRedis;
|
|
using Microsoft.Extensions.DependencyModel;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using SqlSugar;
|
|
using SqlSugar.IOC;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Reflection.PortableExecutable;
|
|
using System.Runtime.Loader;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using UserCenter.Model.Interface;
|
|
using VideoAnalysisCore.AICore.SherpaOnnx;
|
|
using VideoAnalysisCore.Model;
|
|
using VideoAnalysisCore.Model.Dto;
|
|
using VideoAnalysisCore.Model.Enum;
|
|
using VideoAnalysisCore.Model.Interface;
|
|
|
|
namespace VideoAnalysisCore.Common
|
|
{
|
|
|
|
/// <summary>
|
|
/// 程序 公共变量
|
|
/// </summary>
|
|
public static class AppCommon
|
|
{
|
|
/// <summary>
|
|
/// 应用有效程序集
|
|
/// </summary>
|
|
public static readonly IEnumerable<Assembly> Assemblies;
|
|
/// <summary>
|
|
/// 主库数据库表类型
|
|
/// </summary>
|
|
public static readonly IEnumerable<Type> DbMatserType;
|
|
public static readonly IEnumerable<Type> KnowsType;
|
|
static AppCommon()
|
|
{
|
|
try
|
|
{
|
|
Assemblies = ExpandFunction.GetAssemblies();
|
|
var assembliesType = Assemblies.Where(s => s.FullName.Contains("VideoAnalysis")).SelectMany(s => s.ExportedTypes
|
|
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false)));
|
|
DbMatserType = assembliesType
|
|
.Where(u => u.GetInterfaces().Contains(typeof(IDB)));
|
|
}
|
|
catch
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 程序配置
|
|
/// </summary>
|
|
public static AppConfig Config = new AppConfig();
|
|
/// <summary>
|
|
/// ServiceProvider
|
|
/// </summary>
|
|
public static IServiceProvider? Services;
|
|
/// <summary>
|
|
/// 文件下载路径
|
|
/// </summary>
|
|
public static string TaskCachedFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TaskCachedFile");
|
|
|
|
/// <summary>
|
|
/// 模型地址
|
|
/// </summary>
|
|
public static string AIModelFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AICore", "_Static");
|
|
|
|
|
|
/// <summary>
|
|
/// 获取视频路径
|
|
/// </summary>
|
|
/// <param name="tid"></param>
|
|
/// <returns></returns>
|
|
public static string GetVideoPath(string tid) => $"./video/{tid}/{tid}.mp4";
|
|
}
|
|
|
|
/// <summary>
|
|
/// 拓展函数
|
|
/// </summary>
|
|
public static class ExpandFunction
|
|
{
|
|
|
|
static Dictionary<string, string> FormulaData;
|
|
static string FormulaDataKey;
|
|
/// <summary>
|
|
/// 帧文件名称
|
|
/// </summary>
|
|
public static string FrameName = "frame_";
|
|
|
|
/// <summary>
|
|
/// 删除 AI分析任务的缓存文件
|
|
/// </summary>
|
|
/// <param name="taskId"></param>
|
|
/// <returns></returns>
|
|
public static bool DeleteTaskFile(long? taskId)
|
|
{
|
|
if(taskId is null || taskId == 0) return false;
|
|
var path = taskId.ToString().LocalPath();
|
|
if (!string.IsNullOrEmpty(path) && Directory.Exists(path))
|
|
{
|
|
try
|
|
{
|
|
Directory.Delete(path, true);
|
|
Console.WriteLine($"已删除缓存文件: {taskId}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"删除缓存文件 {taskId} 时出错: {ex.Message}");
|
|
}
|
|
}
|
|
else return false;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 对象转化为JSON字符串
|
|
/// </summary>
|
|
/// <param name="o">拓展对象</param>
|
|
/// <param name="WriteIndented">美化输出?</param>
|
|
/// <returns></returns>
|
|
public static string ToJson(this object o, bool WriteIndented = false)
|
|
{
|
|
var jsonOptions = new JsonSerializerOptions
|
|
{
|
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
|
WriteIndented = WriteIndented // 如果需要美化输出
|
|
};
|
|
return JsonSerializer.Serialize(o, jsonOptions);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取视频帧文件资源路径
|
|
/// </summary>
|
|
/// <param name="task"></param>
|
|
/// <param name="fTime">帧文件 对应的时间轴</param>
|
|
/// <returns></returns>
|
|
public static string FramePath(this VideoTask task, int fTime)
|
|
{
|
|
return Path.Combine(task.Id.ToString().LocalPath(), $"{FrameName}{(fTime / 5).ToString().PadLeft(3, '0')}.jpg");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 处理数学公式
|
|
/// </summary>
|
|
/// <param name="f"></param>
|
|
/// <returns></returns>
|
|
public static string HandleFormula(string f)
|
|
{
|
|
if (FormulaData is null)
|
|
{
|
|
var hotwords = JsonSerializer.Deserialize<HotwordMode[]>(File.ReadAllText(Path.Combine(AppCommon.AIModelFile, "Hotwords.json")));
|
|
foreach (var item in hotwords.OrderByDescending(s => s.key.Count()))
|
|
foreach (var key in item.v)
|
|
FormulaData.Add(key, item.key);
|
|
}
|
|
if (string.IsNullOrEmpty(FormulaDataKey))
|
|
FormulaDataKey = string.Join("|", FormulaData.Keys.Count);
|
|
if (string.IsNullOrEmpty(f))
|
|
return f;
|
|
return Regex.Replace(f, FormulaDataKey,
|
|
match =>
|
|
FormulaData[match.Value]
|
|
);
|
|
}
|
|
/// <summary>
|
|
/// 获取应用有效程序集
|
|
/// </summary>
|
|
/// <returns>IEnumerable</returns>
|
|
public static List<Assembly> GetAssemblies()
|
|
{
|
|
// 获取当前解决方案的所有程序集
|
|
var assembliesStr = DependencyContext.Default.RuntimeLibraries
|
|
.Where(u => !u.Name.StartsWith(nameof(Microsoft))
|
|
&& !u.Name.StartsWith(nameof(System))
|
|
&& !u.Name.StartsWith("netstandard")
|
|
&& (u.Type == "project"));
|
|
|
|
var assemblies = assembliesStr.Select(a => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(a.Name))).ToList();
|
|
var assemblies1 = Assembly.GetEntryAssembly().GetReferencedAssemblies().Where(x => x.Name.StartsWith("App.") || x.Name.StartsWith("UserCenter."))
|
|
.Select(a => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(a.Name))).ToList();
|
|
foreach (var item in assemblies1)
|
|
{
|
|
if (!assemblies.Contains(item))
|
|
assemblies.Add(item);
|
|
}
|
|
return assemblies;
|
|
}
|
|
/// <summary>
|
|
/// 获取Task处理后的 说话人字幕
|
|
/// </summary>
|
|
public static TotalCaptionsDto GetSpeakerCaptions(SenseVoiceRes[] captionsArr, OfflineSpeakerRes[]? speakerArr = null)
|
|
{
|
|
if (captionsArr is null || captionsArr.Length == 0)
|
|
//|| speakerArr is null || speakerArr.Length == 0)
|
|
throw new Exception("音频解析数据异常");
|
|
// 教师说话人Id
|
|
var techerId = speakerArr is null || !speakerArr.Any()
|
|
? 0
|
|
: speakerArr.GroupBy(s => s.SpeakerIndex).Select(s => (s.Key, s.Sum(x => x.Total)))
|
|
.OrderByDescending(s => s.Item2).First().Key;
|
|
var teacherSpeaking = 0f;
|
|
var studentSpeaking = 0f;
|
|
var results = new Dictionary<SenseVoiceRes, List<int>>();
|
|
var ss = new List<int> { 1 };
|
|
if (speakerArr is null || speakerArr.Count() == 0)
|
|
{
|
|
results = captionsArr.ToDictionary(s => s, s => ss);
|
|
}
|
|
else
|
|
{
|
|
foreach (var segment in captionsArr)
|
|
{
|
|
var spList = new List<int>();
|
|
foreach (var speakerRes in speakerArr)
|
|
{
|
|
if (speakerRes.Start > segment.End)
|
|
break;
|
|
if (segment.Start <= speakerRes.End
|
|
&& segment.End >= speakerRes.Start)
|
|
{
|
|
if (speakerRes.SpeakerIndex == techerId)
|
|
teacherSpeaking += speakerRes.Total;
|
|
else
|
|
studentSpeaking += speakerRes.Total;
|
|
spList.Add(speakerRes.SpeakerIndex);
|
|
}
|
|
}
|
|
var sp = spList.Distinct().ToList();
|
|
if (sp.Count > 0)
|
|
results.Add(segment, sp);
|
|
}
|
|
|
|
}
|
|
//拼接 提示词字幕源
|
|
var stringBuilder = new StringBuilder();
|
|
foreach (var item in results)
|
|
{
|
|
//stringBuilder.Append(item.Value.First());
|
|
//stringBuilder.Append(":");
|
|
stringBuilder.Append((int)item.Key.Start);
|
|
stringBuilder.Append(":");
|
|
//stringBuilder.Append((int)item.Key.End);
|
|
//stringBuilder.Append(":");
|
|
stringBuilder.Append(item.Key.Text);
|
|
stringBuilder.Append("|");
|
|
}
|
|
return new TotalCaptionsDto
|
|
{
|
|
StudentSpeaking = (decimal)studentSpeaking,
|
|
TeacherSpeaking = (decimal)teacherSpeaking,
|
|
Captions = stringBuilder.ToString(),
|
|
TimeBase = results.Select(s => new TimeBase()
|
|
{
|
|
Start = s.Key.Start,
|
|
End = s.Key.End,
|
|
Content = s.Key.Text,
|
|
TimeBaseType = s.Value.Count == 1 && s.Value.First() == techerId
|
|
? TimeBaseTypeEnum.教师讲授
|
|
: TimeBaseTypeEnum.互动交流
|
|
})
|
|
};
|
|
}
|
|
/// <summary>
|
|
/// 获取下一个枚举值
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="current"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
public static T? NextEnum<T>(this T current) where T : struct, Enum
|
|
{
|
|
if (!typeof(T).IsEnum)
|
|
throw new ArgumentException("传入类型不是枚举");
|
|
T[] values = (T[])Enum.GetValues(typeof(T));
|
|
int currentIndex = Array.IndexOf(values, current);
|
|
if (currentIndex == values.Length - 1)
|
|
return null;
|
|
int nextIndex = (currentIndex + 1) % values.Length;
|
|
return values[nextIndex];
|
|
}
|
|
/// <summary>
|
|
/// 转化枚举
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <returns></returns>
|
|
public static T? ToEnum<T>(this object value) where T : struct, Enum
|
|
{
|
|
try
|
|
{
|
|
if (value is null || string.IsNullOrEmpty(value.ToString()))
|
|
return null;
|
|
if (Enum.TryParse<T>(value.ToString(), true, out var result) && Enum.IsDefined(typeof(T), result))
|
|
return result;
|
|
return null;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 转化本地缓存目录
|
|
/// </summary>
|
|
/// <param name="taskId">任务id</param>
|
|
/// <returns></returns>
|
|
public static string LocalPath(this string taskId)
|
|
{
|
|
return Path.Combine(AppCommon.TaskCachedFile, taskId);
|
|
}
|
|
|
|
}
|
|
|
|
}
|