feat: 视频生成页面改造、字幕冻结修复及多项前端优化
- 修复字幕切换模板后冻结的 bug:ASS.js 新实例在视频播放中创建时收不到 play/playing 事件,RAF 循环不会启动。创建实例后手动触发 play 事件。 - VideoGeneration 页面 overhaul:卡片点击预览、左右箭头导航、换一个素材、 动态按钮文案和占位提示。 - 修复私有音色素材预览播放 trialUrl 的问题,改为播放 sourceUrl。 - 放宽空镜素材匹配逻辑:优先满足时长,fallback 到最近时长并随机选择。 - 隐藏脚本生成页面的时长滑块。 - 修复登录页和侧边栏标题渐变 WebKit 兼容问题。 - 清理旧计划文档、测试文件和临时脚本。 - 更新 Makefile、prompts、materials.json 等配置。
This commit is contained in:
@@ -1,83 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
生成预设音色试听音频并上传到七牛云
|
||||
================================================
|
||||
|
||||
这个脚本会为所有预设音色生成试听音频(文案:"您好,我是您的家装顾问。需要我为您生成几版效果图看看吗?"),
|
||||
然后自动上传到七牛云存储。
|
||||
|
||||
运行方式:
|
||||
cd python-api
|
||||
python scripts/generate_preset_voice_previews.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from app.config import get_settings
|
||||
from app.services.tts_service import TTSService
|
||||
from app.services.qiniu_service import get_qiniu_service
|
||||
|
||||
# 试听文案
|
||||
PREVIEW_TEXT = "您好,我是您的家装顾问。需要我为您生成几版效果图看看吗?"
|
||||
|
||||
|
||||
async def generate_all_previews():
|
||||
"""为所有预设音色生成试听音频并上传"""
|
||||
settings = get_settings()
|
||||
tts_service = TTSService()
|
||||
qiniu = get_qiniu_service()
|
||||
|
||||
# 获取所有预设音色
|
||||
preset_voices = TTSService.PRESET_VOICES
|
||||
print(f"开始为 {len(preset_voices)} 个预设音色生成试听音频...\n")
|
||||
|
||||
for idx, voice in enumerate(preset_voices, 1):
|
||||
voice_id = voice["voice_id"]
|
||||
name = voice["name"]
|
||||
preview_key = voice["previewKey"]
|
||||
print(f"[{idx}/{len(preset_voices)}] 正在生成: {name} ({voice_id})")
|
||||
|
||||
# 创建临时文件
|
||||
with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as f:
|
||||
temp_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# 生成音频
|
||||
output_path = await tts_service.synthesize_to_file(
|
||||
text=PREVIEW_TEXT,
|
||||
output_path=temp_path,
|
||||
voice_id=voice_id,
|
||||
speed=1.0,
|
||||
voice_language=voice.get("language", "zh"),
|
||||
)
|
||||
|
||||
print(f" ✓ 生成完成,正在上传到七牛云...")
|
||||
|
||||
# 上传到七牛云
|
||||
result = qiniu.upload_file(
|
||||
local_path=str(output_path),
|
||||
key=preview_key,
|
||||
file_type="audio",
|
||||
check_duplicate=False, # 强制覆盖
|
||||
)
|
||||
|
||||
url = result["url"]
|
||||
print(f" ✓ 上传成功: {url}")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ 失败: {str(e)}")
|
||||
|
||||
finally:
|
||||
# 清理临时文件
|
||||
if temp_path.exists():
|
||||
temp_path.unlink()
|
||||
|
||||
print()
|
||||
|
||||
print("\n全部完成!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(generate_all_previews())
|
||||
@@ -1,128 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
上传官方预置音色试听音频到七牛云
|
||||
================================================
|
||||
|
||||
KlingAI 已经为每个官方预置音色提供了 trial_url,我们直接下载这个音频
|
||||
然后上传到七牛云存储,获取永久链接。
|
||||
|
||||
运行方式:
|
||||
cd python-api
|
||||
python scripts/upload_preset_voice_previews.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
|
||||
from app.config import get_settings
|
||||
from app.services.qiniu_service import get_qiniu_service
|
||||
from app.ai.providers.klingai_provider import KlingAIProvider
|
||||
|
||||
|
||||
async def download_file(url: str, temp_path: Path) -> None:
|
||||
"""下载文件到本地"""
|
||||
async with httpx.AsyncClient(timeout=60.0) as client:
|
||||
response = await client.get(url)
|
||||
response.raise_for_status()
|
||||
temp_path.write_bytes(response.content)
|
||||
|
||||
|
||||
async def upload_all_previews():
|
||||
"""下载所有官方预置音色试听并上传到七牛云"""
|
||||
settings = get_settings()
|
||||
qiniu = get_qiniu_service()
|
||||
provider = KlingAIProvider({
|
||||
"access_key": settings.KLINGAI_ACCESS_KEY or "",
|
||||
"secret_key": settings.KLINGAI_SECRET_KEY or "",
|
||||
})
|
||||
|
||||
# 获取官方预置音色列表
|
||||
voices = await provider.list_preset_voices()
|
||||
print(f"获取到 {len(voices)} 个官方预置音色\n")
|
||||
|
||||
description_map = {
|
||||
"钓系女友": "甜美撒娇",
|
||||
"温柔女声": "温柔细腻",
|
||||
"播报男声": "沉稳播报",
|
||||
"盐系少年": "清新少年",
|
||||
"撒娇女友": "可爱撒娇",
|
||||
}
|
||||
|
||||
results = []
|
||||
|
||||
for idx, voice in enumerate(voices, 1):
|
||||
if voice.get("status") != "succeed":
|
||||
print(f"[{idx}] 跳过 - 状态不为 succeed: {voice.get('status')}")
|
||||
continue
|
||||
|
||||
voice_id = voice["voice_id"]
|
||||
voice_name = voice["voice_name"]
|
||||
trial_url = voice.get("trial_url")
|
||||
|
||||
if not trial_url:
|
||||
print(f"[{idx}] {voice_name} - 没有 trial_url,跳过")
|
||||
continue
|
||||
|
||||
print(f"[{idx}/{len(voices)}] 处理: {voice_name} ({voice_id})")
|
||||
print(f" 原地址: {trial_url}")
|
||||
|
||||
# 下载到临时文件
|
||||
ext = ".wav"
|
||||
with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as f:
|
||||
temp_path = Path(f.name)
|
||||
|
||||
try:
|
||||
await download_file(trial_url, temp_path)
|
||||
print(f" ✓ 下载完成 ({temp_path.stat().st_size / 1024:.1f} KB)")
|
||||
|
||||
# 上传到七牛云
|
||||
key = f"meijiaka-zj/audios/{voice_id}{ext}"
|
||||
result = qiniu.upload_file(
|
||||
local_path=str(temp_path),
|
||||
key=key,
|
||||
file_type="audio",
|
||||
check_duplicate=False,
|
||||
)
|
||||
final_url = result["url"]
|
||||
print(f" ✓ 上传成功: {final_url}")
|
||||
|
||||
results.append({
|
||||
"voice_id": voice_id,
|
||||
"name": voice_name,
|
||||
"description": description_map.get(voice_name, ""),
|
||||
"previewUrl": final_url,
|
||||
"language": "zh",
|
||||
"recommended": voice_name == "温柔女声",
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ 失败: {str(e)}")
|
||||
|
||||
finally:
|
||||
# 清理临时文件
|
||||
if temp_path.exists():
|
||||
temp_path.unlink()
|
||||
|
||||
print()
|
||||
|
||||
print("\n=== 最终结果 ===")
|
||||
print("复制以下内容到 TTSService.PRESET_VOICES:")
|
||||
print()
|
||||
for r in results:
|
||||
print(f" {{")
|
||||
print(f" \"voice_id\": \"{r['voice_id']}\",")
|
||||
print(f" \"name\": \"{r['name']}\",")
|
||||
print(f" \"language\": \"{r['language']}\",")
|
||||
print(f" \"description\": \"{r['description']}\",")
|
||||
print(f" \"previewUrl\": \"{r['previewUrl']}\",")
|
||||
print(f" \"recommended\": {str(r['recommended']).lower()},")
|
||||
print(f" }},")
|
||||
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(upload_all_previews())
|
||||
Reference in New Issue
Block a user