This commit is contained in:
owenchen 2026-05-28 15:05:46 +08:00
parent e60118eb53
commit 2e990e687f
38 changed files with 441 additions and 237 deletions

View File

@ -307,6 +307,7 @@ namespace CloudBuilder.AI.Gui
tableLayoutPanel6.SetRowSpan(sentenceGv, 2); tableLayoutPanel6.SetRowSpan(sentenceGv, 2);
sentenceGv.Size = new Size(687, 349); sentenceGv.Size = new Size(687, 349);
sentenceGv.TabIndex = 4; sentenceGv.TabIndex = 4;
sentenceGv.CellDoubleClick += sentenceGv_CellDoubleClick;
// //
// sentenceIndexCol // sentenceIndexCol
// //

View File

@ -4,6 +4,7 @@ using CloudBuilder.AI.Gui.Request;
using CloudBuilder.AI.Linkup; using CloudBuilder.AI.Linkup;
using CloudBuilder.AI.Policy; using CloudBuilder.AI.Policy;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using CloudBuilder.Core.Request;
using CloudBuilder.Core.Service; using CloudBuilder.Core.Service;
using CloudBuilder.Gui; using CloudBuilder.Gui;
using CloudBuilder.Request.AI; using CloudBuilder.Request.AI;
@ -15,10 +16,13 @@ using CloudBuilder.Security.Policy;
using DocumentFormat.OpenXml.Wordprocessing; using DocumentFormat.OpenXml.Wordprocessing;
using Krypton.Toolkit; using Krypton.Toolkit;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using NAudio.Wave;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Data; using System.Data;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Security.Permissions; using System.Security.Permissions;
@ -106,7 +110,7 @@ namespace CloudBuilder.AI.Gui
private void InitGridViewPagination() private void InitGridViewPagination()
{ {
searchCriteria = new AiChapterSearchCriteria(); searchCriteria = new AiChapterSearchCriteria();
gridViewPagination.SetLimit(searchCriteria.limit); gridViewPagination.SetLimit(searchCriteria.limit);
gridViewPagination.OnPaginationChanged += SearchChapter; gridViewPagination.OnPaginationChanged += SearchChapter;
} }
@ -260,7 +264,7 @@ namespace CloudBuilder.AI.Gui
guiOperateHelper.EnableControls(false, contentPanel); guiOperateHelper.EnableControls(false, contentPanel);
//查詢有記錄時,修改等控件有效 //查詢有記錄時,修改等控件有效
if (aiBookEntity != null) if (aiBookEntity != null)
guiOperateHelper.EnableControls(true, bottomPanel, processPanel,gridViewPagination); guiOperateHelper.EnableControls(true, bottomPanel, processPanel, gridViewPagination);
else else
guiOperateHelper.EnableControls(false, bottomPanel, processPanel, gridViewPagination); guiOperateHelper.EnableControls(false, bottomPanel, processPanel, gridViewPagination);
} }
@ -269,7 +273,7 @@ namespace CloudBuilder.AI.Gui
savePanel.Visible = true; savePanel.Visible = true;
operationPanel.Visible = false; operationPanel.Visible = false;
guiOperateHelper.EnableControls(true, bottomPanel, contentPanel); guiOperateHelper.EnableControls(true, bottomPanel, contentPanel);
guiOperateHelper.EnableControls(false, topPanel, processPanel, gridViewPagination); guiOperateHelper.EnableControls(false, topPanel, processPanel, gridViewPagination);
@ -802,5 +806,114 @@ namespace CloudBuilder.AI.Gui
ShowMessageBox.ShowError(ex.Message); ShowMessageBox.ShowError(ex.Message);
} }
} }
private void sentenceGv_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
try
{
if (sentenceGv.RowCount <= 0 || sentenceGv.SelectedCells.Count <= 0)
{
ShowMessageBox.ShowInfo(securityMessagePolicy.ComposeMessage(SecurityMessagePolicy.GUI_WS_RECORD_NOT_FOUND));
return;
}
AiSentenceViewEntity sentence = sentenceGv.GetRowData<AiSentenceViewEntity>(sentenceGv.SelectedCells[0].RowIndex);
using (Stream audioStream = ServiceHelper.GetService<IServiceRequest>()!.PostStreamAsync("/AiBook/GetTtsStream?text=" + sentence.Content, null, string.Empty).GetAwaiter().GetResult())
{
PlayMp3Stream(audioStream);
}
}
catch (ValidatedException ex)
{
ShowMessageBox.ShowInfo(ex.Message);
}
catch (ServiceErrorException ex)
{
ShowMessageBox.ShowError(ex.Message);
}
catch (Exception ex)
{
ShowMessageBox.ShowError(ex.Message);
}
}
public void PlayMp3Stream(Stream audioStream)
{
string tempFile = Path.Combine(Path.GetTempPath(), "temp_audio.mp3");
// 保存流到临时文件
using (var fs = new FileStream(tempFile, FileMode.Create))
{
audioStream.Position = 0;
audioStream.CopyTo(fs);
}
// 直接调用你已经能播放的方法!!!
//PlayAudioFile(tempFile);
Task.Run(() =>
{
try
{
PlayAudioFile(tempFile); // 后台播放
}
finally
{
File.Delete(tempFile); // 播完删除
}
});
}
public static void PlayAudioFile(string filePath)
{
// 必用这种写法,绝对不会提前释放
var reader = new AudioFileReader(filePath);
var waveOut = new WaveOutEvent();
waveOut.Init(reader);
waveOut.Play();
// 同步等待播放完成(这是最稳、最不会出错的)
while (waveOut.PlaybackState == NAudio.Wave.PlaybackState.Playing)
{
System.Threading.Thread.Sleep(100);
}
// 播放完再释放
waveOut.Dispose();
reader.Dispose();
}
public void DownloadFile(int fileId, string saveFilePath)
{
try
{
ServiceHelper.GetService<IServiceRequest>()!.DownloadFile("/DmsFile/DownloadFile?fileId=" + fileId, saveFilePath, null, OpenFile);
}
catch (ValidatedException ex)
{
ShowMessageBox.ShowInfo(ex.Message);
}
catch (ServiceErrorException ex)
{
ShowMessageBox.ShowError(ex.Message);
}
catch (Exception ex)
{
ShowMessageBox.ShowError(ex.Message);
}
}
private void OpenFile(string fileFullName)
{
Process.Start(new ProcessStartInfo
{
FileName = fileFullName,
UseShellExecute = true // 使用Windows Shell执行
});
}
} }
} }

View File

@ -48,6 +48,8 @@
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="ClosedXML" Version="0.102.1" /> <PackageReference Include="ClosedXML" Version="0.102.1" />
<PackageReference Include="NAudio" Version="2.3.0" />
<PackageReference Include="NAudio.Core" Version="2.3.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.0" /> <PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,4 +1,5 @@
using CloudBuilder.AI.Data; using CliWrap;
using CloudBuilder.AI.Data;
using CloudBuilder.AI.Entity; using CloudBuilder.AI.Entity;
using CloudBuilder.AI.Policy; using CloudBuilder.AI.Policy;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
@ -14,8 +15,10 @@ using CloudBuilder.Security.Utility;
using DocumentFormat.OpenXml.ExtendedProperties; using DocumentFormat.OpenXml.ExtendedProperties;
using DocumentFormat.OpenXml.Presentation; using DocumentFormat.OpenXml.Presentation;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.IO;
using System.Text; using System.Text;
using System.Transactions; using System.Transactions;
namespace CloudBuilder.AI.Service namespace CloudBuilder.AI.Service
@ -291,7 +294,7 @@ namespace CloudBuilder.AI.Service
foreach (var name in names) foreach (var name in names)
{ {
entity = new AiChapterPersonEntity(); entity = new AiChapterPersonEntity();
entity.Guid= Guid.NewGuid().ToString(); entity.Guid = Guid.NewGuid().ToString();
entity.ChapterGuid = paragraph.ChapterGuid; entity.ChapterGuid = paragraph.ChapterGuid;
entity.ParagraphGuid = paragraph.Guid; entity.ParagraphGuid = paragraph.Guid;
entity.ParagraphIndex = paragraph.ParagraphIndex; entity.ParagraphIndex = paragraph.ParagraphIndex;
@ -304,5 +307,76 @@ namespace CloudBuilder.AI.Service
if (aiChapterPersonEntities != null && aiChapterPersonEntities.Count() > 0) if (aiChapterPersonEntities != null && aiChapterPersonEntities.Count() > 0)
repositoryAiChapterPersonEntity.InsertNow(aiChapterPersonEntities); repositoryAiChapterPersonEntity.InsertNow(aiChapterPersonEntities);
} }
[PermissionOperate(name: AIPermissionPolicy.AiBook, operate: Operate.Find)]
public IActionResult GetTtsStream(string text)
{
string fileDownloadName = Path.Combine(service.Configuration["FileServiceSettings:BackupDmsFileDirectory"], @"voice", DateTime.Now.ToString("yyyyMMddHHmmss") + ".mp3");
string param = PackParameters(ChineseTtsVoice.Sherpa2.ToString(), fileDownloadName, text);
RunMyProcess(service.Configuration["CloudBuilder.Topshelf:Path"], param);
var stream = System.IO.File.OpenRead(fileDownloadName);
string contentType = "application/octet-stream";
return new FileStreamResult(stream, contentType) { FileDownloadName = fileDownloadName };
}
private string PackParameters(string voice, string save_path, string text)
{
List<string> para = new List<string>();
para.Add("task:" + "TtsTask");
para.Add("voice:" + voice);
para.Add("save_path:" + save_path);
para.Add("text:" + text.Replace(":", ""));
return para.ToArray().ToDelimiteredList(' ');
}
/// <summary>
/// 执行cmd指令(同步等待await要求等待返回转码太耗时)
/// </summary>
/// <param name="Parameters"></param>
public void RunMyProcess(string exePath, string Parameters)
{
try
{
if (string.IsNullOrEmpty(exePath)) return;
var result = Cli.Wrap(exePath)
.WithWorkingDirectory(Path.GetDirectoryName(exePath))
.WithArguments(Parameters)
.ExecuteAsync().GetAwaiter().GetResult();
Thread.Sleep(300);
}
catch (Exception ex)
{
throw new ValidatedException(ex.Message);
}
}
/// <summary>
/// 执行cmd指令(异步不等待await要求等待返回转码太耗时)
/// </summary>
/// <param name="Parameters"></param>
public void RunMyProcessAsync(string exePath, string Parameters)
{
try
{
if (string.IsNullOrEmpty(exePath)) return;
Cli.Wrap(exePath)
.WithWorkingDirectory(Path.GetDirectoryName(exePath))
.WithArguments(Parameters)
.ExecuteAsync();
Thread.Sleep(300);
}
catch (Exception ex)
{
throw new ValidatedException(ex.Message);
}
}
} }
} }

View File

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.6" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.0" />
<PackageReference Include="ClosedXML" Version="0.102.1" /> <PackageReference Include="ClosedXML" Version="0.102.1" />
@ -24,9 +25,7 @@
<PackageReference Include="Microsoft.ML" Version="5.0.0" /> <PackageReference Include="Microsoft.ML" Version="5.0.0" />
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.26.0" /> <PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.26.0" />
<PackageReference Include="Microsoft.ML.OnnxTransformer" Version="5.0.0" /> <PackageReference Include="Microsoft.ML.OnnxTransformer" Version="5.0.0" />
<PackageReference Include="EdgeTtsSharp" Version="2.0.6" /> <PackageReference Include="NAudio.Core" Version="2.3.0" />
<PackageReference Include="EdgeTtsSharp.NAudio" Version="2.0.6" />
<PackageReference Include="org.k2fsa.sherpa.onnx" Version="1.13.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,225 +0,0 @@
using CloudBuilder.AI.Policy;
using CloudBuilder.Core.DatabaseAccessor.Entity;
using DocumentFormat.OpenXml.Spreadsheet;
using EdgeTtsSharp;
using Google.Protobuf.WellKnownTypes;
using SherpaOnnx;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.AI.Service.Utility
{
public class SaveAudioToFileHelper
{
private IApplicationService service;
private OfflineTts offlineTts1;
private OfflineTts offlineTts2;
private OfflineTts offlineTts3;
private OfflineTts offlineTts4;
public SaveAudioToFileHelper(IApplicationService service)
{
this.service = service;
InitOfflineTtsConfig(DeclaimerPolicy.VITS_ZH_HF_THERESA);
InitOfflineTtsConfig(DeclaimerPolicy.VITS_ZH_HF_FANCHEN_C);
InitOfflineTtsConfig(DeclaimerPolicy.SHERPA_ONNX_VITS_ZH_LL);
InitOfflineTtsConfig(DeclaimerPolicy.VITS_MELO_TTS_ZH_EN);
}
private void InitOfflineTtsConfig(string ttsId)
{
var config = new OfflineTtsConfig();
config.Model.Provider = "cpu";
config.Model.NumThreads = 2;
config.MaxNumSentences = 1;
switch (ttsId)
{
case DeclaimerPolicy.VITS_ZH_HF_THERESA:
config.Model.Vits.Model = "tts-models/vits-zh-hf-theresa/theresa.onnx";
config.Model.Vits.Tokens = "tts-models/vits-zh-hf-theresa/tokens.txt";
config.Model.Vits.Lexicon = "tts-models/vits-zh-hf-theresa/lexicon.txt";
offlineTts1 = new OfflineTts(config);
break;
case DeclaimerPolicy.VITS_ZH_HF_FANCHEN_C:
config.Model.Vits.Model = "tts-models/vits-zh-hf-fanchen-C/vits-zh-hf-fanchen-C.onnx";
config.Model.Vits.Tokens = "tts-models/vits-zh-hf-fanchen-C/tokens.txt";
config.Model.Vits.Lexicon = "tts-models/vits-zh-hf-fanchen-C/lexicon.txt";
config.RuleFars = "tts-models/vits-zh-hf-fanchen-C/phone.fst,tts-models/vits-zh-hf-fanchen-C/number.fst";
offlineTts2 = new OfflineTts(config);
break;
case DeclaimerPolicy.SHERPA_ONNX_VITS_ZH_LL:
config.Model.Vits.Model = "tts-models/sherpa-onnx-vits-zh-ll/model.onnx";
config.Model.Vits.Tokens = "tts-models/sherpa-onnx-vits-zh-ll/tokens.txt";
config.Model.Vits.Lexicon = "tts-models/sherpa-onnx-vits-zh-ll/lexicon.txt";
config.RuleFars = "tts-models/sherpa-onnx-vits-zh-ll/phone.fst,tts-models/sherpa-onnx-vits-zh-ll/number.fst";
offlineTts3 = new OfflineTts(config);
break;
default://DeclaimerPolicy.VITS_MELO_TTS_ZH_EN
config.Model.Vits.Model = "tts-models/vits-melo-tts-zh_en/model.onnx";
config.Model.Vits.Tokens = "tts-models/vits-melo-tts-zh_en/tokens.txt";
config.Model.Vits.Lexicon = "tts-models/vits-melo-tts-zh_en/lexicon.txt";
offlineTts4 = new OfflineTts(config);
break;
}
// --- 2. 初始化引擎 ---
offlineTts1 = new OfflineTts(config);
}
public void AudioToFile(string text, string saveFilePath)
{
AudioToFile(DeclaimerPolicy.VITS_MELO_TTS_ZH_EN, 0, text, saveFilePath);
}
public void AudioToFile(string ttsId, int sid, string text, string saveFilePath)
{
float speed = 1.0f;
OfflineTtsGeneratedAudio audio;
switch (ttsId)
{
case DeclaimerPolicy.VITS_ZH_HF_THERESA:
audio = offlineTts1.Generate(text, speed, sid);
break;
case DeclaimerPolicy.VITS_ZH_HF_FANCHEN_C:
audio = offlineTts2.Generate(text, speed, sid);
break;
case DeclaimerPolicy.SHERPA_ONNX_VITS_ZH_LL:
audio = offlineTts3.Generate(text, speed, sid);
break;
default://DeclaimerPolicy.VITS_MELO_TTS_ZH_EN
audio = offlineTts4.Generate(text, speed, sid);
break;
}
audio.SaveToWaveFile(saveFilePath);
}
public static void AudioToFile(ChineseTtsVoice actorName, string text, string saveFilePath)
{
// 步骤1同步获取异步语音对象
var voiceTask = EdgeTts.GetVoice(actorName.GetVoiceId());
var voice = voiceTask.GetAwaiter().GetResult(); // 替代await同步阻塞
// 步骤2同步保存音频文件
var saveTask = voice.SaveAudioToFile(text, saveFilePath);
saveTask.GetAwaiter().GetResult(); // 同步等待保存完成
}
}
/// <summary>
/// 微软Edge TTS中文语音音色枚举
/// 对应官方Neural语音标识覆盖港澳台及大陆各地区音色
/// </summary>
public enum ChineseTtsVoice
{
/// <summary>
/// 粤语(香港)- 喜佳(女性)
/// </summary>
[System.ComponentModel.Description("zh-HK-HiuGaaiNeural")]
ZhHkHiuGaaiNeural,
/// <summary>
/// 粤语(香港)- 喜文(女性)
/// </summary>
[System.ComponentModel.Description("zh-HK-HiuMaanNeural")]
ZhHkHiuMaanNeural,
/// <summary>
/// 粤语(香港)- 云龙(男性)
/// </summary>
[System.ComponentModel.Description("zh-HK-WanLungNeural")]
ZhHkWanLungNeural,
/// <summary>
/// 普通话(中国大陆)- 晓晓(女性)
/// </summary>
[System.ComponentModel.Description("zh-CN-XiaoxiaoNeural")]
ZhCnXiaoxiaoNeural,
/// <summary>
/// 普通话(中国大陆)- 小艺(女性)
/// </summary>
[System.ComponentModel.Description("zh-CN-XiaoyiNeural")]
ZhCnXiaoyiNeural,
/// <summary>
/// 普通话(中国大陆)- 云健(男性)
/// </summary>
[System.ComponentModel.Description("zh-CN-YunjianNeural")]
ZhCnYunjianNeural,
/// <summary>
/// 普通话(中国大陆)- 云希(男性)
/// </summary>
[System.ComponentModel.Description("zh-CN-YunxiNeural")]
ZhCnYunxiNeural,
/// <summary>
/// 普通话(中国大陆)- 云夏(女性)
/// </summary>
[System.ComponentModel.Description("zh-CN-YunxiaNeural")]
ZhCnYunxiaNeural,
/// <summary>
/// 普通话(中国大陆)- 云阳(男性)
/// </summary>
[System.ComponentModel.Description("zh-CN-YunyangNeural")]
ZhCnYunyangNeural,
/// <summary>
/// 普通话(中国大陆-辽宁)- 小北(女性)
/// </summary>
[System.ComponentModel.Description("zh-CN-liaoning-XiaobeiNeural")]
ZhCnLiaoningXiaobeiNeural,
/// <summary>
/// 普通话(台湾)- 小珍(女性)
/// </summary>
[System.ComponentModel.Description("zh-TW-HsiaoChenNeural")]
ZhTwHsiaoChenNeural,
/// <summary>
/// 普通话(台湾)- 云哲(男性)
/// </summary>
[System.ComponentModel.Description("zh-TW-YunJheNeural")]
ZhTwYunJheNeural,
/// <summary>
/// 普通话(台湾)- 小瑜(女性)
/// </summary>
[System.ComponentModel.Description("zh-TW-HsiaoYuNeural")]
ZhTwHsiaoYuNeural,
/// <summary>
/// 普通话(中国大陆-陕西)- 小妮(女性)
/// </summary>
[System.ComponentModel.Description("zh-CN-shaanxi-XiaoniNeural")]
ZhCnShaanxiXiaoniNeural
}
public static class ChineseTtsVoiceExtensions
{
/// <summary>
/// 获取枚举值对应的Description特性值
/// </summary>
/// <param name="voice">语音枚举值</param>
/// <returns>描述字符串(无特性时返回枚举名称)</returns>
public static string GetVoiceId(this ChineseTtsVoice voice)
{
// 获取枚举成员的特性
var field = voice.GetType().GetField(voice.ToString());
var attribute = field?.GetCustomAttribute<DescriptionAttribute>();
// 有特性返回描述,无则返回枚举名
return attribute?.Description ?? voice.ToString();
}
}
}

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.AI.Policy
{
public enum ChineseTtsVoice
{
/// <summary>
/// 普通话(中国大陆) 娇柔(女性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-0")]
Sherpa0,
/// <summary>
/// 普通话(中国大陆)- 柔美(女性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 柔美(女性)-2")]
Sherpa2,
/// <summary>
/// 普通话(中国大陆)- 知性(男性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 知性(男性)-4")]
Sherpa4,
/// <summary>
/// 普通话(中国大陆)- 知青(男性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 知青(男性)-3")]
Sherpa3,
/// <summary>
/// 普通话(中国大陆)- 中青(男性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 中青(男性)-1")]
Sherpa1,
/// <summary>
/// 普通话(中国大陆)- 爱莎(女性)- 文件小速度快
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 爱莎(女性)-6")]
Aaishe6,
/// <summary>
/// 普通话(中国大陆)- 爱莎(女性)- 文件小速度快
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 爱莎(女性)-14")]
Aaishe14,
/// <summary>
/// 普通话(中国大陆)- 爱莎(女性)- 文件小速度快
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 爱莎(女性)-18")]
Aaishe18,
/// <summary>
/// 普通话(中国大陆)- 云健(男性)- 文件小速度快
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 云健(男性)-10")]
Aaishe10,
/// <summary>
/// 普通话(中国大陆)- 爱莎(女性)- 文件小速度快
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 爱莎(女性)-1")]
Aaishe1,
/// <summary>
/// 普通话(中国大陆)- 云健(男性)- 文件小速度快
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 云健(男性)-15")]
Aaishe15,
/// 普通话(中国大陆) 云阳(男性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 云阳(男性)-10")]
Fanchen10,
/// <summary>
/// 普通话(中国大陆) 云阳(男性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 云阳(男性)-110")]
Fanchen110,
/// 普通话(中国大陆) 喜佳(女性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 云夏(女性)-6")]
Fanchen6,
/// <summary>
/// 普通话(中国大陆) 云阳(男性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 云阳(男性)-15")]
Fanchen15,
/// <summary>
/// 普通话(中国大陆) 喜佳(女性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-2")]
Fanchen2,
/// <summary>
/// 普通话(中国大陆) 喜佳(女性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-14")]
Fanchen14,
/// <summary>
/// 普通话(中国大陆) 喜佳(女性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-20")]
Fanchen20,
/// <summary>
/// 普通话(中国大陆) 喜佳(女性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 喜佳(女性)-36")]
Fanchen36,
/// <summary>
/// 粤语(香港)- 女性(男性)- 文件大
/// </summary>
[System.ComponentModel.Description("粤语(香港) 梅隆 (女性)-0")]
Melo,
/// <summary>
/// 普通话(中国大陆)- 神精女(女性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 神精女(女性)-0")]
Eula,
/// <summary>
/// 普通话(中国大陆)- 小神精女(女性)
/// </summary>
[System.ComponentModel.Description("普通话(中国大陆) 小神精女(女性)-27")]
Theresa,
}
}

View File

@ -5,6 +5,7 @@ using CloudBuilder.AI.SearchCriteria;
using CloudBuilder.Core.Service; using CloudBuilder.Core.Service;
using CloudBuilder.Security.Data; using CloudBuilder.Security.Data;
using CloudBuilder.Security.SearchCriteria; using CloudBuilder.Security.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.AI.Service namespace CloudBuilder.AI.Service
{ {
@ -23,5 +24,7 @@ namespace CloudBuilder.AI.Service
void ExtractNames(string bookGuid); void ExtractNames(string bookGuid);
void ExtractNamesByChapterGuid(string bookGuid, string chapterGuid); void ExtractNamesByChapterGuid(string bookGuid, string chapterGuid);
IActionResult GetTtsStream(string text);
} }
} }

View File

@ -39,6 +39,8 @@ namespace CloudBuilder.Core.Request
Task<R> PostAsync<R>(string api, MultipartFormDataContent data); Task<R> PostAsync<R>(string api, MultipartFormDataContent data);
Task<Stream> PostStreamAsync<T>(string api, Dictionary<string, string> headers, T data);
void AddHeaders(string name, string? value); void AddHeaders(string name, string? value);
void SetContext(IContext loginContext); void SetContext(IContext loginContext);
HttpClient GetHttpClient(); HttpClient GetHttpClient();

View File

@ -361,6 +361,56 @@ namespace CloudBuilder.Core.Request
if (completedCallback != null) completedCallback(filePath); if (completedCallback != null) completedCallback(filePath);
} }
public async Task<Stream> PostStreamAsync<T>(string api, Dictionary<string, string> headers, T data)
{
if (headers != null && headers.Count > 0)
{
foreach (var header in headers)
{
if (request.DefaultRequestHeaders.Contains(header.Key)) request.DefaultRequestHeaders.Remove(header.Key);
request.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
this.request.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", loginContext.GetToken());
if (request.DefaultRequestHeaders.Contains(ClaimPolicy.HEADER_LOCALE))
request.DefaultRequestHeaders.Remove(ClaimPolicy.HEADER_LOCALE);
request.DefaultRequestHeaders.Add(ClaimPolicy.HEADER_LOCALE, loginContext.GetCulture());
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
try
{
var response = await this.request.PostAsJsonAsync(api, data, options).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
//{"errors":{},"type":"https://tools.ietf.org/html/rfc9110#section-15.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"00-64ad066978f4471cb3c9ce219c213da9-bccca8afd14676b3-00"}
var responseContext = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
string errors = string.Empty;
if (responseContext != null && responseContext.StartsWith("{\"errors\"")) errors = responseContext.Substring(11, responseContext.IndexOf("}") - 11);
if (response.ReasonPhrase == "Forbidden")
throw new Exception(string.Format("{0}", CoreMessagePolicy.GetInstance().ComposeMessage(CoreMessagePolicy.NO_PERMISSION)));
throw new Exception(string.Format("{0}{1}", response.ReasonPhrase, string.IsNullOrEmpty(errors) ? "" : "\r\n" + errors));
}
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStreamAsync();
}
catch (Exception ex )
{
throw ex;
}
}
public async Task<R> PostAsync<R>(string api, MultipartFormDataContent data) public async Task<R> PostAsync<R>(string api, MultipartFormDataContent data)
{ {

View File

@ -9,6 +9,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Request.AI namespace CloudBuilder.Request.AI
{ {
@ -75,6 +76,10 @@ var compositionData = new ProcessBookCompositionData3{ key = key, encoding = e
{ {
var compositionData = new ExtractNamesByChapterGuidCompositionData2{ bookGuid = bookGuid, chapterGuid = chapterGuid }; Task.Run(() => serviceRequest.PostAsJsonAsync<ExtractNamesByChapterGuidCompositionData2, dynamic>("/AiBook/ExtractNamesByChapterGuid", compositionData)).GetAwaiter().GetResult(); } var compositionData = new ExtractNamesByChapterGuidCompositionData2{ bookGuid = bookGuid, chapterGuid = chapterGuid }; Task.Run(() => serviceRequest.PostAsJsonAsync<ExtractNamesByChapterGuidCompositionData2, dynamic>("/AiBook/ExtractNamesByChapterGuid", compositionData)).GetAwaiter().GetResult(); }
public IActionResult GetTtsStream(string text)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<IActionResult>("/AiBook/GetTtsStream?text=" + text.ToString())).GetAwaiter().GetResult(); }
} }
} }

View File

@ -9,6 +9,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Request.AI namespace CloudBuilder.Request.AI
{ {

View File

@ -9,6 +9,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Request.AI namespace CloudBuilder.Request.AI
{ {

View File

@ -9,6 +9,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Request.AI namespace CloudBuilder.Request.AI
{ {

View File

@ -9,6 +9,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Request.AI namespace CloudBuilder.Request.AI
{ {

View File

@ -9,6 +9,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace CloudBuilder.Request.AI namespace CloudBuilder.Request.AI

View File

@ -28,9 +28,7 @@
<PackageReference Include="Microsoft.ML" Version="5.0.0" /> <PackageReference Include="Microsoft.ML" Version="5.0.0" />
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.26.0" /> <PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.26.0" />
<PackageReference Include="Microsoft.ML.OnnxTransformer" Version="5.0.0" /> <PackageReference Include="Microsoft.ML.OnnxTransformer" Version="5.0.0" />
<PackageReference Include="EdgeTtsSharp" Version="2.0.6" />
<PackageReference Include="EdgeTtsSharp.NAudio" Version="2.0.6" />
<PackageReference Include="org.k2fsa.sherpa.onnx" Version="1.13.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -11,6 +11,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Web.Entry.Controllers.Ai namespace CloudBuilder.Web.Entry.Controllers.Ai
{ {
@ -246,6 +247,26 @@ this.aiBookService= aiBookService;
} }
} }
[PermissionOperate(name: "AiBook", operate: "Find")]
[HttpPost]
public dynamic GetTtsStream(string text)
{
try
{
var result = aiBookService.GetTtsStream(text);
return result;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
} }
} }

View File

@ -11,6 +11,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Web.Entry.Controllers.Ai namespace CloudBuilder.Web.Entry.Controllers.Ai
{ {

View File

@ -11,6 +11,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Web.Entry.Controllers.Ai namespace CloudBuilder.Web.Entry.Controllers.Ai
{ {

View File

@ -11,6 +11,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Web.Entry.Controllers.Ai namespace CloudBuilder.Web.Entry.Controllers.Ai
{ {

View File

@ -11,6 +11,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
namespace CloudBuilder.Web.Entry.Controllers.Ai namespace CloudBuilder.Web.Entry.Controllers.Ai
{ {

View File

@ -11,6 +11,7 @@ using CloudBuilder.AI.Service;
using System; using System;
using CloudBuilder.AI.Data; using CloudBuilder.AI.Data;
using CloudBuilder.AI.SearchCriteria; using CloudBuilder.AI.SearchCriteria;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace CloudBuilder.Web.Entry.Controllers.Ai namespace CloudBuilder.Web.Entry.Controllers.Ai

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -1,3 +1,4 @@
using CloudBuilder.Core.DatabaseAccessor; using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.Policy; using CloudBuilder.Core.Policy;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;

View File

@ -62,7 +62,7 @@
"Arguments": " -i {0} -c:v libx264 -vf scale=1280:-1 -start_number 0 -hls_time {1} -hls_list_size 0 -f hls {2}" //0-1-2- 1280×720852*480 "Arguments": " -i {0} -c:v libx264 -vf scale=1280:-1 -start_number 0 -hls_time {1} -hls_list_size 0 -f hls {2}" //0-1-2- 1280×720852*480
}, },
"CloudBuilder.Topshelf": { "CloudBuilder.Topshelf": {
"Path": "D:\\NET8\\CloudBuilder.Topshelf\\bin\\Debug\\net8.0\\CloudBuilder.Topshelf.exe" // "Path": "D:\\MAFAI\\CloudBuilder.Topshelf\\bin\\Debug\\net8.0\\CloudBuilder.Topshelf.exe" //
}, },
"SecuritySettings": { "SecuritySettings": {
"RsaPublicKey": "-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8HMr2CBpoZPm3t9tCVlrKtTmI4jNJc7/HhxjIEiDjC8czP4PV+44LjXvLYcSV0fwi6nE4LH2c5PBPEnPfqp0g8TZeX+bYGvd70cXee9d8wHgBqi4k0J0X33c0ZnW7JruftPyvJo9OelYSofBXQTcwI+3uIl/YvrgQRv6A5mW01QIDAQAB-----END PUBLIC KEY-----", "RsaPublicKey": "-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8HMr2CBpoZPm3t9tCVlrKtTmI4jNJc7/HhxjIEiDjC8czP4PV+44LjXvLYcSV0fwi6nE4LH2c5PBPEnPfqp0g8TZeX+bYGvd70cXee9d8wHgBqi4k0J0X33c0ZnW7JruftPyvJo9OelYSofBXQTcwI+3uIl/YvrgQRv6A5mW01QIDAQAB-----END PUBLIC KEY-----",

View File

@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudBuilder.AI.Service", "
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudBuilder.Security", "CloudBuilder.Security\CloudBuilder.Security.csproj", "{269A5036-BAE9-FD61-1DD5-0EA93B79F0D7}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudBuilder.Security", "CloudBuilder.Security\CloudBuilder.Security.csproj", "{269A5036-BAE9-FD61-1DD5-0EA93B79F0D7}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudBuilder.Core", "CloudBuilder.Core\CloudBuilder.Core.csproj", "{AFA9D41C-E8A5-32D3-E81B-2ABA7C37D46C}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -57,6 +59,10 @@ Global
{269A5036-BAE9-FD61-1DD5-0EA93B79F0D7}.Debug|Any CPU.Build.0 = Debug|Any CPU {269A5036-BAE9-FD61-1DD5-0EA93B79F0D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{269A5036-BAE9-FD61-1DD5-0EA93B79F0D7}.Release|Any CPU.ActiveCfg = Release|Any CPU {269A5036-BAE9-FD61-1DD5-0EA93B79F0D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{269A5036-BAE9-FD61-1DD5-0EA93B79F0D7}.Release|Any CPU.Build.0 = Release|Any CPU {269A5036-BAE9-FD61-1DD5-0EA93B79F0D7}.Release|Any CPU.Build.0 = Release|Any CPU
{AFA9D41C-E8A5-32D3-E81B-2ABA7C37D46C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AFA9D41C-E8A5-32D3-E81B-2ABA7C37D46C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AFA9D41C-E8A5-32D3-E81B-2ABA7C37D46C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AFA9D41C-E8A5-32D3-E81B-2ABA7C37D46C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -61,6 +61,8 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="ClosedXML" Version="0.102.1" /> <PackageReference Include="ClosedXML" Version="0.102.1" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3351.48" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3351.48" />
<PackageReference Include="NAudio" Version="2.3.0" />
<PackageReference Include="NAudio.Core" Version="2.3.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.0" /> <PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>