Compare commits

..

2 Commits

Author SHA1 Message Date
小肥羊 0de07e733a 新增 本地运行流程 2026-02-12 15:53:14 +08:00
小肥羊 abca6480a1 删除 过时的打包配置AI模型 2026-02-12 11:38:59 +08:00
11 changed files with 224 additions and 105 deletions

1
.gitignore vendored
View File

@ -368,3 +368,4 @@ VideoAnalysisCore/AICore/_Static/
VideoAnalysis/WebUI/node_modules/
VideoAnalysis/WebUI/dist/
VideoAnalysis/WebUI/.vscode/
/VideoAnalysis/device_id.config

View File

@ -1,6 +1,8 @@

using Learn.VideoAnalysis.API.Expand;
using Mapster;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.OpenApi.Models;
using System.Text.Encodings.Web;
using System.Text.Json;
@ -23,29 +25,29 @@ namespace Learn.VideoAnalysis.API
builder.Services.AddControllers(options =>
{
// 全局模型赋值默认值 和 统一返回格式处理
// 全局模型赋值默认值 和 统一返回格式处理
options.Filters.Add<HttpLogAttribute>();
}).AddJsonOptions(options =>
{
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);//中文转换时不使用Unicode
//options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;// 默认小驼峰 null 大驼峰
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);//中文转换时不使用Unicode
//options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;// 默认小驼峰 null 大驼峰
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
var file = Path.Combine(AppContext.BaseDirectory, "Learn.VideoAnalysis.API.xml"); // xml文档绝对路径
var file1 = Path.Combine(AppContext.BaseDirectory, "VideoAnalysisCore.xml"); // xml文档绝对路径
c.IncludeXmlComments(file, true); // true : 显示控制器层注释
c.IncludeXmlComments(file1, true); // true : 显示控制器层注释
c.OrderActionsBy(o => o.RelativePath); // 对action的名称进行排序如果有多个就可以看见效果了。
var file = Path.Combine(AppContext.BaseDirectory, "Learn.VideoAnalysis.API.xml"); // xml文档绝对路径
var file1 = Path.Combine(AppContext.BaseDirectory, "VideoAnalysisCore.xml"); // xml文档绝对路径
c.IncludeXmlComments(file, true); // true : 显示控制器层注释
c.IncludeXmlComments(file1, true); // true : 显示控制器层注释
c.OrderActionsBy(o => o.RelativePath); // 对action的名称进行排序如果有多个就可以看见效果了。
});
builder.Services.AddMapster();
//初始化 插件
builder.Configuration.AddAppConfig(args);
//初始化 插件
builder.AddAppConfig(args);
builder.Services.AddHttpClient();
builder.Services.AddSqlSugarExpand();
@ -75,10 +77,12 @@ namespace Learn.VideoAnalysis.API
app.MapControllers();
//自定义 应用
//自定义 应用
app.UseCorsExpand();
app.UseSqlSugarExpand();
app.UseCoravelExpand();
// 注册启动后的回调
app.UseServiceSystem();
app.Run();

View File

@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "10.0.3",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}

View File

@ -1,4 +1,4 @@
using VideoAnalysisCore.Common;
using VideoAnalysisCore.Common;
using Microsoft.OpenApi.Models;
using VideoAnalysisCore.AICore.SherpaOnnx;
using Mapster;
@ -16,6 +16,8 @@ using VideoAnalysisCore.AICore.FFMPGE;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using System.Text.Json;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Hosting.Server;
@ -25,42 +27,46 @@ namespace Learn.VideoAnalysis
{
public static void Main(string[] args)
{
//交互式环境选择函数
AppConfigExpand.SelectEnvironment(ref args);
var builder = WebApplication.CreateBuilder(args);
//设置接口请求体最大100m
builder.WebHost.ConfigureKestrel(serverOptions => {
//设置接口请求体最大100m
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxRequestBodySize = 100_000_000; // 100MB
});
builder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.ClearProviders(); // 清除默认的日志提供程序
loggingBuilder.AddConsole(); // 添加控制台日志提供程序
loggingBuilder.SetMinimumLevel(LogLevel.Warning); // 设置最小日志级别为 Warning
loggingBuilder.ClearProviders(); // 清除默认的日志提供程序
loggingBuilder.AddConsole(); // 添加控制台日志提供程序
loggingBuilder.SetMinimumLevel(LogLevel.Warning); // 设置最小日志级别为 Warning
});
//绑定 appsetting 配置
builder.Configuration.AddAppConfig(args);
//初始化 插件
//绑定 appsetting 配置
builder.AddAppConfig(args);
//初始化 插件
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerExpand("AI视频分析");
builder.Services.AddSwaggerExpand("AI视频分析");
builder.Services.AddPermissionAuthentication();
builder.Services.AddSqlSugarExpand();
builder.Services.AddRedisExpand();
//工作流
//工作流
builder.Services.AddSimpleTexOcrClient();
builder.Services.AddDownloadFileExpand();
builder.Services.AddFFMPGEExpand();
builder.Services.AddAlibabaCloudVod();
builder.Services.AddAliyunOSS();
//语音转写
//语音转写
builder.Services.AddSenseVoiceExpand();
builder.Services.AddFunASRNanoExpand();
builder.Services.AddSherpaVadExpand();
//builder.Services.AddSpeakerAI();
//定时任务
//定时任务
builder.Services.AddCoravel();
//异常过滤器
//异常过滤器
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(ExceptionFilter));
@ -68,8 +74,8 @@ namespace Learn.VideoAnalysis
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);//中文转换时不使用Unicode
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;// 默认小驼峰 null 大驼峰
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);//中文转换时不使用Unicode
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;// 默认小驼峰 null 大驼峰
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
@ -100,15 +106,15 @@ namespace Learn.VideoAnalysis
AppCommon.Services = app.Services;
app.UseMiddleware<BasicAuthMiddleware>("Swagger");
// Configure the HTTP request pipeline.
//开启redis队列服务
//开启redis队列服务
_ = app.Services.GetRequiredService<RedisInit>();
app.UseSwagger();
app.UseSwaggerUI();
app.UseSwaggerUI();
app.UseExceptionHandler("/Error");
//添加wwwroot 静态目录
//添加wwwroot 静态目录
app.UseStaticFiles();
//添加 自定义 静态目录
//添加 自定义 静态目录
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(AppCommon.TaskCachedFile),
@ -123,10 +129,11 @@ namespace Learn.VideoAnalysis
app.MapControllers();
//自定义 应用
//自定义 应用
app.UseCorsExpand();
app.UseSqlSugarExpand();
app.UseCoravelExpand();
app.UseServiceSystem();
app.Run();

View File

@ -1,31 +1,15 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:9624",
"sslPort": 0
}
},
"profiles": {
"http:5238": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "ui/index.html",
"applicationUrl": "http://*:5238",
"applicationUrl": "http://*:7532",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -1,4 +1,11 @@
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:7532"
}
}
},
"Logging": {
"LogLevel": {
"Default": "Warning",

View File

@ -16,7 +16,7 @@ namespace VideoAnalysisCore.Common
/// <summary>
/// 程序ID
/// </summary>
public string ID { get; set; } = string.Empty;
public ushort ID { get; set; } = 0;
/// <summary>
/// Admin
/// </summary>

View File

@ -1,6 +1,8 @@
using Coravel;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using VideoAnalysisCore.Common;
using VideoAnalysisCore.Job;
@ -8,16 +10,45 @@ namespace VideoAnalysisCore.Common.Expand
{
public static class AppConfigExpand
{
public static void AddAppConfig(this IConfigurationManager cm, string[] args)
public static ushort GenerateIdFromIpPower()
{
var mac = NetworkInterface.GetAllNetworkInterfaces()
.Where(n => n.OperationalStatus == OperationalStatus.Up && n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
.Select(n => n.GetPhysicalAddress())
.FirstOrDefault(addr => addr.GetAddressBytes().Length > 0);
if (mac == null) return 0;
byte[] bytes = mac.GetAddressBytes();
// 取 MAC 地址的最后两个字节生成 ushort
return (ushort)((bytes[^2] << 8) | bytes[^1]);
}
public static ushort GetPersistentDeviceId()
{
string path = "device_id.config";
if (File.Exists(path))
{
return ushort.Parse(File.ReadAllText(path));
}
// 首次启动,生成 ID (比如用你之前的 IP 幂运算法)
ushort newId = GenerateIdFromIpPower();
File.WriteAllText(path, newId.ToString());
return newId;
}
public static void AddAppConfig(this IHostApplicationBuilder hb, string[] args)
{
Console.WriteLine($"{DateTime.Now}=>初始化 AppConfig");
cm.GetSection("AppConfig").Bind(AppCommon.Config);
hb.Configuration.GetSection("AppConfig").Bind(AppCommon.Config);
AppCommon.Config.ID = GetPersistentDeviceId();
var argList = args.ToList();
var eArgs = Environment.GetEnvironmentVariable("va_args");
if (!string.IsNullOrEmpty(eArgs))
argList.AddRange(eArgs.Split(","));
Console.WriteLine("===========================================");
Console.WriteLine($"设备ID{AppCommon.Config.ID}");
Console.WriteLine($"当前环境: {hb.Environment.EnvironmentName}");
Console.WriteLine("启动参数如下:");
Console.WriteLine(string.Join(',', args));
Console.WriteLine("===========================================");
@ -25,5 +56,51 @@ namespace VideoAnalysisCore.Common.Expand
if (args.Contains("IS_Server"))
AppCommon.Config.TaskSetting.IS_Server = true;
}
/// <summary>
/// 交互式环境选择函数
/// </summary>
public static void SelectEnvironment(ref string[] args)
{
// 如果没有控制台(如在某些后台服务或重定向中),则跳过交互
if (Console.IsInputRedirected) return;
Console.Title = "ASP.NET Core 环境选择器";
Console.WriteLine("==================================================");
Console.WriteLine(" 请选择程序运行环境 (直接回车默认使用开发环境)");
Console.WriteLine("==================================================");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(" [1] 生产环境 (Production)");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(" [2] 测试环境 (Staging)");
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(" [3] 开发环境 (Development)");
Console.ResetColor();
Console.WriteLine("==================================================");
Console.Write(" 请输入编号 [1/2/3] (默认 3): ");
// 获取输入
string? input = Console.ReadLine()?.Trim();
// 映射逻辑
string selectedEnv = input switch
{
"1" => Environments.Production,
"2" => Environments.Staging,
"3" => Environments.Development,
_ => Environments.Development // 输入错误或直接回车,默认走开发环境
};
// 核心:在 Build 之前覆盖环境名
args = args.Concat( new[] { "--environment", selectedEnv }).ToArray();
// 成功反馈
Console.Clear();
Console.BackgroundColor = ConsoleColor.DarkBlue;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine($" 已确认切换至: {selectedEnv} 模式 ");
Console.ResetColor();
Console.WriteLine();
}
}
}

View File

@ -0,0 +1,77 @@
using Coravel;
using Coravel.Scheduling.Schedule;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using VideoAnalysisCore.Job;
using Microsoft.Extensions.Hosting;
using VideoAnalysisCore.Common;
using System.Diagnostics;
namespace VideoAnalysisCore.Common.Expand
{
public static class ServiceSystemExpand
{
/// <summary>
/// 系统服务
/// </summary>
/// <param name="app1"></param>
public static void UseServiceSystem(this IHost app1)
{
var app = app1.Services;
// 注册启动后的回调
app.GetRequiredService<IHostApplicationLifetime>()
.ApplicationStarted.Register(() =>
{
var server = AppCommon.Services.GetRequiredService<IServer>();
var addressFeature = server.Features.Get<IServerAddressesFeature>();
Console.WriteLine("===========================================");
Console.WriteLine($"Kestrel 监听地址: ");
foreach (var address in addressFeature.Addresses)
{
Console.WriteLine($"{address}");
}
Console.WriteLine("===========================================");
// 将 "*" 替换为 "localhost" 或 "127.0.0.1",因为 Uri 类不直接支持带有通配符的 Host
var normalizedAddress = addressFeature.Addresses.First()
.Replace("*", "127.0.0.1")
.Replace("+", "127.0.0.1");
var uri = new Uri(normalizedAddress);
int port = uri.Port; // 这里的 port 就是你要的数字 (int)
OpenBrowser($"http://localhost:{uri.Port}/ui/index.html");
});
}
// 跨平台打开浏览器的方法
static void OpenBrowser(string url)
{
try
{
if (OperatingSystem.IsWindows())
{
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
}
else if (OperatingSystem.IsLinux())
{
Process.Start("xdg-open", url);
}
else if (OperatingSystem.IsMacOS())
{
Process.Start("open", url);
}
}
catch (Exception ex)
{
Console.WriteLine($"无法自动打开浏览器: {ex.Message}");
}
}
}
}

View File

@ -28,7 +28,7 @@ namespace VideoAnalysisCore.Common.Expand
HttpServiceCollectionExtensions.AddHttpContextAccessor(services);
Console.WriteLine($"{DateTime.Now}=>初始化 YitId雪花ID");
var options = new IdGeneratorOptions(ushort.Parse(AppCommon.Config.ID));
var options = new IdGeneratorOptions((ushort)(AppCommon.Config.ID % 63));
YitIdHelper.SetIdGenerator(options);
#region SqlSugar注入

View File

@ -9,53 +9,8 @@
<ItemGroup>
<None Remove="AICore\_Static\ffmpeg.exe" />
<None Remove="AICore\_Static\Hotwords.json" />
<None Remove="AICore\_Static\itn_subject_sx.fst" />
<None Remove="AICore\_Static\sherpa-onnx-fst.py" />
<None Remove="AICore\_Static\sherpa-onnx-pyannote-segmentation-3-0\model.int8.onnx" />
<None Remove="AICore\_Static\sherpa-onnx-pyannote-segmentation-3-0\model.onnx" />
<None Remove="AICore\_Static\sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20\bpe.model" />
<None Remove="AICore\_Static\sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20\decoder-epoch-99-avg-1.onnx" />
<None Remove="AICore\_Static\sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20\encoder-epoch-99-avg-1.onnx" />
<None Remove="AICore\_Static\sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20\joiner-epoch-99-avg-1.onnx" />
<None Remove="AICore\_Static\wespeaker\wespeaker_zh_cnceleb_resnet34_LM.onnx" />
</ItemGroup>
<ItemGroup>
<Content Include="AICore\_Static\ffmpeg.exe">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\Hotwords.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\itn_subject_sx.fst">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\sherpa-onnx-fst.py">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\sherpa-onnx-pyannote-segmentation-3-0\model.int8.onnx">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\sherpa-onnx-pyannote-segmentation-3-0\model.onnx">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20\bpe.model">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20\decoder-epoch-99-avg-1.onnx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20\encoder-epoch-99-avg-1.onnx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20\joiner-epoch-99-avg-1.onnx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="AICore\_Static\wespeaker\wespeaker_zh_cnceleb_resnet34_LM.onnx">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AlibabaCloud.SDK.Vod20170321" Version="3.6.1" />
@ -80,10 +35,4 @@
<PackageReference Include="Whisper.net.Runtime" Version="1.5.0" />
<PackageReference Include="xFFmpeg.NET" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="AICore\_Static\sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20\decoder-epoch-99-avg-1.int8.onnx">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>