tts中英音色的补充&英文页面的标题文字修改

This commit is contained in:
1152450298 committed 2025-12-29 10:34:24 +08:00
1 parent cd488022dd
commit 349f41f953
4 files changed
+145 -63

No files matched your search

+84
View File
@@ -22,6 +22,90 @@ from typing import List, Dict, Any
# Edge TTS voice presets for local inference
EDGE_TTS_VOICES: List[Dict[str, Any]] = [
# Chinese voices
{
"id": "zh-CN-XiaoxiaoNeural",
"label_key": "tts.voice.zh_CN_XiaoxiaoNeural",
"locale": "zh-CN",
"gender": "female"
},
{
"id": "zh-CN-XiaoyiNeural",
"label_key": "tts.voice.zh_CN_XiaoyiNeural",
"locale": "zh-CN",
"gender": "female"
},
{
"id": "zh-CN-YunjianNeural",
"label_key": "tts.voice.zh_CN_YunjianNeural",
"locale": "zh-CN",
"gender": "male"
},
{
"id": "zh-CN-YunxiNeural",
"label_key": "tts.voice.zh_CN_YunxiNeural",
"locale": "zh-CN",
"gender": "male"
},
{
"id": "zh-CN-YunyangNeural",
"label_key": "tts.voice.zh_CN_YunyangNeural",
"locale": "zh-CN",
"gender": "male"
},
{
"id": "zh-CN-YunyeNeural",
"label_key": "tts.voice.zh_CN_YunyeNeural",
"locale": "zh-CN",
"gender": "male"
},
{
"id": "zh-CN-YunfengNeural",
"label_key": "tts.voice.zh_CN_YunfengNeural",
"locale": "zh-CN",
"gender": "male"
},
{
"id": "zh-CN-liaoning-XiaobeiNeural",
"label_key": "tts.voice.zh_CN_liaoning_XiaobeiNeural",
"locale": "zh-CN",
"gender": "female"
},
{
"id": "en-US-AriaNeural",
"label_key": "tts.voice.en_US_AriaNeural",
"locale": "en-US",
"gender": "female"
},
{
"id": "en-US-JennyNeural",
"label_key": "tts.voice.en_US_JennyNeural",
"locale": "en-US",
"gender": "female"
},
{
"id": "en-US-GuyNeural",
"label_key": "tts.voice.en_US_GuyNeural",
"locale": "en-US",
"gender": "male"
},
{
"id": "en-US-DavisNeural",
"label_key": "tts.voice.en_US_DavisNeural",
"locale": "en-US",
"gender": "male"
},
{
"id": "en-GB-SoniaNeural",
"label_key": "tts.voice.en_GB_SoniaNeural",
"locale": "en-GB",
"gender": "female"
},
{
"id": "en-GB-RyanNeural",
"label_key": "tts.voice.en_GB_RyanNeural",
"locale": "en-GB",
"gender": "male"
},
{
"id": "ko-KR-InJoonNeural",
"label_key": "tts.voice.ko-KR-InJoonNeural",
+38 -34
View File
@@ -203,6 +203,20 @@
"tts.voice_selector": "Voice Selection",
"tts.speed": "Speed",
"tts.speed_label": "{speed}x",
"tts.voice.zh_CN_XiaoxiaoNeural": "zh-CN-XiaoxiaoNeural",
"tts.voice.zh_CN_XiaoyiNeural": "zh-CN-XiaoyiNeural",
"tts.voice.zh_CN_YunjianNeural": "zh-CN-YunjianNeural",
"tts.voice.zh_CN_YunxiNeural": "zh-CN-YunxiNeural",
"tts.voice.zh_CN_YunyangNeural": "zh-CN-YunyangNeural",
"tts.voice.zh_CN_YunyeNeural": "zh-CN-YunyeNeural",
"tts.voice.zh_CN_YunfengNeural": "zh-CN-YunfengNeural",
"tts.voice.zh_CN_liaoning_XiaobeiNeural": "zh-CN-liaoning-XiaobeiNeural",
"tts.voice.en_US_AriaNeural": "en-US-AriaNeural",
"tts.voice.en_US_JennyNeural": "en-US-JennyNeural",
"tts.voice.en_US_GuyNeural": "en-US-GuyNeural",
"tts.voice.en_US_DavisNeural": "en-US-DavisNeural",
"tts.voice.en_GB_SoniaNeural": "en-GB-SoniaNeural",
"tts.voice.en_GB_RyanNeural": "en-GB-RyanNeural",
"tts.voice.ko-KR-InJoonNeural": "ko-KR-InJoonNeural",
"tts.voice.ko-KR-SunHiNeural": "ko-KR-SunHiNeural",
"tts.voice.fr-FR-EloiseNeural": "fr-FR-EloiseNeural",
@@ -350,40 +364,6 @@
"pipeline.custom_media.description": "Use your own photos/videos, AI adds narration and voiceover",
"pipeline.digital_human.name": "Digital Human Generate",
"pipeline.digital_human.description": "Using a piece of text, two images, and an audio clip, AI can help you generate a digital human video.",
"digital_human.section.character_assets": "😊 Character Image Upload",
"digital_human.assets.character_what": "Upload digital human character images",
"digital_human.assets.how": "Supports JPG/PNG/WebP format images, recommend clear and relevant materials",
"digital_human.assets.upload": "Upload Assets",
"digital_human.assets.upload_help": "Only supports single image upload",
"digital_human.assets.character_sucess": "Character image uploaded successfully",
"digital_human.assets.preview": "📷 Asset Preview",
"digital_human.assets.character_empty_hint": "💡 Please upload a digital human character image",
"digital_human.section.goods_assets": "👝 Product Image Upload",
"digital_human.assets.goods_what": "Upload product images",
"digital_human.assets.goods_sucess": "Product image uploaded successfully",
"digital_human.assets.goods_empty_hint": "💡 Please upload a product image",
"digital_human.section.goods_info": "🗃️ Product Information",
"digital_human.goods_title": "Product Name",
"digital_human.goods_title_placeholder": "e.g.: lipstick, coffee machine, robot vacuum, etc.",
"digital_human.goods_title_help": "Product name, cannot be empty",
"digital_human.section.workflow": "⚙️ Workflow Configuration",
"digital_human.workflow.what": "Configure the two-step workflow for digital human video generation: first step generates collage and copy, second step generates final video",
"digital_human.workflow.how": "Select appropriate workflows to handle different generation steps, ensuring output quality",
"digital_human.workflow.first_step": "First Step Workflow",
"digital_human.workflow.first_select": "Select First Step Workflow",
"digital_human.workflow.first_help": "Used to generate collage and copy based on character image, product image and product name",
"digital_human.workflow.second_step": "Second Step Workflow",
"digital_human.workflow.second_select": "Select Second Step Workflow",
"digital_human.workflow.second_help": "Used to generate final digital human video based on generated image and audio",
"digital_human.workflow.selected_info": "Selected workflow configuration",
"digital_human.workflow.auto_selected": "Auto-selected workflow",
"digital_human.workflow.configuration": "Workflow Configuration Status",
"digital_human.workflow.not_found": "not found",
"digital_human.workflow.ready": "Digital human workflows are ready, you can start generating videos",
"digital_human.workflow.missing": "Missing required digital human workflows, please check configuration",
"digital_human.workflow.advanced_options": "Advanced Options (Manual Override)",
"digital_human.workflow.manual_override": "Manual Workflow Selection (use only when needed)",
"digital_tts.what": "Convert narration text to natural human-like speech",
"asset_based.section.assets": "📦 Asset Upload",
"asset_based.section.video_info": "📝 Video Information",
"asset_based.section.source": "⚙️ Service Configuration",
@@ -421,6 +401,30 @@
"asset_based.progress.generating_script": "📝 Generating video script...",
"asset_based.progress.script_complete": "✅ Script generation complete",
"asset_based.progress.concat_complete": "✅ Video concatenation complete",
"digital_human.section.character_assets": "😊 Upload a digital human character image",
"digital_human.assets.character_what": "Upload an image of your digital human character",
"digital_human.assets.how": "Supports JPG/PNG/WebP formats. Clear and relevant images are recommended.",
"digital_human.assets.upload": "Upload the image.",
"digital_human.assets.upload_help": "Single image upload only",
"digital_human.assets.character_sucess": "Image upload successful",
"digital_human.assets.preview": "📷 Image preview",
"digital_human.assets.character_empty_hint": "💡 Please upload a digital human character image",
"digital_human.section.goods_assets": "👝 Upload product images",
"digital_human.assets.goods_what": "Upload an image of your product",
"digital_human.assets.goods_sucess": "Product image uploaded successfully",
"digital_human.assets.goods_empty_hint": "💡 Please upload a product image",
"digital_human.section.goods_info": "🗃️ Product Information",
"digital_human.goods_title": "Product Name",
"digital_human.goods_title_placeholder": "Example: Lipstick, Coffee Machine, Robot Vacuum Cleaner, etc.",
"digital_human.goods_title_help": "Product Name, cannot be empty",
"digital_human.section.workflow": "⚙️ Workflow Loading",
"digital_human.workflow.what": "Configure the two-step workflow for digital human video generation: Step 1 generates the collage and text, Step 2 generates the final video",
"digital_human.workflow.first_step": "Voiceover & collage workflow successfully loaded",
"digital_human.workflow.second_step": "Digital human synthesis workflow loaded successfully",
"digital_human.workflow.ready": "Digital human workflow is ready to start video generation",
"digital_human.workflow.missing": "Necessary digital human workflow is missing, please check configuration",
"digital_tts.what": "Converts narration text into natural, lifelike speech",
"faq.expand_to_view": "FAQ",
"faq.load_error": "Failed to load FAQ content",
"faq.more_help": "Need more help?"
+14
View File
@@ -203,6 +203,20 @@
"tts.voice_selector": "音色选择",
"tts.speed": "语速",
"tts.speed_label": "{speed}x",
"tts.voice.zh_CN_XiaoxiaoNeural": "女声-温柔(晓晓)",
"tts.voice.zh_CN_XiaoyiNeural": "女声-甜美(晓伊)",
"tts.voice.zh_CN_YunjianNeural": "男声-专业(云健)",
"tts.voice.zh_CN_YunxiNeural": "男声-磁性(云希)",
"tts.voice.zh_CN_YunyangNeural": "男声-新闻(云扬)",
"tts.voice.zh_CN_YunyeNeural": "男声-自然(云野)",
"tts.voice.zh_CN_YunfengNeural": "男声-沉稳(云锋)",
"tts.voice.zh_CN_liaoning_XiaobeiNeural": "女声-东北(小北)",
"tts.voice.en_US_AriaNeural": "女声-自然(Aria)",
"tts.voice.en_US_JennyNeural": "女声-温暖(Jenny)",
"tts.voice.en_US_GuyNeural": "男声-标准(Guy)",
"tts.voice.en_US_DavisNeural": "男声-友好(Davis)",
"tts.voice.en_GB_SoniaNeural": "女声-英式(Sonia)",
"tts.voice.en_GB_RyanNeural": "男声-英式(Ryan)",
"tts.voice.ko-KR-InJoonNeural": "KR 男声-友好(仁俊)",
"tts.voice.ko-KR-SunHiNeural": "KR 女声-友好(善海)",
"tts.voice.fr-FR-EloiseNeural": "FR 女声-友好(埃洛伊斯)",
+9 -29
View File
@@ -194,7 +194,6 @@ class DigitalHumanPipelineUI(PipelineUI):
tts_voice = video_params.get("tts_voice", "zh-CN-YunjianNeural")
tts_speed = video_params.get("tts_speed", 1.2)
# Debug: 显示获取到的TTS参数
logger.info(f"🔧 获取到的TTS参数:")
logger.info(f" - tts_voice: {tts_voice}")
logger.info(f" - tts_speed: {tts_speed}")
@@ -274,8 +273,6 @@ class DigitalHumanPipelineUI(PipelineUI):
first_workflow_config = json.load(f)
# Build workflow parameters for first workflow
# 注意:参数名需要根据你的 workflow 实际参数来调整
# 尝试多种可能的参数名
first_workflow_params = {
# 根据之前的日志尝试的参数名
"firstimage": character_assets[0],
@@ -321,8 +318,7 @@ class DigitalHumanPipelineUI(PipelineUI):
if first_result.outputs:
for node_id, node_output in first_result.outputs.items():
logger.info(f" - 节点 {node_id}: {node_output}")
# 尝试获取所有可能的属性
for attr in dir(first_result):
if not attr.startswith('_'):
try:
@@ -336,13 +332,11 @@ class DigitalHumanPipelineUI(PipelineUI):
generated_image_url = None
generated_text = None
generated_video_url = None
# 检查是否直接返回了视频(一步到位的工作流)
if hasattr(first_result, 'videos') and first_result.videos:
generated_video_url = first_result.videos[0]
logger.info(f"✅ 第一步工作流直接生成了视频: {generated_video_url}")
# 如果直接生成了视频,跳过后续步骤
progress_bar.progress(100)
status_text.text(tr("status.success"))
@@ -357,7 +351,6 @@ class DigitalHumanPipelineUI(PipelineUI):
return final_video_path
# 如果没有直接返回视频,尝试提取图片和文本(原来的逻辑)
# Extract image - try direct access first
if hasattr(first_result, 'images') and first_result.images:
generated_image_url = first_result.images[0]
@@ -388,17 +381,14 @@ class DigitalHumanPipelineUI(PipelineUI):
break
if not generated_image_url:
# 如果没有找到图片,尝试查看是否有其他类型的输出
logger.error("❌ 第一步工作流未返回图片,尝试查找其他输出...")
if hasattr(first_result, 'outputs') and first_result.outputs:
for node_id, node_output in first_result.outputs.items():
logger.info(f"- 节点 {node_id} 输出: {node_output}")
# 尝试使用第一个可用的图片输出(如果有的话)
if hasattr(first_result, 'outputs') and first_result.outputs:
for node_id, node_output in first_result.outputs.items():
if isinstance(node_output, dict):
# 检查所有可能的图片字段
for key in ['images', 'image', 'output_image', 'result_image']:
if key in node_output and node_output[key]:
if isinstance(node_output[key], list) and len(node_output[key]) > 0:
@@ -416,12 +406,10 @@ class DigitalHumanPipelineUI(PipelineUI):
raise Exception("第一步工作流未返回图片,请检查工作流配置")
if not generated_text:
# 如果没有找到文本,尝试查找其他文本输出
logger.warning("⚠️ 第一步工作流未返回文本,尝试查找其他文本输出...")
if hasattr(first_result, 'outputs') and first_result.outputs:
for node_id, node_output in first_result.outputs.items():
if isinstance(node_output, dict):
# 检查所有可能的文本字段
for key in ['text', 'texts', 'output_text', 'result_text', 'description', 'caption']:
if key in node_output and node_output[key]:
if isinstance(node_output[key], list) and len(node_output[key]) > 0:
@@ -435,7 +423,6 @@ class DigitalHumanPipelineUI(PipelineUI):
if generated_text:
break
# 如果还是没有文本,使用默认文本
if not generated_text:
generated_text = f"为您推荐这款{goods_title},品质优良,值得拥有!"
logger.warning(f"⚠️ 使用默认文本: {generated_text}")
@@ -444,7 +431,7 @@ class DigitalHumanPipelineUI(PipelineUI):
# Download generated image to local
generated_image_path = os.path.join(task_dir, "generated_image.png")
timeout = httpx.Timeout(10.0) # 简化timeout设置
timeout = httpx.Timeout(10.0)
async with httpx.AsyncClient(timeout=timeout) as client:
response = await client.get(generated_image_url)
response.raise_for_status()
@@ -461,8 +448,8 @@ class DigitalHumanPipelineUI(PipelineUI):
await pixelle_video.tts(
text=generated_text,
output_path=audio_path,
inference_mode="local", # 明确指定推理模式
voice=tts_voice, # 使用voice而不是voice_id
inference_mode="local",
voice=tts_voice,
speed=tts_speed
)
@@ -482,12 +469,9 @@ class DigitalHumanPipelineUI(PipelineUI):
second_workflow_config = json.load(f)
# Build workflow parameters for second workflow
# 注意:参数名需要根据你的 workflow 实际参数来调整
# 尝试多种可能的参数名
second_workflow_params = {
# 尝试常见的参数名
"videoimage": generated_image_path, # 生成的图片路径
"audio": audio_path, # 音频路径
"videoimage": generated_image_path,
"audio": audio_path,
}
logger.info(f"🔧 传递给第二步工作流的参数:")
@@ -523,7 +507,6 @@ class DigitalHumanPipelineUI(PipelineUI):
for node_id, node_output in second_result.outputs.items():
logger.info(f" - 节点 {node_id}: {node_output}")
# 尝试获取所有可能的属性
for attr in dir(second_result):
if not attr.startswith('_'):
try:
@@ -550,17 +533,14 @@ class DigitalHumanPipelineUI(PipelineUI):
break
if not generated_video_url:
# 如果没有找到视频,尝试查看是否有其他类型的输出
logger.error("❌ 第二步工作流未返回视频,尝试查找其他输出...")
if hasattr(second_result, 'outputs') and second_result.outputs:
for node_id, node_output in second_result.outputs.items():
logger.info(f"- 节点 {node_id} 输出: {node_output}")
# 尝试使用第一个可用的视频输出(如果有的话)
if hasattr(second_result, 'outputs') and second_result.outputs:
for node_id, node_output in second_result.outputs.items():
if isinstance(node_output, dict):
# 检查所有可能的视频字段
for key in ['videos', 'video', 'output_video', 'result_video', 'mp4']:
if key in node_output and node_output[key]:
if isinstance(node_output[key], list) and len(node_output[key]) > 0:
@@ -581,7 +561,7 @@ class DigitalHumanPipelineUI(PipelineUI):
# Download video to local
final_video_path = os.path.join(task_dir, "final.mp4")
timeout = httpx.Timeout(300.0) # 简化timeout设置
timeout = httpx.Timeout(300.0)
async with httpx.AsyncClient(timeout=timeout) as client:
response = await client.get(generated_video_url)
response.raise_for_status()