tts中英音色的补充&英文页面的标题文字修改
This commit is contained in:
1 parent
cd488022dd
commit
349f41f953
4 files changed
+145
-63
No files matched your search
@@ -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
@@ -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?"
|
||||
|
||||
@@ -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 女声-友好(埃洛伊斯)",
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in new issue
Block a user