Spaces:
Running
Running
fix: 完善云端UI日志,以便追踪错误;添加更多文本
Browse files- src/gui_cloud.py +75 -6
src/gui_cloud.py
CHANGED
|
@@ -27,6 +27,46 @@ logging.basicConfig(
|
|
| 27 |
)
|
| 28 |
logger = logging.getLogger(__name__)
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
# 项目根目录
|
| 31 |
BASE_DIR = Path(__file__).parent.parent.absolute()
|
| 32 |
|
|
@@ -75,19 +115,23 @@ def cleanup_workspace(workspace: str):
|
|
| 75 |
def create_zip(source_dir: str, zip_name: str) -> Optional[str]:
|
| 76 |
"""打包目录为 zip(使用 uuid 避免多用户冲突)"""
|
| 77 |
if not os.path.isdir(source_dir):
|
|
|
|
| 78 |
return None
|
| 79 |
try:
|
| 80 |
unique_id = str(uuid.uuid4())[:8]
|
| 81 |
zip_path = os.path.join(CloudConfig.TEMP_BASE, f"{zip_name}_{unique_id}.zip")
|
| 82 |
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
|
|
|
|
| 83 |
for root, dirs, files in os.walk(source_dir):
|
| 84 |
for file in files:
|
| 85 |
file_path = os.path.join(root, file)
|
| 86 |
arcname = os.path.relpath(file_path, source_dir)
|
| 87 |
zf.write(file_path, arcname)
|
|
|
|
|
|
|
| 88 |
return zip_path
|
| 89 |
except Exception as e:
|
| 90 |
-
logger.error(f"打包失败: {e}")
|
| 91 |
return None
|
| 92 |
|
| 93 |
|
|
@@ -147,6 +191,7 @@ def validate_audio_upload(files) -> Tuple[bool, str, List[str]]:
|
|
| 147 |
return True, f"找到 {len(valid_files)} 个音频文件", valid_files
|
| 148 |
|
| 149 |
|
|
|
|
| 150 |
def process_make_voicebank(
|
| 151 |
audio_files,
|
| 152 |
source_name: str,
|
|
@@ -159,13 +204,20 @@ def process_make_voicebank(
|
|
| 159 |
|
| 160 |
返回: (状态, 日志, 下载文件路径, 会话存储的音源包路径)
|
| 161 |
"""
|
| 162 |
-
from src.pipeline import PipelineConfig, VoiceBankPipeline
|
| 163 |
-
|
| 164 |
logs = []
|
|
|
|
|
|
|
| 165 |
def log(msg):
|
| 166 |
logs.append(msg)
|
| 167 |
logger.info(msg)
|
| 168 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
# 验证输入
|
| 170 |
if not source_name or not source_name.strip():
|
| 171 |
return "❌ 请输入音源名称", "", None, None
|
|
@@ -191,10 +243,23 @@ def process_make_voicebank(
|
|
| 191 |
|
| 192 |
# 复制音频文件到输入目录
|
| 193 |
progress(0.05, desc="复制音频文件...")
|
|
|
|
| 194 |
for src_path in file_paths:
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
|
| 199 |
# 获取 MFA 模型路径
|
| 200 |
mfa_models = scan_mfa_models()
|
|
@@ -374,6 +439,7 @@ def validate_voicebank_zip(zip_file) -> Tuple[bool, str, Optional[str]]:
|
|
| 374 |
return validate_voicebank_zip_path(zip_path)
|
| 375 |
|
| 376 |
|
|
|
|
| 377 |
def process_export_voicebank(
|
| 378 |
zip_file,
|
| 379 |
plugin_name: str,
|
|
@@ -736,6 +802,7 @@ def create_cloud_ui():
|
|
| 736 |
with gr.Tab("🎵 制作音源"):
|
| 737 |
gr.Markdown("### 上传音频文件")
|
| 738 |
gr.Markdown("支持格式: WAV, MP3, FLAC, OGG, M4A")
|
|
|
|
| 739 |
|
| 740 |
audio_upload = gr.File(
|
| 741 |
label="上传音频文件",
|
|
@@ -776,6 +843,8 @@ def create_cloud_ui():
|
|
| 776 |
|
| 777 |
gr.Markdown("""
|
| 778 |
> ⏱️ **模型速度参考**:small 约 4 秒/句,medium 约 12 秒/句(medium 慢 2-3 倍但更准确)
|
|
|
|
|
|
|
| 779 |
""")
|
| 780 |
|
| 781 |
make_btn = gr.Button("🚀 开始制作", variant="primary", size="lg", interactive=False)
|
|
|
|
| 27 |
)
|
| 28 |
logger = logging.getLogger(__name__)
|
| 29 |
|
| 30 |
+
|
| 31 |
+
def safe_gradio_handler(func):
|
| 32 |
+
"""
|
| 33 |
+
Gradio 处理函数的安全包装器
|
| 34 |
+
|
| 35 |
+
捕获所有异常并返回友好的错误信息,避免 Gradio 显示默认的"错误"状态
|
| 36 |
+
"""
|
| 37 |
+
import functools
|
| 38 |
+
import traceback
|
| 39 |
+
|
| 40 |
+
@functools.wraps(func)
|
| 41 |
+
def wrapper(*args, **kwargs):
|
| 42 |
+
try:
|
| 43 |
+
return func(*args, **kwargs)
|
| 44 |
+
except Exception as e:
|
| 45 |
+
# 记录完整的异常堆栈
|
| 46 |
+
error_trace = traceback.format_exc()
|
| 47 |
+
logger.error(f"处理函数 {func.__name__} 发生异常:\n{error_trace}")
|
| 48 |
+
|
| 49 |
+
# 根据函数返回值数量返回错误信息
|
| 50 |
+
# 检查函数的类型注解来确定返回值数量
|
| 51 |
+
annotations = getattr(func, '__annotations__', {})
|
| 52 |
+
return_type = annotations.get('return', None)
|
| 53 |
+
|
| 54 |
+
error_msg = f"❌ 系统错误: {str(e)}"
|
| 55 |
+
error_detail = f"异常类型: {type(e).__name__}\n详情: {str(e)}"
|
| 56 |
+
|
| 57 |
+
# 根据函数名判断返回值数量
|
| 58 |
+
if func.__name__ == 'process_make_voicebank':
|
| 59 |
+
return error_msg, error_detail, None, None
|
| 60 |
+
elif func.__name__ == 'process_export_voicebank':
|
| 61 |
+
return error_msg, error_detail, None
|
| 62 |
+
elif func.__name__ == 'collect_and_export':
|
| 63 |
+
return error_msg, error_detail, None
|
| 64 |
+
else:
|
| 65 |
+
# 默认返回单个错误消息
|
| 66 |
+
return error_msg
|
| 67 |
+
|
| 68 |
+
return wrapper
|
| 69 |
+
|
| 70 |
# 项目根目录
|
| 71 |
BASE_DIR = Path(__file__).parent.parent.absolute()
|
| 72 |
|
|
|
|
| 115 |
def create_zip(source_dir: str, zip_name: str) -> Optional[str]:
|
| 116 |
"""打包目录为 zip(使用 uuid 避免多用户冲突)"""
|
| 117 |
if not os.path.isdir(source_dir):
|
| 118 |
+
logger.warning(f"打包失败: 目录不存在 {source_dir}")
|
| 119 |
return None
|
| 120 |
try:
|
| 121 |
unique_id = str(uuid.uuid4())[:8]
|
| 122 |
zip_path = os.path.join(CloudConfig.TEMP_BASE, f"{zip_name}_{unique_id}.zip")
|
| 123 |
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
|
| 124 |
+
file_count = 0
|
| 125 |
for root, dirs, files in os.walk(source_dir):
|
| 126 |
for file in files:
|
| 127 |
file_path = os.path.join(root, file)
|
| 128 |
arcname = os.path.relpath(file_path, source_dir)
|
| 129 |
zf.write(file_path, arcname)
|
| 130 |
+
file_count += 1
|
| 131 |
+
logger.info(f"打包完成: {zip_path} ({file_count} 个文件)")
|
| 132 |
return zip_path
|
| 133 |
except Exception as e:
|
| 134 |
+
logger.error(f"打包失败: {e}", exc_info=True)
|
| 135 |
return None
|
| 136 |
|
| 137 |
|
|
|
|
| 191 |
return True, f"找到 {len(valid_files)} 个音频文件", valid_files
|
| 192 |
|
| 193 |
|
| 194 |
+
@safe_gradio_handler
|
| 195 |
def process_make_voicebank(
|
| 196 |
audio_files,
|
| 197 |
source_name: str,
|
|
|
|
| 204 |
|
| 205 |
返回: (状态, 日志, 下载文件路径, 会话存储的音源包路径)
|
| 206 |
"""
|
|
|
|
|
|
|
| 207 |
logs = []
|
| 208 |
+
workspace = None
|
| 209 |
+
|
| 210 |
def log(msg):
|
| 211 |
logs.append(msg)
|
| 212 |
logger.info(msg)
|
| 213 |
|
| 214 |
+
try:
|
| 215 |
+
# 导入依赖(放在 try 块内以捕获导入错误)
|
| 216 |
+
from src.pipeline import PipelineConfig, VoiceBankPipeline
|
| 217 |
+
except Exception as e:
|
| 218 |
+
logger.error(f"导入模块失败: {e}", exc_info=True)
|
| 219 |
+
return f"❌ 系统错误: 模块加载失败", str(e), None, None
|
| 220 |
+
|
| 221 |
# 验证输入
|
| 222 |
if not source_name or not source_name.strip():
|
| 223 |
return "❌ 请输入音源名称", "", None, None
|
|
|
|
| 243 |
|
| 244 |
# 复制音频文件到输入目录
|
| 245 |
progress(0.05, desc="复制音频文件...")
|
| 246 |
+
copied_count = 0
|
| 247 |
for src_path in file_paths:
|
| 248 |
+
# 检查源文件是否存在
|
| 249 |
+
if not os.path.exists(src_path):
|
| 250 |
+
log(f"⚠️ 文件不存在或已被清理: {src_path}")
|
| 251 |
+
continue
|
| 252 |
+
try:
|
| 253 |
+
dst_path = os.path.join(input_dir, os.path.basename(src_path))
|
| 254 |
+
shutil.copy2(src_path, dst_path)
|
| 255 |
+
copied_count += 1
|
| 256 |
+
except Exception as e:
|
| 257 |
+
log(f"⚠️ 复制文件失败 {os.path.basename(src_path)}: {e}")
|
| 258 |
+
|
| 259 |
+
if copied_count == 0:
|
| 260 |
+
return "❌ 无法访问上传的文件,请重新上传", "\n".join(logs), None, None
|
| 261 |
+
|
| 262 |
+
log(f"📋 已复制 {copied_count}/{len(file_paths)} 个文件到工作目录")
|
| 263 |
|
| 264 |
# 获取 MFA 模型路径
|
| 265 |
mfa_models = scan_mfa_models()
|
|
|
|
| 439 |
return validate_voicebank_zip_path(zip_path)
|
| 440 |
|
| 441 |
|
| 442 |
+
@safe_gradio_handler
|
| 443 |
def process_export_voicebank(
|
| 444 |
zip_file,
|
| 445 |
plugin_name: str,
|
|
|
|
| 802 |
with gr.Tab("🎵 制作音源"):
|
| 803 |
gr.Markdown("### 上传音频文件")
|
| 804 |
gr.Markdown("支持格式: WAV, MP3, FLAC, OGG, M4A")
|
| 805 |
+
gr.Markdown("允许同时拖拽多个文件上传,也可点击上传框的右上角追加文件")
|
| 806 |
|
| 807 |
audio_upload = gr.File(
|
| 808 |
label="上传音频文件",
|
|
|
|
| 843 |
|
| 844 |
gr.Markdown("""
|
| 845 |
> ⏱️ **模型速度参考**:small 约 4 秒/句,medium 约 12 秒/句(medium 慢 2-3 倍但更准确)
|
| 846 |
+
>
|
| 847 |
+
> <span style="color: red;">**small完全够用的,medium费时还容易炸空间,除非实在识别不出来字再用**</span>
|
| 848 |
""")
|
| 849 |
|
| 850 |
make_btn = gr.Button("🚀 开始制作", variant="primary", size="lg", interactive=False)
|