This commit is contained in:
owenchen 2026-06-04 08:16:49 +08:00
parent 39494fb5c3
commit 871de41ba5
9 changed files with 259 additions and 49 deletions

Binary file not shown.

View File

@ -33,6 +33,7 @@ public partial class Program
string taskName = null; string taskName = null;
Dictionary<string, string> bodyDict = new Dictionary<string, string>(); Dictionary<string, string> bodyDict = new Dictionary<string, string>();
File.AppendAllText("log.txt", "Main:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
if (args != null && args.Length > 0) if (args != null && args.Length > 0)
{ {

View File

@ -2,7 +2,7 @@
"profiles": { "profiles": {
"CloudBuilder.Topshelf": { "CloudBuilder.Topshelf": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "task:ChineseNameExtractorTask book_id:B000008" "commandLineArgs": "task:TtsTask book_id:B000008 chapter_id:4 speed:1 voice:Sherpa2 actor:韩立 actor_voice:Sherpa1 save_path:D:\\\\Net8\\\\FileServer\\\\Backup\\\\DmsFile\\\\voice"
} }
} }
} }

View File

@ -156,6 +156,27 @@ namespace CloudBuilder.Topshelf.Python
return null; return null;
} }
public List<string> GetSurnames()
{
try
{
//必须定义方法的方式使用
using (Py.GIL())
{
string json = python.GetValueExec($"processor.get_surnames_json()");
// 反序列化
if (json == "[]") return null;
return JsonSerializer.Deserialize<List<string>>(json);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
public string Get(string arg) public string Get(string arg)
{ {
return python.GetValueExec(arg); return python.GetValueExec(arg);

View File

@ -3,15 +3,16 @@ using CloudBuilder.Core.DatabaseAccessor.Entity;
using CloudBuilder.Core.DependencyInjection.Task; using CloudBuilder.Core.DependencyInjection.Task;
using CloudBuilder.Topshelf.Python; using CloudBuilder.Topshelf.Python;
using CloudBuilder.Topshelf.Utility; using CloudBuilder.Topshelf.Utility;
using System.Text.RegularExpressions;
using static System.Net.Mime.MediaTypeNames; using static System.Net.Mime.MediaTypeNames;
namespace CloudBuilder.Topshelf.Task namespace CloudBuilder.Topshelf.Task
{ {
//task:ChineseNameExtractorTask book_id:B000008 //task:ChineseNameExtractorTask book_id:B000008 chapter_id:3
public class ChineseNameExtractorTask : IScheduleTask public class ChineseNameExtractorTask : IScheduleTask
{ {
private readonly IApplicationService service; private readonly IApplicationService service;
private List<string> commonSurnames;
public ChineseNameExtractorTask(IApplicationService service) public ChineseNameExtractorTask(IApplicationService service)
{ {
this.service = service; this.service = service;
@ -19,14 +20,20 @@ namespace CloudBuilder.Topshelf.Task
public void Run(Dictionary<string, string> bodyDict) public void Run(Dictionary<string, string> bodyDict)
{ {
File.AppendAllText("log.txt", "ChineseNameExtractorTask:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
if (!bodyDict.ContainsKey("book_id")) return; if (!bodyDict.ContainsKey("book_id")) return;
string guid = bodyDict["book_id"]; string bookId = bodyDict["book_id"];
string chapterId = string.Empty;
if (bodyDict.ContainsKey("chapter_id"))
chapterId = bodyDict["chapter_id"];
PythonCommand python = new PythonCommand(service); PythonCommand python = new PythonCommand(service);
try try
{ {
Hanlp2 hanlp = new Hanlp2(python); Hanlp2 hanlp = new Hanlp2(python);
commonSurnames = hanlp.GetSurnames();
IRepository<AiSentenceViewEntity> repository = service.GetRepository<IRepository<AiSentenceViewEntity>>(); IRepository<AiSentenceViewEntity> repository = service.GetRepository<IRepository<AiSentenceViewEntity>>();
@ -34,6 +41,9 @@ namespace CloudBuilder.Topshelf.Task
IRepository<AiChapterPersonEntity> repositoryAiChapterPersonEntity = service.GetRepository<IRepository<AiChapterPersonEntity>>(); IRepository<AiChapterPersonEntity> repositoryAiChapterPersonEntity = service.GetRepository<IRepository<AiChapterPersonEntity>>();
AiChapterPersonEntity[] dels = repositoryAiChapterPersonEntity.DetachedEntities.Where(x => x.BookId == bookId).ToArray();
if (dels != null && dels.Length > 0) repositoryAiChapterPersonEntity.DeleteNow(dels);
string depRoot; string depRoot;
List<NamedEntity> ners; List<NamedEntity> ners;
@ -44,7 +54,7 @@ namespace CloudBuilder.Topshelf.Task
SpeakerAnalysisHelper speakerAnalysisHelper = new SpeakerAnalysisHelper(); SpeakerAnalysisHelper speakerAnalysisHelper = new SpeakerAnalysisHelper();
AiSentenceViewEntity[] ents = repository.DetachedEntities.Where(x => x.BookId == guid).OrderBy(x => x.ChapterId).ThenBy(x => x.ParagraphId).ThenBy(x => x.SentenceIndex).ToArray(); AiSentenceViewEntity[] ents = repository.DetachedEntities.Where(x => x.BookId == bookId && (string.IsNullOrEmpty(chapterId) || x.ChapterId == Convert.ToInt32(chapterId))).OrderBy(x => x.ChapterId).ThenBy(x => x.ParagraphId).ThenBy(x => x.SentenceIndex).ToArray();
if (ents == null || ents.Length == 0) return; if (ents == null || ents.Length == 0) return;
AiSentenceEntity aiSentenceEntity; AiSentenceEntity aiSentenceEntity;
@ -70,22 +80,23 @@ namespace CloudBuilder.Topshelf.Task
{ {
if (p.Length == 1) continue; if (p.Length == 1) continue;
aiChapterPerson = new AiChapterPersonEntity(); aiChapterPerson = new AiChapterPersonEntity();
aiChapterPerson.PersonName = p; aiChapterPerson.PersonName = KeepChineseCharactersOnly(p);
aiChapterPerson.BookId = ent.BookId; aiChapterPerson.BookId = ent.BookId;
aiChapterPerson.ChapterId = ent.ChapterId; aiChapterPerson.ChapterId = ent.ChapterId;
aiChapterPerson.ParagraphIndex = ent.ParagraphId; aiChapterPerson.ParagraphIndex = ent.ParagraphId;
aiChapterPerson.SentenceIndex= ent.SentenceIndex; aiChapterPerson.SentenceIndex = ent.SentenceIndex;
aiChapterPerson.DialogueIndc = ent.DialogueIndc;
aiChapterPerson.OrderIndex = orderIndex++; aiChapterPerson.OrderIndex = orderIndex++;
cps.Add(aiChapterPerson); cps.Add(aiChapterPerson);
repositoryAiChapterPersonEntity.InsertNow(aiChapterPerson); repositoryAiChapterPersonEntity.InsertNow(aiChapterPerson);
} }
} }
UpdateProgress(processedLines, totalLines); ConsoleOutput.UpdateProgress(processedLines, totalLines);
} }
speakerAnalysisHelper.AiChapterPersons = cps.ToArray(); speakerAnalysisHelper.AiChapterPersons = cps.ToArray();
speakerAnalysisHelper.CommonSurnames = commonSurnames;
speakerAnalysisHelper.MatchPerson(ents, repositoryAiSentenceEntity); speakerAnalysisHelper.MatchPerson(ents, repositoryAiSentenceEntity);
} }
catch (Exception ex) catch (Exception ex)
@ -99,18 +110,25 @@ namespace CloudBuilder.Topshelf.Task
} }
} }
private static void UpdateProgress(int processed, int total)
private string KeepChineseCharactersOnly(string input)
{ {
double progressPercent = (double)processed / total * 100; if (string.IsNullOrEmpty(input))
int progressBarLength = 50; // 进度条总长度 return input;
int filledLength = (int)(progressPercent / 100 * progressBarLength);
// 构建进度条(如:[██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] // 方法1使用正则表达式
string progressBar = "[" + new string('█', filledLength) + new string('░', progressBarLength - filledLength) + "]"; //return Regex.Replace(input, @"[^\u4e00-\u9fff]", "");
return Regex.Replace(input, @"[^\u4e00-\u9fff\【\】]", "");
string progressInfo = $"{progressBar} {progressPercent:F2}% | 已处理:{processed}/{total} "; // 方法2手动遍历性能更好
Console.Write($"\r{progressInfo}"); // var sb = new StringBuilder();
Console.Out.Flush(); // foreach (char c in input)
// {
// if (c >= 0x4e00 && c <= 0x9fff)
// sb.Append(c);
// }
// return sb.ToString();
} }
} }
} }

View File

@ -17,7 +17,7 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
namespace CloudBuilder.Topshelf.Task.TTS namespace CloudBuilder.Topshelf.Task.TTS
{ {
//task:TtsTask book_id:B000008 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:TtsTask voice:Sherpa2 save_path:D:\\Net8\\FileServer\\Backup\\DmsFile\\voice\\20260528112739.mp3 text:二愣子睁大着双眼,直直望着茅草和烂泥糊成的黑屋顶,身上盖着的旧棉被,已呈深黄色,看不出原来的本来面目,还若有若无的散发着淡淡的霉味。 //task:TtsTask voice:Sherpa2 save_path:D:\\Net8\\FileServer\\Backup\\DmsFile\\voice\\20260528112739.mp3 text:二愣子睁大着双眼,直直望着茅草和烂泥糊成的黑屋顶,身上盖着的旧棉被,已呈深黄色,看不出原来的本来面目,还若有若无的散发着淡淡的霉味。
public class TtsTask : IScheduleTask public class TtsTask : IScheduleTask
{ {
@ -38,7 +38,11 @@ namespace CloudBuilder.Topshelf.Task.TTS
if (bodyDict.ContainsKey("book_id")) if (bodyDict.ContainsKey("book_id"))
bookId = bodyDict["book_id"]; bookId = bodyDict["book_id"];
AiSentenceViewEntity[] ents = repository.DetachedEntities.Where(x => x.BookId == bookId).OrderBy(x => x.ChapterId).ThenBy(x => x.ParagraphId).ThenBy(x => x.SentenceIndex).ToArray(); string chapterId = string.Empty;
if (bodyDict.ContainsKey("chapter_id"))
chapterId = bodyDict["chapter_id"];
AiSentenceViewEntity[] ents = repository.DetachedEntities.Where(x => x.BookId == bookId && (string.IsNullOrEmpty(chapterId) || x.ChapterId == Convert.ToInt32(chapterId))).OrderBy(x => x.ChapterId).ThenBy(x => x.ParagraphId).ThenBy(x => x.SentenceIndex).ToArray();
if (ents == null || ents.Length == 0) return; if (ents == null || ents.Length == 0) return;
@ -55,10 +59,15 @@ namespace CloudBuilder.Topshelf.Task.TTS
if (bodyDict.ContainsKey("save_path")) if (bodyDict.ContainsKey("save_path"))
save_path = bodyDict["save_path"]; save_path = bodyDict["save_path"];
GenerateAndMergeChapterAudio(ents, actor, actor_voice, voice, save_path); float speed = 1.0f;
if (bodyDict.ContainsKey("speed"))
speed = Convert.ToSingle(bodyDict["speed"]);
GenerateAndMergeChapterAudio(ents, speed, actor, actor_voice, voice, save_path);
} }
public void GenerateAndMergeChapterAudio(AiSentenceViewEntity[] ents, string actor, string actor_voice, string mainVoice, string save_path) public void GenerateAndMergeChapterAudio(AiSentenceViewEntity[] ents, float speed, string actor, string actor_voice, string mainVoice, string save_path)
{ {
// 1. 按 BookId + ChapterId 分组(关键!) // 1. 按 BookId + ChapterId 分组(关键!)
var chapterGroups = ents var chapterGroups = ents
@ -69,7 +78,7 @@ namespace CloudBuilder.Topshelf.Task.TTS
int totalLines = ents.Count(); int totalLines = ents.Count();
int processedLines = 0; // 已处理行数计数器 int processedLines = 0; // 已处理行数计数器
UpdateProgress(processedLines, totalLines); UpdateProgress(processedLines, totalLines);
Dictionary<string, string> personMapping = new Dictionary<string, string>();
foreach (var chapter in chapterGroups) foreach (var chapter in chapterGroups)
{ {
string bookId = chapter.Key.BookId; string bookId = chapter.Key.BookId;
@ -85,9 +94,27 @@ namespace CloudBuilder.Topshelf.Task.TTS
if (ent.DialogueIndc == YesNoPolicy.YES) if (ent.DialogueIndc == YesNoPolicy.YES)
{ {
if (ent.PersonName == actor) if (ent.PersonName == actor)
voice = actor_voice; {
if (personMapping.ContainsKey(ent.PersonName))
voice = personMapping[ent.PersonName];
else
{
voice = actor_voice;
personMapping.Add(ent.PersonName, voice);
}
}
else else
voice = AI.Policy.ChineseTtsVoiceRandom.GetRandom().ToString(); {
if (!string.IsNullOrEmpty(ent.PersonName) && personMapping.ContainsKey(ent.PersonName))
voice = personMapping[ent.PersonName];
else
{
voice = AI.Policy.ChineseTtsVoiceRandom.GetRandom(80).ToString();
personMapping.Add(ent.PersonName, voice);
}
}
} }
else else
{ {
@ -102,7 +129,7 @@ namespace CloudBuilder.Topshelf.Task.TTS
string filePath = Path.Combine(save_path, fileName); string filePath = Path.Combine(save_path, fileName);
// 生成音频 // 生成音频
SaveAudioToFileHelper.AudioToFile(EnumHelper.ToEnum<ChineseTtsVoice>(voice), ent.Content, filePath); SaveAudioToFileHelper.AudioToFile(EnumHelper.ToEnum<ChineseTtsVoice>(voice), speed, ent.Content, filePath);
tempMp3List.Add(filePath); // 加入合并列表 tempMp3List.Add(filePath); // 加入合并列表

View File

@ -62,6 +62,20 @@ namespace CloudBuilder.Topshelf.Utility
_originalErrorHandle = GetStdHandle(STD_ERROR_HANDLE); _originalErrorHandle = GetStdHandle(STD_ERROR_HANDLE);
SetStdHandle(STD_ERROR_HANDLE, IntPtr.Zero); SetStdHandle(STD_ERROR_HANDLE, IntPtr.Zero);
} }
public static void UpdateProgress(int processed, int total)
{
double progressPercent = (double)processed / total * 100;
int progressBarLength = 50; // 进度条总长度
int filledLength = (int)(progressPercent / 100 * progressBarLength);
// 构建进度条(如:[██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░]
string progressBar = "[" + new string('█', filledLength) + new string('░', progressBarLength - filledLength) + "]";
string progressInfo = $"{progressBar} {progressPercent:F2}% | 已处理:{processed}/{total} ";
Console.Write($"\r{progressInfo}");
Console.Out.Flush();
}
} }
} }

View File

@ -97,7 +97,7 @@ namespace CloudBuilder.Topshelf.Utility
public static void AudioToFile(ChineseTtsVoice actorName, string text, string saveFilePath) public static void AudioToFile(ChineseTtsVoice actorName, float speed, string text, string saveFilePath)
{ {
InitConfig(); InitConfig();
int sid = 0; int sid = 0;
@ -113,7 +113,7 @@ namespace CloudBuilder.Topshelf.Utility
case ChineseTtsVoice.Fanchen14: case ChineseTtsVoice.Fanchen14:
case ChineseTtsVoice.Fanchen36: case ChineseTtsVoice.Fanchen36:
sid = actorName.GetSidId(); sid = actorName.GetSidId();
audio = fanchenTTs.Generate(text, 1.0f, sid); audio = fanchenTTs.Generate(text, speed, sid);
break; break;
case ChineseTtsVoice.Sherpa0: case ChineseTtsVoice.Sherpa0:
case ChineseTtsVoice.Sherpa1: case ChineseTtsVoice.Sherpa1:
@ -121,19 +121,19 @@ namespace CloudBuilder.Topshelf.Utility
case ChineseTtsVoice.Sherpa3: case ChineseTtsVoice.Sherpa3:
case ChineseTtsVoice.Sherpa4: case ChineseTtsVoice.Sherpa4:
sid = actorName.GetSidId(); sid = actorName.GetSidId();
audio = sherpaTTs.Generate(text, 1.0f, sid); audio = sherpaTTs.Generate(text, speed, sid);
break; break;
case ChineseTtsVoice.Melo: case ChineseTtsVoice.Melo:
sid = actorName.GetSidId(); sid = actorName.GetSidId();
audio = meloTTs.Generate(text, 1.0f, sid); audio = meloTTs.Generate(text, speed, sid);
break; break;
case ChineseTtsVoice.Eula: case ChineseTtsVoice.Eula:
sid = actorName.GetSidId(); sid = actorName.GetSidId();
audio = eulaTTs.Generate(text, 1.0f, sid); audio = eulaTTs.Generate(text, speed, sid);
break; break;
case ChineseTtsVoice.Theresa: case ChineseTtsVoice.Theresa:
sid = actorName.GetSidId(); sid = actorName.GetSidId();
audio = theresaTTs.Generate(text, 1.0f, sid); audio = theresaTTs.Generate(text, speed, sid);
break; break;
case ChineseTtsVoice.Aaishe1: case ChineseTtsVoice.Aaishe1:
case ChineseTtsVoice.Aaishe6: case ChineseTtsVoice.Aaishe6:
@ -142,11 +142,11 @@ namespace CloudBuilder.Topshelf.Utility
case ChineseTtsVoice.Aaishe18: case ChineseTtsVoice.Aaishe18:
case ChineseTtsVoice.Aaishe10: case ChineseTtsVoice.Aaishe10:
sid = actorName.GetSidId(); sid = actorName.GetSidId();
audio = icefallTTs.Generate(text, 1.0f, sid); audio = icefallTTs.Generate(text, speed, sid);
break; break;
default: default:
sid = actorName.GetSidId(); sid = actorName.GetSidId();
audio = matchaTTs.Generate(text, 1.0f, sid); audio = matchaTTs.Generate(text, speed, sid);
break; break;
} }

View File

@ -1,15 +1,6 @@
using CloudBuilder.AI.Entity; using CloudBuilder.AI.Entity;
using CloudBuilder.Core.DatabaseAccessor.Entity; using CloudBuilder.Core.DatabaseAccessor.Entity;
using CloudBuilder.Topshelf.Python; using CloudBuilder.Topshelf.Python;
using System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;
namespace CloudBuilder.Topshelf.Utility namespace CloudBuilder.Topshelf.Utility
{ {
@ -104,7 +95,7 @@ namespace CloudBuilder.Topshelf.Utility
} }
//如果主语==动作发出者 //如果主语==动作发出者
//if (!string.IsNullOrEmpty(person) && !ps.Contains(person) && !ps.Contains(string.Format("[{0}]", person))) ps.Add(string.Format("[{0}]", person)); if (!string.IsNullOrEmpty(person) && !ps.Contains(person) && !ps.Contains(string.Format("[{0}]", person))) ps.Add(string.Format("【{0}】", person));
//动作接受者 //动作接受者
//if (!string.IsNullOrEmpty(arg1) && !ps.Contains(arg1)) ps.Add(arg1); //if (!string.IsNullOrEmpty(arg1) && !ps.Contains(arg1)) ps.Add(arg1);
@ -191,22 +182,83 @@ namespace CloudBuilder.Topshelf.Utility
AiSentenceEntity aiSentence; AiSentenceEntity aiSentence;
string dialogueName = string.Empty; string dialogueName = string.Empty;
int totalLines = aiSentences.Length;
int processedLines = 0; // 已处理行数计数器
Console.WriteLine($"\r");
foreach (AiSentenceViewEntity aiSentenceView in aiSentences) foreach (AiSentenceViewEntity aiSentenceView in aiSentences)
{ {
dialogueName= string.Empty; aiSentenceView.PersonName = null;
processedLines++;
ConsoleOutput.UpdateProgress(processedLines, totalLines);
if (aiSentenceView.DialogueIndc != YesNoPolicy.YES) continue; if (aiSentenceView.DialogueIndc != YesNoPolicy.YES) continue;
//A说“B,你好!”/“A,你好”B对A说。
if (aiSentences.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphId == aiSentenceView.ParagraphId).Count() > 1) if (aiSentences.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphId == aiSentenceView.ParagraphId).Count() > 1)
{
dialogueName = string.Empty;
//提取对话中的人物
if (AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex == x.SentenceIndex).Any())
{
dialogueName = AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex == x.SentenceIndex).FirstOrDefault().PersonName;
}
//提取对话段落中,非对话内容中的人物
if (AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex != x.SentenceIndex && x.PersonName != dialogueName).Any())
{
aiSentenceView.PersonName = AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex != x.SentenceIndex && x.PersonName != dialogueName).OrderBy(x => x.OrderIndex).FirstOrDefault().PersonName;
if (aiSentenceView.PersonName.Contains("【"))
{
aiSentenceView.PersonName = AiChapterPersons.Where(x => !x.PersonName.Contains("【") && x.PersonName.Contains(aiSentenceView.PersonName.Replace("【", "").Replace("】", ""))).FirstOrDefault()!.PersonName;
}
}
//说话者在前面
if (aiSentenceView.SentenceIndex == 2)
{
//如果没有提取到人物,优先向前,再向后
if (string.IsNullOrEmpty(aiSentenceView.PersonName))
{
aiSentenceView.PersonName = GetForwardName(dialogueName, aiSentenceView, aiSentences);
}
//如果没有提取到人物,优先向前,再向后
if (string.IsNullOrEmpty(aiSentenceView.PersonName))
{
aiSentenceView.PersonName = GetBackwardName(dialogueName, aiSentenceView, aiSentences);
}
}
else
{
if (string.IsNullOrEmpty(aiSentenceView.PersonName))
{
aiSentenceView.PersonName = GetBackwardName(dialogueName, aiSentenceView, aiSentences);
}
if (string.IsNullOrEmpty(aiSentenceView.PersonName))
{
aiSentenceView.PersonName = GetForwardName(dialogueName, aiSentenceView, aiSentences);
}
}
//如果“你好”,前后面有說話者的,单独内容,下下文不能是同一個人
dialogueName = aiSentenceView.PersonName;
//dialogueName = string.Empty;
}
else
{ {
if (AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex == x.SentenceIndex).Any()) if (AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex == x.SentenceIndex).Any())
{ {
dialogueName = AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex == x.SentenceIndex).FirstOrDefault().PersonName; dialogueName = AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex == x.SentenceIndex).FirstOrDefault().PersonName;
} }
//"你好,"張三說道
//"你好"
if (aiSentences.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphId == aiSentenceView.ParagraphId - 1 && x.DialogueIndc == YesNoPolicy.NO).Count() > 0 && aiSentences.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphId == aiSentenceView.ParagraphId + 1 && x.DialogueIndc == YesNoPolicy.YES).Count() == 1) dialogueName = string.Empty;
if (AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex != x.SentenceIndex && x.PersonName != dialogueName).Any()) //如果没有提取到人物,优先向前,再向后
aiSentenceView.PersonName = GetForwardName(dialogueName, aiSentenceView, aiSentences, 3);
//如果没有提取到人物,优先向前,再向后
if (string.IsNullOrEmpty(aiSentenceView.PersonName))
{ {
aiSentenceView.PersonName = AiChapterPersons.Where(x =>x.ChapterId==aiSentenceView.ChapterId && x.ParagraphIndex == aiSentenceView.ParagraphId && aiSentenceView.SentenceIndex != x.SentenceIndex && x.PersonName != dialogueName).OrderBy(x => x.OrderIndex).FirstOrDefault().PersonName; aiSentenceView.PersonName = GetBackwardName(dialogueName, aiSentenceView, aiSentences, 5);
} }
//如果“你好”,前后面有說話者的,单独内容,下下文不能是同一個人
dialogueName = aiSentenceView.PersonName;
} }
aiSentence = new AiSentenceEntity(); aiSentence = new AiSentenceEntity();
@ -215,9 +267,86 @@ namespace CloudBuilder.Topshelf.Utility
} }
} }
public string GetForwardName(string excludeName, AiSentenceViewEntity aiSentenceView, AiSentenceViewEntity[] aiSentences, int maxLength = 10)
{
foreach (var cp in AiChapterPersons.Where(x => x.DialogueIndc != YesNoPolicy.YES && x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex < aiSentenceView.ParagraphId && x.OrderIndex == 1).OrderByDescending(x => x.ParagraphIndex).ToArray())
{
if (aiSentences.Where(x => x.ChapterId == cp.ChapterId && x.ParagraphId == cp.ParagraphIndex
&& x.DialogueIndc == YesNoPolicy.YES).Any())
{
if (aiSentences.Where(x => x.ChapterId == cp.ChapterId && x.ParagraphId == cp.ParagraphIndex
&& x.DialogueIndc == YesNoPolicy.NO).Any())
{
if (string.IsNullOrEmpty(cp.PersonName) || cp.PersonName.Contains("【")) continue;
if (!string.IsNullOrEmpty(excludeName) && excludeName == cp.PersonName) continue;
if (!string.IsNullOrEmpty(excludeName) && AreSamePerson(excludeName, cp.PersonName)) continue;
if (aiSentenceView.ParagraphId - cp.ParagraphIndex > maxLength) break;
return cp.PersonName;
}
break;
}
if (string.IsNullOrEmpty(cp.PersonName) || cp.PersonName.Contains("【")) continue;
if (!string.IsNullOrEmpty(excludeName) && excludeName == cp.PersonName) continue;
if (!string.IsNullOrEmpty(excludeName) && AreSamePerson(excludeName, cp.PersonName)) continue;
if (aiSentenceView.ParagraphId - cp.ParagraphIndex > maxLength) break;
return cp.PersonName;
}
return null;
}
public string GetBackwardName(string excludeName, AiSentenceViewEntity aiSentenceView, AiSentenceViewEntity[] aiSentences, int maxLength = 10)
{
foreach (var cp in AiChapterPersons.Where(x => x.ChapterId == aiSentenceView.ChapterId && x.ParagraphIndex > aiSentenceView.ParagraphId).OrderBy(x => x.ParagraphIndex).ThenBy(x => x.OrderIndex).ToArray())
{
if (aiSentences.Where(x => x.ChapterId == cp.ChapterId && x.ParagraphId == cp.ParagraphIndex && x.DialogueIndc == YesNoPolicy.YES).Any()) break;
if (string.IsNullOrEmpty(cp.PersonName) || cp.PersonName.Contains("【")) continue;
if (!string.IsNullOrEmpty(excludeName) && aiSentenceView.PersonName == cp.PersonName) continue;
if (!string.IsNullOrEmpty(excludeName) && AreSamePerson(excludeName, cp.PersonName)) continue;
if (cp.ParagraphIndex - aiSentenceView.ParagraphId > maxLength) break;
return cp.PersonName;
}
return null;
}
public bool AreSamePerson(string nameA, string nameB)
{
if (string.IsNullOrWhiteSpace(nameA) || string.IsNullOrWhiteSpace(nameB))
return false;
if (nameA.Substring(0, 1) == nameB.Substring(0, 1) && nameA != nameB)
return false;
string surnameA = ExtractSurname(nameA);
string surnameB = ExtractSurname(nameB);
return !string.IsNullOrEmpty(surnameA) && surnameA == surnameB;
}
private string ExtractSurname(string name)
{
// 优先提取第一个字符(常见“姓+后缀”模式)
if (name.Length > 0 && CommonSurnames.Contains(name[0].ToString()))
return name[0].ToString();
// 对于“韩胖子”这类姓不在首位的情况,扫描整个字符串
foreach (char c in name)
{
if (CommonSurnames.Contains(c.ToString()))
return c.ToString();
}
return null;
}
public AiSentenceEntity[] AiSentences { get; set; } public AiSentenceEntity[] AiSentences { get; set; }
public AiChapterPersonEntity[] AiChapterPersons { get; set; } public AiChapterPersonEntity[] AiChapterPersons { get; set; }
public List<string> CommonSurnames { get; set; }
} }
} }
/* /*