This commit is contained in:
owenchen 2026-06-22 17:02:13 +08:00
parent 44ed43516e
commit 996bb243df
5 changed files with 56 additions and 72 deletions

View File

@ -240,7 +240,7 @@ namespace CloudBuilder.AI.Service
repositoryAiChapterEntity.InsertNow(data.AiChapters); repositoryAiChapterEntity.InsertNow(data.AiChapters);
if (data.AiSentences == null || data.AiSentences.Count() == 0) return data; if (data.AiSentences == null || data.AiSentences.Count() == 0) return data;
repositoryAiSentenceEntity.InsertNow(data.AiSentences); repositoryAiSentenceEntity.InsertNow(data.AiSentences);
return FindByKey(key); return FindByKey(key);
} }
@ -337,9 +337,13 @@ namespace CloudBuilder.AI.Service
private string PackTtsParameters(string voice, string save_path, string bookId, string chapterId) private string PackTtsParameters(string voice, string save_path, string bookId, string chapterId)
{ {
//速度快,音质差
//task:TtsTask book_id:B000008 chapter_id:3 speed:1 voice:Sherpa2 actor:韩立 actor_voice:Sherpa1 save_path:D:\\Net8\\FileServer\\Backup\\DmsFile\\voice //task:TtsTask book_id:B000008 chapter_id:3 speed:1 voice:Sherpa2 actor:韩立 actor_voice:Sherpa1 save_path:D:\\Net8\\FileServer\\Backup\\DmsFile\\voice
//速度慢,音质好
//task:SoVITSTtsTask book_id:B000011 chapter_id:3 save_path:D:\\Net8\\FileServer\\Backup\\DmsFile\\voice
//非必要参数 voice:MIDDLE_AGED_FEMALE5 actor:韩立 actor_voice:YOUNG_MALE2
List<string> para = new List<string>(); List<string> para = new List<string>();
para.Add("task:" + "TtsTask"); para.Add("task:" + "SoVITSTtsTask");
para.Add("book_id:" + bookId); para.Add("book_id:" + bookId);
para.Add("chapter_id:" + chapterId); para.Add("chapter_id:" + chapterId);
para.Add("voice:" + voice); para.Add("voice:" + voice);

View File

@ -31,14 +31,6 @@ namespace CloudBuilder.AI.Entity
public string PersonName { get; set; } public string PersonName { get; set; }
[Column("actor_name")] [Column("actor_name")]
public string? ActorName { get; set; } public string? ActorName { get; set; }
[Column("gender")]
public string? Gender { get; set; }
[Column("sentiment")]
public string? Sentiment { get; set; }
[Column("sound_file_path")]
public string? SoundFilePath { get; set; }
[Column("status")]
public string? Status { get; set; }
public const string CREATED_DATETIME = "CreatedDatetime"; public const string CREATED_DATETIME = "CreatedDatetime";
public const string CREATED_BY = "CreatedBy"; public const string CREATED_BY = "CreatedBy";
@ -48,13 +40,9 @@ namespace CloudBuilder.AI.Entity
public const string BOOK_ID = "BookId"; public const string BOOK_ID = "BookId";
public const string PERSON_NAME = "PersonName"; public const string PERSON_NAME = "PersonName";
public const string ACTOR_NAME = "ActorName"; public const string ACTOR_NAME = "ActorName";
public const string GENDER = "Gender";
public const string SENTIMENT = "Sentiment";
public const string SOUND_FILE_PATH = "SoundFilePath";
public const string STATUS = "Status";
public const string DB_NAME_AI_BOOK_PERSON = "ai_book_person"; public const string DB_NAME_AI_BOOK_PERSON = "ai_book_person";
public const string DB_NAME_FIELDS="created_datetime,created_by,updated_datetime,updated_by,person_id,book_id,person_name,actor_name,gender,sentiment,sound_file_path,status"; public const string DB_NAME_FIELDS="created_datetime,created_by,updated_datetime,updated_by,person_id,book_id,person_name,actor_name";
public const string DB_CREATED_DATETIME = "created_datetime"; public const string DB_CREATED_DATETIME = "created_datetime";
public const string DB_CREATED_BY = "created_by"; public const string DB_CREATED_BY = "created_by";
@ -64,10 +52,6 @@ namespace CloudBuilder.AI.Entity
public const string DB_BOOK_ID = "book_id"; public const string DB_BOOK_ID = "book_id";
public const string DB_PERSON_NAME = "person_name"; public const string DB_PERSON_NAME = "person_name";
public const string DB_ACTOR_NAME = "actor_name"; public const string DB_ACTOR_NAME = "actor_name";
public const string DB_GENDER = "gender";
public const string DB_SENTIMENT = "sentiment";
public const string DB_SOUND_FILE_PATH = "sound_file_path";
public const string DB_STATUS = "status";
public static AiBookPersonEntity GetInstance() public static AiBookPersonEntity GetInstance()
@ -95,10 +79,6 @@ namespace CloudBuilder.AI.Entity
this.BookId = source.BookId; this.BookId = source.BookId;
this.PersonName = source.PersonName; this.PersonName = source.PersonName;
this.ActorName = source.ActorName; this.ActorName = source.ActorName;
this.Gender = source.Gender;
this.Sentiment = source.Sentiment;
this.SoundFilePath = source.SoundFilePath;
this.Status = source.Status;
if (includeSystemFields) if (includeSystemFields)
{ {

View File

@ -97,9 +97,11 @@ namespace CloudBuilder.AI.Utility
// 复姓列表 // 复姓列表
private static readonly HashSet<string> CompoundSurnames = new HashSet<string> private static readonly HashSet<string> CompoundSurnames = new HashSet<string>
{ {
"欧阳","太史","端木","上官","司马","东方","独孤","南宫", "游竺", "权逯", "盖益", "桓公", "万俟", "司马", "上官", "欧阳",
"万俟","闻人","夏侯","诸葛","尉迟","公羊","赫连","澹台", "夏侯", "诸葛", "闻人", "东方", "赫连", "皇甫", "尉迟", "公羊",
"皇甫","宗政","濮阳","公冶","太叔","申屠","公孙","慕容" "澹台", "公冶", "宗政", "濮阳", "淳于", "单于", "太叔", "申屠",
"公孙", "仲孙", "轩辕", "令狐", "钟离", "宇文", "长孙", "慕容",
"司徒", "司空"
}; };
/// <summary> /// <summary>

View File

@ -18,7 +18,7 @@ namespace CloudBuilder.AI.Utility
// 动态生成的正则表达式(匹配所有配置的引号类型) // 动态生成的正则表达式(匹配所有配置的引号类型)
private Regex _dynamicQuotePattern; private Regex _dynamicQuotePattern;
// 引号内必须包含的结尾标点集合 // 引号内必须包含的结尾标点集合
private readonly string _endPunctuations = "?!。,;:、……~·"; private readonly string _endPunctuations = "?!.,;:?!。,;:、……~·";
/// <summary> /// <summary>
/// 默认构造函数:初始化常用成对引号(中文、英文、直角引号) /// 默认构造函数:初始化常用成对引号(中文、英文、直角引号)
@ -48,47 +48,31 @@ namespace CloudBuilder.AI.Utility
/// <summary> /// <summary>
/// 初始化动态正则:根据配置的成对引号生成匹配规则 /// 初始化动态正则:根据配置的成对引号生成匹配规则
/// </summary> /// </summary>
//private void InitDynamicRegex()
//{
// if (_quotePairs.Count == 0)
// {
// // 空配置时生成空正则,避免匹配任何内容
// _dynamicQuotePattern = new Regex(@"^$", RegexOptions.Compiled);
// return;
// }
// // 步骤1转义所有引号避免正则特殊字符冲突拼接左/右引号匹配组
// string escapedLeftQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.LeftQuote)));
// string escapedRightQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.RightQuote)));
// // 步骤2转义标点集合避免正则解析错误
// string escapedPunctuations = Regex.Escape(_endPunctuations);
// // 步骤3构建动态正则表达式
// // 分组说明:
// // ?<left>:匹配配置的任意左引号
// // ?<content>:匹配引号内的内容(必须以指定标点结尾)
// // ?<right>:匹配配置的任意右引号
// string regexPattern = $@"(?<left>{escapedLeftQuotes})(?<content>.+?[{escapedPunctuations}])(?<right>{escapedRightQuotes})";
// _dynamicQuotePattern = new Regex(regexPattern, RegexOptions.Compiled | RegexOptions.Singleline);
//}
private void InitDynamicRegex() private void InitDynamicRegex()
{ {
if (_quotePairs.Count == 0) if (_quotePairs.Count == 0)
{ {
// 空配置时生成空正则,避免匹配任何内容
_dynamicQuotePattern = new Regex(@"^$", RegexOptions.Compiled); _dynamicQuotePattern = new Regex(@"^$", RegexOptions.Compiled);
return; return;
} }
// 步骤1转义所有引号避免正则特殊字符冲突拼接左/右引号匹配组
string escapedLeftQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.LeftQuote))); string escapedLeftQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.LeftQuote)));
string escapedRightQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.RightQuote))); string escapedRightQuotes = string.Join("|", _quotePairs.Select(p => Regex.Escape(p.RightQuote)));
// 步骤2转义标点集合避免正则解析错误
string escapedPunctuations = Regex.Escape(_endPunctuations);
// ✅ 修复:移除必须以标点结尾的限制,匹配任意引号内容 // 步骤3构建动态正则表达式
string regexPattern = $@"(?<left>{escapedLeftQuotes})(?<content>.+?)(?<right>{escapedRightQuotes})"; // 分组说明:
// ?<left>:匹配配置的任意左引号
// ?<content>:匹配引号内的内容(必须以指定标点结尾)
// ?<right>:匹配配置的任意右引号
string regexPattern = $@"(?<left>{escapedLeftQuotes})(?<content>.+?[{escapedPunctuations}])(?<right>{escapedRightQuotes})";
_dynamicQuotePattern = new Regex(regexPattern, RegexOptions.Compiled | RegexOptions.Singleline); _dynamicQuotePattern = new Regex(regexPattern, RegexOptions.Compiled | RegexOptions.Singleline);
} }
/// <summary> /// <summary>
/// 核心分割方法:按配置的成对引号分割文本,结果无引号字符 /// 核心分割方法:按配置的成对引号分割文本,结果无引号字符
/// </summary> /// </summary>
@ -182,15 +166,12 @@ namespace CloudBuilder.AI.Utility
/// <returns>true=包含对话false=纯旁白/空文本</returns> /// <returns>true=包含对话false=纯旁白/空文本</returns>
public bool IsDialogue(string text) public bool IsDialogue(string text)
{ {
// 1. 处理空文本/NULL直接返回false
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
return false; return false;
// 2. 文本预处理:去空格、统一全角/半角引号
string cleanedText = text.Trim(); string cleanedText = text.Trim();
// 替换全角空格为半角,避免干扰判断
cleanedText = Regex.Replace(cleanedText, @" ", " "); cleanedText = Regex.Replace(cleanedText, @" ", " ");
// 统一引号格式(中文弯引号、直引号 → 标准双引号) // 统一引号
cleanedText = cleanedText.Replace("“", "\"") cleanedText = cleanedText.Replace("“", "\"")
.Replace("”", "\"") .Replace("”", "\"")
.Replace("〝", "\"") .Replace("〝", "\"")
@ -200,17 +181,34 @@ namespace CloudBuilder.AI.Utility
.Replace("", "'") .Replace("", "'")
.Replace("", "'"); .Replace("", "'");
// 3. 核心判断规则: // 1. 存在说话引导标记(冒号/逗号 + 引号)
var hasQuote = Regex.IsMatch(cleanedText, @""".+?""") || bool hasDialogueTag = Regex.IsMatch(cleanedText, @"[:,]\s*""");
Regex.IsMatch(cleanedText, @"[:,]\s*"""); // 冒号/逗号+引号(说话标记)
var hasSingleQuote = Regex.IsMatch(cleanedText, @"'[^']+'"); // 2. 提取所有双引号内容,逐个判断是否为有效对话
var quotedMatches = Regex.Matches(cleanedText, @"""(?<content>.*?)""");
bool hasValidDialogue = false;
foreach (Match m in quotedMatches)
{
string content = m.Groups["content"].Value.Trim();
if (string.IsNullOrEmpty(content)) continue;
// 4. 排除仅含引号符号的无效情况(修复:只匹配替换后的符号) // 规则A包含典型对话结尾标点
var onlyQuote = Regex.IsMatch(cleanedText, @"^\s*[""']{1,2}\s*$"); bool endsWithDialogPunct = Regex.IsMatch(content, @"[。!??!…\.~、,]$");
// 规则B长度至少4个字符或按中文字数算
bool isLongEnough = content.Length >= 4;
// 最终判断 if (endsWithDialogPunct || isLongEnough)
return (hasQuote || hasSingleQuote) && !onlyQuote; {
hasValidDialogue = true;
break;
}
}
// 3. 单引号内容同理(可省略或同逻辑)
// 最终:有说话标记 或 存在有效对话内容,且不是“仅由引号符号组成”
bool onlyQuote = Regex.IsMatch(cleanedText, @"^\s*[""']{1,2}\s*$");
return (hasDialogueTag || hasValidDialogue) && !onlyQuote;
} }
} }
} }

View File

@ -183,12 +183,12 @@ namespace CloudBuilder.AI.Utility
// -------------------- 常用组合快捷方法 -------------------- // -------------------- 常用组合快捷方法 --------------------
public static SoVITSTtsVoice GetRandomElderlyMale() => GetRandomByGenderAndAge("男性", "老年人"); public static SoVITSTtsVoice GetRandomElderlyMale() => GetRandomByGenderAndAge("男性", "老年人");
public static SoVITSTtsVoice GetRandomElderlyFemale() => GetRandomByGenderAndAge("女性", "老年人"); public static SoVITSTtsVoice GetRandomElderlyFemale() => GetRandomByGenderAndAge("女性", "老年人");
public static SoVITSTtsVoice GetRandomMiddleAgedMale() => GetRandomByGenderAndAge("男性", "中年"); public static SoVITSTtsVoice GetRandomMiddleAgedMale() => GetRandomByGenderAndAge("男性", "中年");
public static SoVITSTtsVoice GetRandomMiddleAgedFemale() => GetRandomByGenderAndAge("女性", "中年"); public static SoVITSTtsVoice GetRandomMiddleAgedFemale() => GetRandomByGenderAndAge("女性", "中年");
public static SoVITSTtsVoice GetRandomYoungMale() => GetRandomByGenderAndAge("男性", "青年"); public static SoVITSTtsVoice GetRandomYoungMale() => GetRandomByGenderAndAge("男性", "青年");
public static SoVITSTtsVoice GetRandomYoungFemale() => GetRandomByGenderAndAge("女性", "青年"); public static SoVITSTtsVoice GetRandomYoungFemale() => GetRandomByGenderAndAge("女性", "青年");
public static SoVITSTtsVoice GetRandomChildMale() => GetRandomByGenderAndAge("男性", "儿童"); public static SoVITSTtsVoice GetRandomChildMale() => GetRandomByGenderAndAge("男性", "儿童");
public static SoVITSTtsVoice GetRandomChildFemale() => GetRandomByGenderAndAge("女性", "儿童"); public static SoVITSTtsVoice GetRandomChildFemale() => GetRandomByGenderAndAge("女性", "儿童");
// -------------------- 排除指定音色的扩展(可选)-------------------- // -------------------- 排除指定音色的扩展(可选)--------------------