using FFmpeg.NET.Services; 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.Json.Serialization; using System.Text.RegularExpressions; using System.Threading.Tasks; using UserCenter.Model; using VideoAnalysisCore.AICore.SherpaOnnx; using VideoAnalysisCore.Model; using VideoAnalysisCore.Model.Dto; using VideoAnalysisCore.Model.Enum; using VideoAnalysisCore.Model.Interface; namespace VideoAnalysisCore.Common { /// /// 程序 公共变量 /// public static class AppCommon { /// /// 应用有效程序集 /// public static readonly IEnumerable Assemblies; /// /// 主库数据库表类型 /// public static readonly IEnumerable DbMatserType; public static readonly IEnumerable 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))); EnumType = Assemblies .Where(s => s.FullName.Contains("Model") || s.FullName.Contains("Core")) .SelectMany(s => s.GetTypes().Where(x => x.IsEnum)) .DistinctBy(s => s.Name) .ToDictionary(s => s.Name, s => s); } catch { throw; } } /// /// 程序配置 /// public static AppConfig Config = new AppConfig(); public static readonly Dictionary EnumType; /// /// ServiceProvider /// public static IServiceProvider? Services; /// /// 文件下载路径 /// public static string TaskCachedFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TaskCachedFile"); public static string WebUIFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WebUI", "dist"); /// /// 模型地址 /// public static string AIModelFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AICore", "_Static"); /// /// 获取视频路径 /// /// /// public static string GetVideoPath(string tid) => $"./video/{tid}/task.mp4"; /// /// 获取视频PPT路径 /// /// /// public static string GetVideoPPTPath(string tid) => $"./video/{tid}/ppt.mp4"; } /// /// 拓展函数 /// public static class ExpandFunction { static Dictionary FormulaData; static string FormulaDataKey; /// /// 帧文件名称 /// public static string FrameName = "frame_"; /// /// 删除 AI分析任务视频/PPT的缓存文件 /// /// /// public static async Task DeleteTaskFileAsync(long? taskId,RedisManager redisManager) { if (taskId is null || taskId == 0) return false; var path = taskId.ToString().LocalPath(); string[] filesToDelete = { "ppt.mp4", "task.mp4" }; foreach (string fileName in filesToDelete) { string filePath = Path.Combine(path, fileName); if (File.Exists(filePath)) { try { File.Delete(filePath); await redisManager.AddTaskLog(taskId, $"已成功删除文件: {filePath}"); } catch (Exception ex) { await redisManager.AddTaskLog(taskId, $"删除文件 {filePath} 时出错: {ex.Message}"); } } //else //{ // await redisManager.AddTaskLog(chatReq.taskId, $"文件不存在,跳过删除: {filePath}"); //} } return true; } /// /// 删除 AI分析任务的缓存文件 /// /// /// public static async Task DeleteTaskAllFileAsync(long? taskId, RedisManager redisManager) { 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); await redisManager.AddTaskLog(taskId, $"已清理所有缓存文件: {taskId}"); } catch (Exception ex) { await redisManager.AddTaskLog(taskId, $"删除缓存文件 {taskId} 时出错: {ex.Message}"); } } return true; } /// /// 对象转化为JSON字符串 /// /// 拓展对象 /// 美化输出? /// public static string ToJson(this object o, bool WriteIndented = false) { var jsonOptions = new JsonSerializerOptions { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // 忽略所有 null 值属性 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, WriteIndented = WriteIndented // 如果需要美化输出 }; return JsonSerializer.Serialize(o, jsonOptions); } /// /// 获取视频帧文件资源路径 /// /// /// 帧文件 对应的时间轴 /// public static string FramePath(this VideoTask task, int fTime) { return Path.Combine(task.Id.ToString().LocalPath(), $"{FrameName}{(fTime / 5).ToString().PadLeft(3, '0')}.jpg"); } /// /// 处理数学公式 /// /// /// public static string HandleFormula(string f) { if (FormulaData is null) { var hotwords = JsonSerializer.Deserialize(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] ); } /// /// 获取应用有效程序集 /// /// IEnumerable public static List 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 assembly = Assembly.Load("UserCenter.Model"); assemblies.Add(assembly); return assemblies; } /// /// 获取Task处理后的 说话人字幕 /// 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>(); var ss = new List { 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(); 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 && !string.IsNullOrWhiteSpace(segment.Text)) 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.互动交流 }) }; } /// /// 获取下一个枚举值 /// /// /// /// /// public static T? NextEnum(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]; } /// /// 转化枚举 /// /// /// public static T? ToEnum(this object value) where T : struct, Enum { try { if (value is null || string.IsNullOrEmpty(value.ToString())) return null; if (Enum.TryParse(value.ToString(), true, out var result) && Enum.IsDefined(typeof(T), result)) return result; return null; } catch (Exception) { return null; } } /// /// 转化本地缓存目录 /// /// 任务id /// public static string LocalPath(this string taskId) { return Path.Combine(AppCommon.TaskCachedFile, taskId); } } }