diff --git a/Docs/BACKEND_README.md b/Docs/BACKEND_README.md index f5f43b0..4eb5ac9 100644 --- a/Docs/BACKEND_README.md +++ b/Docs/BACKEND_README.md @@ -51,6 +51,8 @@ backend/ * `POST /api/auth/register`: 用户注册 * `GET /api/auth/me`: 获取当前用户信息 +> 授权有效期策略:在登录与受保护接口鉴权时,后端会检查 `users.expires_at`。账号到期会自动停用 (`is_active=false`) 并清理 session,返回 `403: 会员已到期,请续费`。 + 2. **视频生成 (Videos)** * `POST /api/videos/generate`: 提交生成任务 * `GET /api/videos/tasks/{task_id}`: 查询单个任务状态 @@ -77,10 +79,11 @@ backend/ * `GET /api/assets/bgm`: 背景音乐列表 6. **声音克隆 (Ref Audios)** - * `POST /api/ref-audios`: 上传参考音频 (multipart/form-data) + * `POST /api/ref-audios`: 上传参考音频 (multipart/form-data,自动 Whisper 转写 ref_text) * `GET /api/ref-audios`: 获取参考音频列表 * `PUT /api/ref-audios/{id}`: 重命名参考音频 * `DELETE /api/ref-audios/{id}`: 删除参考音频 + * `POST /api/ref-audios/{id}/retranscribe`: 重新识别参考音频文字(Whisper 转写 + 超 10s 自动截取) 7. **AI 功能 (AI)** * `POST /api/ai/generate-meta`: AI 生成标题和标签 @@ -98,7 +101,7 @@ backend/ 10. **健康检查** * `GET /api/lipsync/health`: LatentSync 服务健康状态 - * `GET /api/voiceclone/health`: Qwen3-TTS 服务健康状态 + * `GET /api/voiceclone/health`: CosyVoice 3.0 服务健康状态 ### 统一响应结构 @@ -123,8 +126,10 @@ backend/ - `voice`: EdgeTTS 音色 ID(edgetts 模式) - `ref_audio_id` / `ref_text`: 参考音频 ID 与文本(voiceclone 模式) - `generated_audio_id`: 预生成配音 ID(存在时跳过内联 TTS,使用已生成的配音文件) -- `custom_assignments`: 自定义素材分配数组(每项含 `material_path` / `start` / `end` / `source_start`),存在时跳过 Whisper 均分 -- `language`: TTS 语言(默认自动检测,声音克隆时透传给 Qwen3-TTS) +- `speed`: 语速(声音克隆模式,默认 1.0,范围 0.8-1.2) +- `custom_assignments`: 自定义素材分配数组(每项含 `material_path` / `start` / `end` / `source_start` / `source_end?`),存在时优先按时间轴可见段生成 +- `output_aspect_ratio`: 输出画面比例(`9:16` 或 `16:9`,默认 `9:16`) +- `language`: TTS 语言(默认自动检测,声音克隆时透传给 CosyVoice 3.0) - `title`: 片头标题文字 - `subtitle_style_id`: 字幕样式 ID - `title_style_id`: 标题样式 ID @@ -136,6 +141,12 @@ backend/ - `bgm_id`: 背景音乐 ID - `bgm_volume`: 背景音乐音量(0-1,默认 0.2) +### 多素材稳定性说明 + +- 多素材片段在拼接前统一重编码,并强制 `25fps + CFR`,减少段边界时间基不一致导致的画面卡顿。 +- concat 流程启用 `+genpts` 重建时间戳,提升拼接后时间轴连续性。 +- 对带旋转元数据的 MOV 素材会先做方向归一化,再进入分辨率判断和后续流程。 + ## 📦 资源库与静态资源 - 本地资源目录:`backend/assets/{fonts,bgm,styles}` diff --git a/Docs/COSYVOICE3_DEPLOY.md b/Docs/COSYVOICE3_DEPLOY.md new file mode 100644 index 0000000..d309d30 --- /dev/null +++ b/Docs/COSYVOICE3_DEPLOY.md @@ -0,0 +1,211 @@ +# CosyVoice 3.0 部署文档 + +## 概览 + +| 项目 | 值 | +|------|------| +| 模型 | Fun-CosyVoice3-0.5B-2512 (0.5B 参数) | +| 端口 | 8010 | +| GPU | 0 (CUDA_VISIBLE_DEVICES=0) | +| PM2 名称 | vigent2-cosyvoice (id=15) | +| Conda 环境 | cosyvoice (Python 3.10) | +| 启动脚本 | `run_cosyvoice.sh` | +| 服务脚本 | `models/CosyVoice/cosyvoice_server.py` | +| 模型加载时间 | ~22-34 秒 | +| 显存占用 | ~3-5 GB | + +## 支持语言 + +中文、英文、日语、韩语、德语、西班牙语、法语、意大利语、俄语,18+ 中国方言 + +## 目录结构 + +``` +models/CosyVoice/ +├── cosyvoice_server.py # FastAPI 服务 (端口 8010) +├── cosyvoice/ # CosyVoice 源码 +│ └── cli/cosyvoice.py # AutoModel 入口 +├── third_party/Matcha-TTS/ # 子模块依赖 +├── pretrained_models/ +│ ├── Fun-CosyVoice3-0.5B/ # 模型文件 (~8.2GB) +│ │ ├── llm.pt # LLM 模型 (1.9GB) +│ │ ├── llm.rl.pt # RL 模型 (1.9GB, 备用) +│ │ ├── flow.pt # Flow 模型 (1.3GB) +│ │ ├── hift.pt # HiFT 声码器 (80MB) +│ │ ├── campplus.onnx # 说话人嵌入 (27MB) +│ │ ├── speech_tokenizer_v3.onnx # 语音分词器 (925MB) +│ │ ├── cosyvoice3.yaml # 模型配置 +│ │ └── CosyVoice-BlankEN/ # Qwen tokenizer +│ └── CosyVoice-ttsfrd/ # 文本正则化资源 +│ ├── resource/ # 解压后的 ttsfrd 资源 +│ └── resource.zip +run_cosyvoice.sh # PM2 启动脚本 +``` + +## API 接口 + +### GET /health + +健康检查,返回: +```json +{ + "service": "CosyVoice 3.0 Voice Clone", + "model": "Fun-CosyVoice3-0.5B", + "ready": true, + "gpu_id": 0 +} +``` + +### POST /generate + +声音克隆生成。 + +**参数 (multipart/form-data):** + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| ref_audio | File | 是 | 参考音频 (WAV) | +| text | string | 是 | 要合成的文本 | +| ref_text | string | 是 | 参考音频的转写文字 | +| language | string | 否 | 语言 (默认 "Chinese",CosyVoice 自动检测) | +| speed | float | 否 | 语速 (默认 1.0,范围 0.5-2.0,建议 0.8-1.2) | + +**返回:** WAV 音频文件 + +**状态码:** +- 200: 成功 +- 429: GPU 忙,请重试 +- 500: 生成失败/超时 +- 503: 模型未加载/服务中毒 + +## 安全机制 + +1. **GPU 推理锁** (`asyncio.Lock`): 防止并发推理导致 GPU 状态损坏 +2. **429 拒绝**: 锁被占用时立即返回 429,客户端重试 +3. **超时保护**: `60 + len(text) * 2` 秒,上限 300 秒 +4. **Poisoned 标记**: 超时后标记服务为中毒状态,健康检查返回 `ready: false` +5. **强制退出**: 超时后 1.5 秒强制 `os._exit(1)`,PM2 自动重启 +6. **启动自检**: 启动时用短文本做一次真实推理,验证 GPU 推理链路可用;失败则 `_model_loaded = False`,健康检查返回 `ready: false`,避免假阳性 +7. **参考音频自动截取**: 参考音频超过 10 秒时自动截取前 10 秒(CosyVoice 建议 3-10 秒),避免采样异常 + +## 运维命令 + +```bash +# 启动 +pm2 start run_cosyvoice.sh --name vigent2-cosyvoice + +# 重启 +pm2 restart vigent2-cosyvoice + +# 查看日志 +pm2 logs vigent2-cosyvoice --lines 50 + +# 健康检查 +curl http://localhost:8010/health + +# 停止 +pm2 stop vigent2-cosyvoice +``` + +## 从零部署步骤 + +### 1. 克隆仓库 + +```bash +cd /home/rongye/ProgramFiles/ViGent2/models +git clone --recursive https://github.com/FunAudioLLM/CosyVoice.git +cd CosyVoice +git submodule update --init --recursive +``` + +### 2. 创建 Conda 环境 + +```bash +conda create -n cosyvoice -y python=3.10 +conda activate cosyvoice +``` + +### 3. 安装依赖 + +注意:不能直接 `pip install -r requirements.txt`,有版本冲突需要处理。 + +```bash +# 安装 PyTorch 2.3.1 (CUDA 12.1) — 必须先装,版本严格要求 +pip install torch==2.3.1 torchaudio==2.3.1 --index-url https://download.pytorch.org/whl/cu121 + +# 核心推理依赖 +pip install conformer==0.3.2 HyperPyYAML==1.2.2 inflect==7.3.1 \ + librosa==0.10.2 lightning==2.2.4 modelscope==1.20.0 omegaconf==2.3.0 \ + pydantic==2.7.0 soundfile==0.12.1 fastapi==0.115.6 uvicorn==0.30.0 \ + transformers==4.51.3 protobuf==4.25 hydra-core==1.3.2 \ + rich==13.7.1 diffusers==0.29.0 x-transformers==2.11.24 wetext==0.0.4 + +# onnxruntime-gpu +pip install onnxruntime-gpu==1.18.0 \ + --extra-index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/ + +# 其他必要依赖 +pip install gdown matplotlib pyarrow wget onnx python-multipart httpx + +# openai-whisper 需要 setuptools < 71(提供 pkg_resources) +pip install "setuptools<71" +pip install --no-build-isolation openai-whisper==20231117 + +# pyworld 需要 g++ 和 Cython +pip install Cython +PATH="/usr/bin:$PATH" pip install pyworld==0.3.4 + +# 关键版本修复 +pip install "numpy<2" # onnxruntime-gpu 不兼容 numpy 2.x +pip install "ruamel.yaml<0.18" # hyperpyyaml 不兼容 ruamel.yaml 0.19+ +``` + +> **重要**: CosyVoice 要求 torch==2.3.1。torch 2.10+ 会导致 CUBLAS_STATUS_INVALID_VALUE 错误。 +> torch 2.3.1+cu121 自带 nvidia-cudnn-cu12,onnxruntime CUDAExecutionProvider 可正常使用。 + +### 4. 下载模型 + +```bash +# 使用 huggingface_hub (国内用 hf-mirror.com) +HF_ENDPOINT=https://hf-mirror.com python -c " +from huggingface_hub import snapshot_download +snapshot_download('FunAudioLLM/Fun-CosyVoice3-0.5B-2512', local_dir='pretrained_models/Fun-CosyVoice3-0.5B') +snapshot_download('FunAudioLLM/CosyVoice-ttsfrd', local_dir='pretrained_models/CosyVoice-ttsfrd') +" +``` + +### 5. 安装 ttsfrd (可选,提升文本正则化质量) + +```bash +cd pretrained_models/CosyVoice-ttsfrd/ +unzip resource.zip -d . +pip install ttsfrd_dependency-0.1-py3-none-any.whl +pip install ttsfrd-0.4.2-cp310-cp310-linux_x86_64.whl +``` + +### 6. 注册 PM2 + +```bash +pm2 start run_cosyvoice.sh --name vigent2-cosyvoice +pm2 save +``` + +## 已知问题 + +1. **ttsfrd "prepare tts engine failed"**: ttsfrd C 库内部日志,Python 层初始化成功,不影响使用 +2. **Sliding Window Attention 警告**: transformers 库提示,不影响推理结果 +3. **onnxruntime Memcpy 性能提示**: `Memcpy nodes are not supported by the CUDA EP`,仅为性能建议日志,不影响功能 + +> 注:libcudnn.so.8 问题在 torch 2.3.1+cu121 环境下已解决(自带 nvidia-cudnn-cu12),onnxruntime CUDAExecutionProvider 可正常加载。 + +## 与 Qwen3-TTS 对比 + +| 特性 | Qwen3-TTS (已停用) | CosyVoice 3.0 (当前) | +|------|-----------|----------------| +| 端口 | 8009 | 8010 | +| 模型大小 | 0.6B | 0.5B | +| 语言 | 中/英/日/韩 | 9 语言 + 18 方言 | +| 克隆方式 | ref_audio + ref_text | ref_audio + ref_text | +| prompt 格式 | 直接传 ref_text | `You are a helpful assistant.<\|endofprompt\|>` + ref_text | +| 内置分段 | 无,需客户端分段 | 内置 text_normalize 自动分段 | +| 状态 | 已停用 (PM2 stopped) | 生产使用中 | diff --git a/Docs/DEPLOY_MANUAL.md b/Docs/DEPLOY_MANUAL.md index 5ec3ab5..e432e91 100644 --- a/Docs/DEPLOY_MANUAL.md +++ b/Docs/DEPLOY_MANUAL.md @@ -336,34 +336,28 @@ chmod +x run_latentsync.sh pm2 start ./run_latentsync.sh --name vigent2-latentsync ``` -### 4. 启动 Qwen3-TTS 声音克隆服务 (可选) +### 4. 启动 CosyVoice 3.0 声音克隆服务 (可选) -> 如需使用声音克隆功能,需要启动此服务。 +> 如需使用声音克隆功能,需要启动此服务。详细部署步骤见 [CosyVoice 3.0 部署文档](COSYVOICE3_DEPLOY.md)。 -1. 安装 HTTP 服务依赖: -```bash -conda activate qwen-tts -pip install fastapi uvicorn python-multipart -``` +1. 启动脚本位于项目根目录: `run_cosyvoice.sh` -2. 启动脚本位于项目根目录: `run_qwen_tts.sh` - -3. 使用 pm2 启动: +2. 使用 pm2 启动: ```bash cd /home/rongye/ProgramFiles/ViGent2 -pm2 start ./run_qwen_tts.sh --name vigent2-qwen-tts +pm2 start ./run_cosyvoice.sh --name vigent2-cosyvoice pm2 save ``` -4. 验证服务: +3. 验证服务: ```bash # 检查健康状态 -curl http://localhost:8009/health +curl http://localhost:8010/health ``` ### 5. 启动服务看门狗 (Watchdog) -> 🛡️ **推荐**:监控 Qwen-TTS 和 LatentSync 服务健康状态,卡死时自动重启。 +> 🛡️ **推荐**:监控 CosyVoice 和 LatentSync 服务健康状态,卡死时自动重启。 ```bash cd /home/rongye/ProgramFiles/ViGent2 @@ -384,7 +378,7 @@ pm2 startup pm2 status # 查看所有服务状态 pm2 logs # 查看所有日志 pm2 logs vigent2-backend # 查看后端日志 -pm2 logs vigent2-qwen-tts # 查看 Qwen3-TTS 日志 +pm2 logs vigent2-cosyvoice # 查看 CosyVoice 日志 pm2 restart all # 重启所有服务 pm2 stop vigent2-latentsync # 停止 LatentSync 服务 pm2 delete all # 删除所有服务 @@ -523,7 +517,7 @@ python3 -c "import torch; print(torch.cuda.is_available())" sudo lsof -i :8006 sudo lsof -i :3002 sudo lsof -i :8007 -sudo lsof -i :8009 # Qwen3-TTS +sudo lsof -i :8010 # CosyVoice ``` ### 查看日志 @@ -533,7 +527,7 @@ sudo lsof -i :8009 # Qwen3-TTS pm2 logs vigent2-backend pm2 logs vigent2-frontend pm2 logs vigent2-latentsync -pm2 logs vigent2-qwen-tts +pm2 logs vigent2-cosyvoice ``` ### SSH 连接卡顿 / 系统响应慢 diff --git a/Docs/DevLogs/Day23.md b/Docs/DevLogs/Day23.md index bc8f409..b44dfca 100644 --- a/Docs/DevLogs/Day23.md +++ b/Docs/DevLogs/Day23.md @@ -328,11 +328,13 @@ interface TimelineSegment { ### 概述 -根据用户反馈,修复 6 项 UI 体验问题,同时修复 Qwen3-TTS 声音克隆服务的 SoX 路径问题和显存缓存管理。 +根据用户反馈,修复 6 项 UI 体验问题,同时修复声音克隆服务的 SoX 路径问题和显存缓存管理。 + +> **注**: Qwen3-TTS 已在后续被 CosyVoice 3.0 (端口 8010) 替换,以下记录为当时的修复过程。 --- -### 一、Qwen3-TTS 稳定性修复 +### 一、Qwen3-TTS 稳定性修复 (已被 CosyVoice 3.0 替换) #### 1.1 SoX PATH 修复 @@ -348,6 +350,8 @@ export PATH="/home/rongye/ProgramFiles/miniconda3/envs/qwen-tts/bin:$PATH" **修复**: `qwen_tts_server.py` 每次生成完成后(无论成功或失败)调用 `torch.cuda.empty_cache()`,防止显存碎片累积。使用 `asyncio.to_thread()` 在线程池中运行推理,避免阻塞事件循环导致健康检查超时。 +> **后续**: Qwen3-TTS 已停用,CosyVoice 3.0 沿用了相同的保护机制(GPU 推理锁、超时保护、显存清理、启动自检)。 + --- ### 二、配音列表按钮布局统一 (反馈 #1 + #6) @@ -415,8 +419,8 @@ export PATH="/home/rongye/ProgramFiles/miniconda3/envs/qwen-tts/bin:$PATH" | 文件 | 变更 | |------|------| -| `run_qwen_tts.sh` | export conda env bin 到 PATH,修复 SoX 找不到问题 | -| `models/Qwen3-TTS/qwen_tts_server.py` | 每次生成后 `torch.cuda.empty_cache()`,asyncio.to_thread 避免阻塞 | +| `run_qwen_tts.sh` | export conda env bin 到 PATH,修复 SoX 找不到问题 (已停用) | +| `models/Qwen3-TTS/qwen_tts_server.py` | 每次生成后 `torch.cuda.empty_cache()`,asyncio.to_thread 避免阻塞 (已停用) | #### 前端修改 @@ -544,3 +548,309 @@ next.splice(toIdx, 0, moved); | `frontend/src/features/home/model/useHomeController.ts` | 集成 useSavedScripts,新增 handleSaveScript | | `frontend/src/features/home/ui/HomePage.tsx` | 传递 savedScripts / handleSaveScript / deleteSavedScript 到 ScriptEditor | | `frontend/src/features/home/model/useTimelineEditor.ts` | reorderSegments 从属性交换改为数组移动(splice) | + +--- + +## 🔤 字幕语言不匹配 + 视频比例错位修复 — 第五阶段 (Day 23) + +### 概述 + +修复两个视频生成 Bug: +1. **字幕语言不匹配**: 中文配音 + 英文翻译文案 → 字幕错误显示英文(Whisper 独立转录,忽略原文) +2. **标题字幕比例错位**: 9:16 竖屏素材生成视频后,标题/字幕按 16:9 横屏布局渲染 + +附带修复代码审查中发现的 `split_word_to_chars` 英文空格丢失问题。 + +--- + +### 一、字幕用原文替换 Whisper 转录文字 + +#### 根因 + +Whisper 对音频独立转录,完全忽略传入的 `text` 参数。当配音语言与编辑器文案语言不一致时(例如:用户先写中文文案 → 翻译成英文 → 生成英文配音 → 再改回中文文案),Whisper "听到"英文语音就输出英文字幕。 + +#### 修复思路 + +Whisper 仅负责检测**语音总时间范围**(`first_start` → `last_end`),字幕文字永远用配音保存的原始文案。 + +#### `whisper_service.py` — `align()` 新增 `original_text` 参数 + +```python +async def align(self, audio_path, text, output_path=None, + language="zh", original_text=None): +``` + +当 `original_text` 非空时: +1. 正常运行 Whisper 转录,记录 `whisper_first_start` 和 `whisper_last_end` +2. 将 `original_text` 传入 `split_word_to_chars()` 在总时间范围上线性分布 +3. 用 `split_segment_to_lines()` 按标点和字数断行 +4. 替换 Whisper 的转录结果 + +#### `workflow.py` — 配音元数据无条件覆盖 + 传入原文 + +```python +# 改前(只在文案为空时覆盖) +if not req.text.strip(): + req.text = meta.get("text", req.text) + +# 改后(无条件用配音元数据覆盖) +meta_text = meta.get("text", "") +if meta_text: + req.text = meta_text +``` + +所有 4 处 `whisper_service.align()` 调用添加 `original_text=req.text`。 + +--- + +### 二、Remotion 动态传入视频尺寸 + +#### 根因 + +`remotion/src/Root.tsx` 硬编码 `width={1280} height={720}`。虽然 `render.ts` 用 ffprobe 检测真实尺寸后覆盖 `composition.width/height`,但 `selectComposition` 阶段组件已按 1280×720 初始化,标题和字幕定位基于错误的画布尺寸。 + +#### 修复 + +##### `Root.tsx` — `calculateMetadata` 从 props 读取尺寸 + +```tsx + ({ + width: props.width || 1080, + height: props.height || 1920, + })} + defaultProps={{ + videoSrc: '', + width: 1080, + height: 1920, + // ... + }} +/> +``` + +默认从 1280×720 改为 1080×1920(竖屏优先),`calculateMetadata` 确保 `selectComposition` 阶段使用 ffprobe 检测的真实尺寸。 + +##### `Video.tsx` — VideoProps 新增可选 `width/height` + +仅供 `calculateMetadata` 访问,组件渲染不引用。 + +##### `render.ts` — inputProps 统一传入视频尺寸 + +```typescript +const inputProps = { + videoSrc: videoFileName, + captions, + title: options.title, + // ... + width: videoWidth, // ffprobe 检测值 + height: videoHeight, // ffprobe 检测值 +}; +``` + +`selectComposition` 和 `renderMedia` 使用同一个 `inputProps`。保留显式 `composition.width/height` 覆盖作为保险。 + +--- + +### 三、代码审查修复:英文空格丢失 + +#### 问题 + +`split_word_to_chars` 原设计处理 Whisper 单个词(如 `" Hello"`),但 `original_text` 传入整段文本时,中间空格被 `continue` 跳过且不 flush `ascii_buffer`,导致 `"Hello World"` 变成 `"HelloWorld"`。 + +#### 执行路径追踪 + +``` +输入: "Hello World" + H,e,l,l,o → ascii_buffer = "Hello" + ' ' → continue(跳过,不 flush!) + W,o,r,l,d → ascii_buffer = "HelloWorld" +结果: tokens = ["HelloWorld"] ← 空格丢失 +``` + +#### 修复 + +遇到空格时 flush `ascii_buffer`,并用 `pending_space` 标记给下一个 token 前置空格: + +```python +if not char.strip(): + if ascii_buffer: + tokens.append(ascii_buffer) + ascii_buffer = "" + if tokens: + pending_space = True + continue +``` + +修复后:`"Hello World"` → tokens = `["Hello", " World"]` → 字幕正确显示。中文不受影响。 + +--- + +### 涉及文件汇总 + +#### 后端修改 + +| 文件 | 变更 | +|------|------| +| `backend/app/services/whisper_service.py` | `align()` 新增 `original_text` 参数;`split_word_to_chars` 修复英文空格丢失 | +| `backend/app/modules/videos/workflow.py` | 配音元数据无条件覆盖 text/language;4 处 `align()` 调用传入 `original_text` | + +#### 前端修改(Remotion) + +| 文件 | 变更 | +|------|------| +| `remotion/src/Root.tsx` | 默认尺寸改为 1080×1920,新增 `calculateMetadata` + width/height defaultProps | +| `remotion/src/Video.tsx` | VideoProps 新增可选 `width`/`height` | +| `remotion/render.ts` | inputProps 统一传入 `videoWidth`/`videoHeight`,selectComposition 和 renderMedia 共用 | + +--- + +## 🎤 参考音频自动转写 + 语速控制 — 第六阶段 (Day 23) + +### 概述 + +解决声音克隆 ref_text 不匹配问题:旧方案使用前端固定文字作为 ref_text,CosyVoice zero-shot 克隆要求 ref_text 必须与参考音频实际内容匹配,不匹配时模型会在生成音频开头"幻觉"出多余片段。 + +**改进**:上传参考音频时自动调用 Whisper 转写内容作为 ref_text,同时新增语速控制功能。 + +--- + +### 一、Whisper 自动转写参考音频 + +#### 1.1 `whisper_service.py` — 语言自动检测 + +`transcribe()` 方法原先硬编码 `language="zh"`,改为接受可选 `language` 参数(默认 `None` = 自动检测),支持多语言参考音频。 + +#### 1.2 `ref_audios/service.py` — 上传时自动转写 + +上传流程变更:转码 WAV → 检查时长(≥1s) → 超 10s 在静音点截取 → **Whisper 自动转写** → 验证非空 → 上传。 + +```python +try: + transcribed = await whisper_service.transcribe(tmp_wav_path) + if transcribed.strip(): + ref_text = transcribed.strip() +except Exception as e: + logger.warning(f"Auto-transcribe failed: {e}") + +if not ref_text or not ref_text.strip(): + raise ValueError("无法识别音频内容,请确保音频包含清晰的语音") +``` + +#### 1.3 `ref_audios/router.py` — ref_text 改为可选 + +`ref_text: str = Form("")`(不再必填),前端不再发送固定文字。 + +--- + +### 二、参考音频智能截取(10 秒上限) + +CosyVoice 对 3-10 秒参考音频效果最好。 + +#### 2.1 静音点检测 + +使用 ffmpeg `silencedetect` 找 10 秒内最后一个静音结束点(阈值 -30dB,最短 0.3s),避免在字词中间硬切: + +```python +def _find_silence_cut_point(file_path, max_duration): + # silencedetect → 解析 silence_end → 找 3s~max_duration 内最后的静音点 + # 找不到则回退到 max_duration +``` + +#### 2.2 淡出处理 + +截取时末尾 0.1 秒淡出(`afade=t=out`),避免截断爆音。 + +--- + +### 三、重新识别功能(旧数据迁移) + +#### 3.1 新增 API + +`POST /api/ref-audios/{audio_id}/retranscribe` — 下载音频 → 超 10s 截取 → Whisper 转写 → 重新上传音频和元数据。 + +#### 3.2 前端 UI + +- RefAudioPanel 新增 RotateCw 按钮("重新识别文字"),转写中显示 `animate-spin` +- 旧音频 ref_text 以固定文字开头时显示 ⚠ 黄色警告 + +--- + +### 四、语速控制(CosyVoice speed 参数) + +#### 4.1 全链路传递 + +``` +前端 GeneratedAudiosPanel (速度选择器) + → useHomeController (speed state + persistence) + → useGeneratedAudios.generateAudio(params) + → POST /api/generated-audios/generate { speed: 1.0 } + → GenerateAudioRequest.speed (Pydantic) + → generate_audio_task → voice_clone_service.generate_audio(speed=) + → _generate_once → POST /generate { speed: "1.0" } + → cosyvoice_server → _model.inference_zero_shot(speed=speed) +``` + +#### 4.2 前端 UI + +声音克隆模式下,配音列表面板标题栏"生成配音"按钮左侧显示语速下拉菜单(`语速: 正常 ▼`): + +| 标签 | speed 值 | +|------|----------| +| 较慢 | 0.8 | +| 稍慢 | 0.9 | +| 正常 | 1.0 (默认) | +| 稍快 | 1.1 | +| 较快 | 1.2 | + +语速选择持久化到 localStorage(`vigent_{storageKey}_speed`)。 + +--- + +### 五、缺少参考音频门控 + +声音克隆模式下未选参考音频时: +- "生成配音"按钮禁用 + title 提示"请先选择参考音频" +- 面板内显示黄色警告条"声音克隆模式需要先选择参考音频" + +--- + +### 六、前端清理 + +- 移除 `FIXED_REF_TEXT` 常量和 `fixedRefText` prop +- 移除"请朗读以下内容"引导区块 +- 上传提示简化为"上传任意语音样本(3-10秒),系统将自动识别内容并克隆声音" +- 录音区备注"建议 3-10 秒,超出将自动截取" + +--- + +### 涉及文件汇总 + +#### 后端修改 + +| 文件 | 变更 | +|------|------| +| `backend/app/services/whisper_service.py` | `transcribe()` 增加可选 `language` 参数,默认 None (自动检测) | +| `backend/app/modules/ref_audios/service.py` | 上传自动转写 + 静音点截取 + 淡出 + retranscribe 函数 | +| `backend/app/modules/ref_audios/router.py` | `ref_text` 改为 Form(""),新增 retranscribe 端点 | +| `backend/app/modules/generated_audios/schemas.py` | `GenerateAudioRequest` 新增 `speed: float = 1.0` | +| `backend/app/modules/generated_audios/service.py` | 传递 `req.speed` 到 voice_clone_service | +| `backend/app/services/voice_clone_service.py` | `generate_audio()` / `_generate_once()` 接受并传递 speed | +| `models/CosyVoice/cosyvoice_server.py` | `/generate` 端点接受 `speed` 参数,传递到 `inference_zero_shot(speed=)` | + +#### 前端修改 + +| 文件 | 变更 | +|------|------| +| `frontend/src/features/home/model/useHomeController.ts` | 新增 speed state,移除 FIXED_REF_TEXT,handleGenerateAudio 传 speed | +| `frontend/src/features/home/model/useHomePersistence.ts` | 新增 speed 持久化 | +| `frontend/src/features/home/model/useRefAudios.ts` | 移除 fixedRefText,新增 retranscribe | +| `frontend/src/features/home/model/useGeneratedAudios.ts` | generateAudio params 新增 speed | +| `frontend/src/features/home/ui/GeneratedAudiosPanel.tsx` | 新增语速选择器 + 缺少参考音频门控 | +| `frontend/src/features/home/ui/RefAudioPanel.tsx` | 移除朗读引导,新增重新识别按钮 + ⚠ 警告 | +| `frontend/src/features/home/ui/HomePage.tsx` | 传递 speed/setSpeed/ttsMode 到 GeneratedAudiosPanel | diff --git a/Docs/DevLogs/Day24.md b/Docs/DevLogs/Day24.md new file mode 100644 index 0000000..f1a217f --- /dev/null +++ b/Docs/DevLogs/Day24.md @@ -0,0 +1,168 @@ +## 🔧 鉴权到期治理 + 多素材时间轴稳定性修复 (Day 24) + +### 概述 + +本日主要完成两条主线: + +1. **账号与鉴权治理**:会员到期改为请求时自动失效(登录/鉴权接口触发),并统一返回续费提示。 +2. **视频生成稳定性**:围绕多素材时间轴、截取语义、拼接边界冻结、画面比例与字幕标题适配进行一轮端到端修复。 + +--- + +## 🔐 会员到期请求时失效 — 第一阶段 (Day 24) + +### 目标 + +避免依赖定时任务,用户在触发登录或访问受保护接口时即可完成到期判定与账号停用。 + +### 行为调整 + +- 到期判断基于 `users.expires_at`。 +- 判定到期后: + - 将 `is_active` 自动置为 `false` + - 删除该用户全部 session + - 返回 `403`,提示:`会员已到期,请续费` + +### 实现点 + +- `users.py` 新增 `deactivate_user_if_expired()`,并补充 `_parse_expires_at()` 统一时区解析。 +- `deps.py` 在 `get_current_user` / `get_current_user_optional` 中统一接入到期检查。 +- `auth/router.py` 在登录路径增加到期停用逻辑;`/api/auth/me` 统一走 `Depends(get_current_user)`。 + +--- + +## 🖼️ 画面比例控制 + 字幕标题适配 — 第二阶段 (Day 24) + +### 2.1 输出画面比例可配置 + +- 时间轴顶部新增“画面比例”下拉:`9:16` / `16:9`。 +- 默认值 `9:16`,并持久化到 localStorage。 +- 生成请求携带 `output_aspect_ratio`,后端在单素材与多素材流程中统一按目标分辨率处理。 + +### 2.2 标题/字幕在窄屏画布防溢出 + +为减少“预览正常、成片溢出”的差异,统一了预览与渲染策略: + +- 根据 composition 宽度进行响应式缩放。 +- 开启可换行:`white-space: normal` + `word-break` + `overflow-wrap`。 +- 描边、字距、上下边距同步按比例缩放。 + +--- + +## 🎥 方向归一化 + 多素材拼接稳定性 — 第三阶段 (Day 24) + +### 3.1 MOV 旋转元数据导致横竖识别错误 + +问题场景:编码分辨率是横屏,但依赖 rotation side-data 才能正确显示为竖屏(常见于手机 MOV)。 + +修复方案: + +- `get_video_metadata()` 扩展返回 `rotation/effective_width/effective_height`。 +- 新增 `normalize_orientation()`,在流程前对带旋转元数据素材做物理方向归一化。 +- 单素材和多素材下载后统一执行方向归一化,再做分辨率决策。 + +### 3.2 多素材“只看到第一段”与边界冻结 + +针对拼接可靠性补了两类保护: + +- **分配保护**:`custom_assignments` 与素材数量不一致时,后端回退自动分配,避免异常输入导致仅首段生效。 +- **编码一致性**: + - 片段准备阶段统一重编码; + - concat 阶段不再走拷贝; + - 进一步统一为 `25fps + CFR`,并在 concat 增加 `+genpts`,降低段边界时间基不连续导致的“画面冻结口型还动”风险。 + +--- + +## ⏱️ 时间轴截取语义对齐修复 — 第四阶段 (Day 24) + +### 背景 + +时间轴设计语义是: + +- 每段可以设置 `sourceStart/sourceEnd`; +- 总时长超出音频时,仅保留可见段,末段截齐音频; +- 总时长不足时,由最后可见段循环补齐。 + +本日将前后端对齐到这一语义。 + +### 4.1 `source_end` 全链路打通 + +此前仅传 `source_start`,导致后端无法准确知道“截到哪里”。 + +本次改动: + +- 前端 `toCustomAssignments()` 增加可选 `source_end`。 +- 后端 `CustomAssignment` schema 增加 `source_end`。 +- workflow 将 `source_end` 透传到 `prepare_segment()`(单素材/多素材均支持)。 +- `prepare_segment()` 增加 `source_end` 参数,按 `[source_start, source_end)` 计算可用片段,并在需要循环时先裁剪再循环,避免循环范围错位。 + +### 4.2 时间轴有效时长计算修复 + +修复 `sourceStart > 0 且 sourceEnd = 0` 时的有效时长错误: + +- 旧逻辑会按整段素材时长计算; +- 新逻辑改为 `materialDuration - sourceStart`。 + +该修复同时用于: + +- `recalcPositions()` 的段时长计算; +- TimelineEditor 中“循环补足”可视化比例计算。 + +### 4.3 可见段分配优先级修复 + +修复“可见段数 < 已选素材数时,custom_assignments 被丢弃回退自动分配”的问题: + +- 生成请求优先以时间轴可见段的 `assignments` 为准; +- 超出时间轴的素材不参与本次生成。 + +### 4.4 单素材截取触发条件补齐 + +单素材模式下,若只改了终点(`sourceEnd > 0`)也会发送 `custom_assignments`,确保截取生效。 + +--- + +## 🧭 页面交互与体验细节 — 第五阶段 (Day 24) + +- 页面刷新后自动回到顶部,避免从历史滚动位置进入页面。 +- 素材列表与历史视频列表滚动增加“跳过首次自动滚动”保护,减少恢复状态时页面跳动。 +- 时间轴比例区移除多余文案,保持信息简洁。 + +--- + +## 涉及文件汇总 + +### 后端修改 + +| 文件 | 变更 | +|------|------| +| `backend/app/repositories/users.py` | 新增 `deactivate_user_if_expired()` 与 `_parse_expires_at()` | +| `backend/app/core/deps.py` | `get_current_user` / `get_current_user_optional` 接入到期失效检查 | +| `backend/app/modules/auth/router.py` | 登录时到期停用 + `/api/auth/me` 统一鉴权依赖 | +| `backend/app/modules/videos/schemas.py` | `CustomAssignment` 新增 `source_end`;保留 `output_aspect_ratio` | +| `backend/app/modules/videos/workflow.py` | 多素材/单素材透传 `source_end`;多素材 prepare/concat 统一 25fps | +| `backend/app/services/video_service.py` | 旋转元数据解析与方向归一化;`prepare_segment` 支持 `source_end/target_fps`;concat 强制 CFR + `+genpts` | + +### 前端修改 + +| 文件 | 变更 | +|------|------| +| `frontend/src/features/home/model/useTimelineEditor.ts` | `CustomAssignment` 新增 `source_end`;修复 sourceStart 开放终点时长计算 | +| `frontend/src/features/home/model/useHomeController.ts` | 多素材以可见 assignments 为准发送;单素材截取触发条件补齐 | +| `frontend/src/features/home/ui/TimelineEditor.tsx` | 画面比例下拉;循环比例按截取后有效时长计算 | +| `frontend/src/features/home/model/useHomePersistence.ts` | `outputAspectRatio` 持久化 | +| `frontend/src/features/home/ui/HomePage.tsx` | 页面进入滚动到顶部;ClipTrimmer/Timeline 交互保持一致 | +| `frontend/src/features/home/ui/FloatingStylePreview.tsx` | 标题/字幕样式预览与成片渲染策略对齐 | + +### Remotion 修改 + +| 文件 | 变更 | +|------|------| +| `remotion/src/components/Title.tsx` | 标题响应式缩放与自动换行,优化竖屏窄画布适配 | +| `remotion/src/components/Subtitles.tsx` | 字幕响应式缩放与自动换行,减少预览/成片差异 | + +--- + +## 验证记录 + +- 后端语法检查:`python -m py_compile backend/app/modules/videos/schemas.py backend/app/modules/videos/workflow.py backend/app/services/video_service.py` +- 前端类型检查:`npx tsc --noEmit` diff --git a/Docs/Doc_Rules.md b/Docs/Doc_Rules.md index efeaa70..c4417e1 100644 --- a/Docs/Doc_Rules.md +++ b/Docs/Doc_Rules.md @@ -30,7 +30,7 @@ | ⚡ **Med** | `Docs/BACKEND_README.md` | **(后端文档)** 接口说明、架构设计 | | ⚡ **Med** | `Docs/FRONTEND_DEV.md` | **(前端规范)** API封装、日期格式化、新页面规范 | | ⚡ **Med** | `Docs/FRONTEND_README.md` | **(前端文档)** 功能说明、页面变更 | -| 🧊 **Low** | `Docs/*_DEPLOY.md` | **(子系统部署)** LatentSync/Qwen3/字幕等独立部署文档 | +| 🧊 **Low** | `Docs/*_DEPLOY.md` | **(子系统部署)** LatentSync/CosyVoice/字幕等独立部署文档 | --- @@ -195,7 +195,7 @@ ViGent2/Docs/ ├── DEPLOY_MANUAL.md # 部署手册 ├── SUPABASE_DEPLOY.md # Supabase 部署文档 ├── LATENTSYNC_DEPLOY.md # LatentSync 部署文档 -├── QWEN3_TTS_DEPLOY.md # 声音克隆部署文档 +├── COSYVOICE3_DEPLOY.md # 声音克隆部署文档 ├── SUBTITLE_DEPLOY.md # 字幕系统部署文档 └── DevLogs/ ├── Day1.md # 开发日志 diff --git a/Docs/FRONTEND_DEV.md b/Docs/FRONTEND_DEV.md index 8937321..3d77710 100644 --- a/Docs/FRONTEND_DEV.md +++ b/Docs/FRONTEND_DEV.md @@ -308,6 +308,7 @@ import { formatDate } from '@/shared/lib/media'; - 背景音乐选择 / 音量 / 开关状态 - 素材选择 / 历史作品选择 - 选中配音 ID (`selectedAudioId`) + - 语速 (`speed`,声音克隆模式) - 时间轴段信息 (`useTimelineEditor` 的 localStorage) ### 历史文案(独立持久化) @@ -361,9 +362,11 @@ import { formatDate } from '@/shared/lib/media'; | 接口 | 方法 | 功能 | |------|------|------| -| `/api/ref-audios` | POST | 上传参考音频 (multipart/form-data: file + ref_text) | +| `/api/ref-audios` | POST | 上传参考音频 (multipart/form-data: file,ref_text 可选,后端自动 Whisper 转写) | | `/api/ref-audios` | GET | 列出用户的参考音频 | +| `/api/ref-audios/{id}` | PUT | 重命名参考音频 | | `/api/ref-audios/{id}` | DELETE | 删除参考音频 (id 需 encodeURIComponent) | +| `/api/ref-audios/{id}/retranscribe` | POST | 重新识别参考音频文字(Whisper 转写 + 超 10s 自动截取) | ### 视频生成 API 扩展 @@ -382,7 +385,8 @@ await api.post('/api/videos/generate', { text: '口播文案', tts_mode: 'voiceclone', ref_audio_id: 'user_id/timestamp_name.wav', - ref_text: '参考音频对应文字', + ref_text: '参考音频对应文字', // 从参考音频 metadata 自动获取 + speed: 1.0, // 语速 (0.8-1.2) }); ``` @@ -396,8 +400,14 @@ const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' }); ``` +### 参考音频自动处理 + +- **自动转写**: 上传参考音频时后端自动调用 Whisper 转写内容作为 `ref_text`,无需用户手动输入 +- **自动截取**: 参考音频超过 10 秒时自动在静音点截取前 10 秒(CosyVoice 建议 3-10 秒) +- **重新识别**: 旧参考音频可通过 retranscribe 端点重新转写并截取 + ### UI 结构 配音方式使用 Tab 切换: - **EdgeTTS 音色** - 预设音色 2x3 网格 -- **声音克隆** - 参考音频列表 + 在线录音 + 参考文字输入 +- **声音克隆** - 参考音频列表 + 在线录音 + 语速下拉菜单 (5 档: 较慢/稍慢/正常/稍快/较快) diff --git a/Docs/FRONTEND_README.md b/Docs/FRONTEND_README.md index d78297b..b00dc24 100644 --- a/Docs/FRONTEND_README.md +++ b/Docs/FRONTEND_README.md @@ -35,8 +35,10 @@ ViGent2 的前端界面,采用 Next.js 16 + TailwindCSS 构建。 ### 3. 声音克隆 [Day 13 新增] - **TTS 模式选择**: EdgeTTS (预设音色) / 声音克隆 (自定义音色) 切换。 -- **参考音频管理**: 上传/列表/删除参考音频 (3-20秒 WAV)。 -- **一键克隆**: 选择参考音频后自动调用 Qwen3-TTS 服务。 +- **参考音频管理**: 上传/列表/重命名/删除参考音频,上传后自动 Whisper 转写 ref_text + 超 10s 自动截取。 +- **重新识别**: 旧参考音频可重新转写并截取 (RotateCw 按钮)。 +- **一键克隆**: 选择参考音频后自动调用 CosyVoice 3.0 服务。 +- **语速控制**: 声音克隆模式下支持 5 档语速 (0.8-1.2),选择持久化 (Day 23)。 - **多语言支持**: EdgeTTS 10 语言声音列表,声音克隆 language 透传 (Day 22)。 ### 4. 配音前置 + 时间轴编排 [Day 23 新增] @@ -45,7 +47,9 @@ ViGent2 的前端界面,采用 Next.js 16 + TailwindCSS 构建。 - **时间轴编辑器**: wavesurfer.js 音频波形 + 色块可视化素材分配,拖拽分割线调整各段时长。 - **素材截取设置**: ClipTrimmer 双手柄 range slider + HTML5 视频预览播放。 - **拖拽排序**: 时间轴色块支持 HTML5 Drag & Drop 调换素材顺序。 -- **自定义分配**: 后端 `custom_assignments` 支持用户定义的素材分配方案。 +- **自定义分配**: 后端 `custom_assignments` 支持用户定义的素材分配方案(含 `source_start/source_end` 截取区间)。 +- **时间轴语义对齐**: 超出音频时仅保留可见段并截齐末段,超出段不参与生成;不足音频时最后可见段自动循环补齐。 +- **画面比例控制**: 时间轴顶部支持 `9:16 / 16:9` 输出比例选择,设置持久化并透传后端。 ### 5. 字幕与标题 [Day 13 新增] - **片头标题**: 可选输入,限制 15 字,视频开头显示 3 秒淡入淡出标题。 diff --git a/Docs/task_complete.md b/Docs/task_complete.md index 5e2a747..6956352 100644 --- a/Docs/task_complete.md +++ b/Docs/task_complete.md @@ -1,8 +1,8 @@ # ViGent2 开发任务清单 (Task Log) **项目**: ViGent2 数字人口播视频生成系统 -**进度**: 100% (Day 23 - 配音前置重构 + 素材时间轴编排 + UI 体验优化) -**更新时间**: 2026-02-10 +**进度**: 100% (Day 24 - 鉴权到期治理 + 多素材时间轴稳定性修复) +**更新时间**: 2026-02-11 --- @@ -10,7 +10,16 @@ > 这里记录了每一天的核心开发内容与 milestone。 -### Day 23: 配音前置重构 + 素材时间轴编排 + UI 体验优化 + 历史文案 (Current) +### Day 24: 鉴权到期治理 + 多素材时间轴稳定性修复 (Current) +- [x] **会员到期请求时失效**: 登录与鉴权接口统一执行 `expires_at` 检查;到期后自动停用账号、清理 session,并返回“会员已到期,请续费”。 +- [x] **画面比例控制**: 时间轴新增 `9:16 / 16:9` 输出比例选择,前端持久化并透传后端,单素材/多素材统一按目标分辨率处理。 +- [x] **标题/字幕防溢出**: Remotion 与前端预览统一响应式缩放、自动换行、描边/字距/边距比例缩放,降低预览与成片差异。 +- [x] **MOV 方向归一化**: 新增旋转元数据解析与 orientation normalize,修复“编码横屏+旋转元数据”导致的竖屏判断偏差。 +- [x] **多素材拼接稳定性**: 片段 prepare 与 concat 统一 25fps/CFR,concat 增加 `+genpts`,缓解段切换处“画面冻结口型还动”。 +- [x] **时间轴语义对齐**: 打通 `source_end` 全链路;修复 `sourceStart>0 且 sourceEnd=0` 时长计算;生成时以时间轴可见段 assignments 为准,超出段不参与。 +- [x] **交互细节优化**: 页面刷新回顶部;素材/历史列表首轮自动滚动抑制,减少恢复状态时页面跳动。 + +### Day 23: 配音前置重构 + 素材时间轴编排 + UI 体验优化 + 声音克隆增强 #### 第一阶段:配音前置 - [x] **配音生成独立化**: 新增 `generated_audios` 后端模块(router/schemas/service),5 个 API 端点,复用现有 TTSService / voice_clone_service / task_store。 @@ -28,8 +37,8 @@ - [x] **MaterialSelector 精简**: 移除旧的时长信息栏和拖拽排序区(功能迁移到 TimelineEditor)。 #### 第三阶段:UI 体验优化 + TTS 稳定性 -- [x] **TTS SoX PATH 修复**: `run_qwen_tts.sh` export conda env bin 到 PATH,修复 `SoX could not be found!` 警告。 -- [x] **TTS 显存管理**: 每次生成后 `torch.cuda.empty_cache()`,asyncio.to_thread 避免阻塞事件循环。 +- [x] **TTS SoX PATH 修复**: `run_qwen_tts.sh` export conda env bin 到 PATH (Qwen3-TTS 已停用,已被 CosyVoice 3.0 替换)。 +- [x] **TTS 显存管理**: 每次生成后 `torch.cuda.empty_cache()`,asyncio.to_thread 避免阻塞事件循环 (CosyVoice 沿用相同机制)。 - [x] **配音列表按钮统一**: Play/Edit/Delete 按钮右侧同组 hover 显示,与 RefAudioPanel 一致,移除文案摘要。 - [x] **素材区解除配音门控**: 移除 MaterialSelector 的 selectedAudio 遮罩,素材随时可上传管理。 - [x] **时间轴拖拽排序**: TimelineEditor 色块支持 HTML5 Drag & Drop 调换素材顺序。 @@ -42,6 +51,20 @@ - [x] **按钮视觉统一**: 文案编辑区 4 个按钮统一为固定高度 `h-7`,移除多余 `` 嵌套。 - [x] **底部栏调整**: "保存文案"按钮移至底部右侧,移除预计时长显示。 +#### 第五阶段:字幕语言不匹配 + 视频比例错位修复 +- [x] **字幕用原文替换 Whisper 转录**: `align()` 新增 `original_text` 参数,字幕文字永远用配音保存的原始文案。 +- [x] **Remotion 动态视频尺寸**: `calculateMetadata` 从 props 读取真实尺寸,修复标题/字幕比例错位。 +- [x] **英文空格丢失修复**: `split_word_to_chars` 遇到空格时 flush buffer + pending_space 标记。 + +#### 第六阶段:参考音频自动转写 + 语速控制 +- [x] **Whisper 自动转写 ref_text**: 上传参考音频时自动调用 Whisper 转写内容作为 ref_text,不再使用前端固定文字。 +- [x] **参考音频自动截取**: 超过 10 秒自动在静音点截取(ffmpeg silencedetect),末尾 0.1 秒淡出避免截断爆音。 +- [x] **重新识别功能**: 新增 `POST /ref-audios/{id}/retranscribe` 端点 + 前端 RotateCw 按钮,旧音频可重新转写并截取。 +- [x] **语速控制**: 全链路 speed 参数(前端选择器 → 持久化 → 后端 → CosyVoice `inference_zero_shot(speed=)`),5 档:较慢(0.8)/稍慢(0.9)/正常(1.0)/稍快(1.1)/较快(1.2)。 +- [x] **缺少参考音频门控**: 声音克隆模式下未选参考音频时,生成配音按钮禁用 + 黄色警告提示。 +- [x] **Whisper 语言自动检测**: `transcribe()` language 参数改为可选(默认 None = 自动检测),支持多语言参考音频。 +- [x] **前端清理**: 移除固定 ref_text 常量、朗读引导文字,简化为"上传任意语音样本,系统将自动识别内容并克隆声音"。 + ### Day 22: 多素材优化 + AI 翻译 + TTS 多语言 - [x] **多素材 Bug 修复**: 6 个高优 Bug(边界溢出、单段 fallback、除零、duration 校验、Whisper 兜底、空列表检查)。 - [x] **架构重构**: 多素材从"逐段 LatentSync"重构为"先拼接再推理",推理次数 N→1。 @@ -117,7 +140,7 @@ - [x] **体验细节优化**: 录音预览 URL 回收,预览弹窗滚动恢复,全局任务提示挂载。 ### Day 16: 深度性能优化 -- [x] **Qwen-TTS 加速**: 集成 Flash Attention 2,模型加载速度提升至 8.9s。 +- [x] **Qwen-TTS 加速**: 集成 Flash Attention 2 (已停用,被 CosyVoice 3.0 替换)。 - [x] **服务守护**: 开发 `Watchdog` 看门狗机制,自动监控并重启僵死服务。 - [x] **LatentSync 性能确认**: 验证 DeepCache + 原生 Flash Attn 生效。 - [x] **文档重构**: 全面更新 README、部署手册及后端文档。 @@ -130,10 +153,10 @@ ### Day 14: AI 增强与体验优化 - [x] **AI 标题/标签**: 集成 GLM-4API 自动生成视频元数据。 - [x] **字幕升级**: Remotion 逐字高亮字幕 (卡拉OK效果) 及动画片头。 -- [x] **模型升级**: Qwen3-TTS 升级至 1.7B-Base 版本。 +- [x] **模型升级**: 声音克隆已迁移至 CosyVoice 3.0 (0.5B)。 ### Day 13: 声音克隆集成 -- [x] **声音克隆微服务**: 封装 Qwen3-TTS 为独立 API (8009端口)。 +- [x] **声音克隆微服务**: 封装 CosyVoice 3.0 为独立 API (8010端口,替换 Qwen3-TTS)。 - [x] **参考音频管理**: Supabase 存储桶配置与管理接口。 - [x] **多模态 TTS**: 前端支持 EdgeTTS / Clone Voice 切换。 @@ -186,7 +209,7 @@ | **核心 API** | 100% | ✅ 稳定 | | **Web UI** | 100% | ✅ 稳定 (移动端适配) | | **唇形同步** | 100% | ✅ LatentSync 1.6 | -| **TTS 配音** | 100% | ✅ EdgeTTS + Qwen3 + 配音前置 + 时间轴编排 | +| **TTS 配音** | 100% | ✅ EdgeTTS + CosyVoice 3.0 + 配音前置 + 时间轴编排 + 自动转写 + 语速控制 | | **自动发布** | 100% | ✅ 抖音/微信视频号/B站/小红书 | | **用户认证** | 100% | ✅ 手机号 + JWT | | **部署运维** | 100% | ✅ PM2 + Watchdog | diff --git a/README.md b/README.md index 34de5ef..03e91f1 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,9 @@ - 🎙️ **多模态配音** - 支持 **EdgeTTS** (微软超自然语音, 10 语言) 和 **CosyVoice 3.0** (3秒极速声音克隆, 9语言+18方言, 语速可调)。上传参考音频自动 Whisper 转写 + 智能截取。配音前置工作流:先生成配音 → 选素材 → 生成视频。 - 📝 **智能字幕** - 集成 faster-whisper + Remotion,自动生成逐字高亮 (卡拉OK效果) 字幕。 - 🎨 **样式预设** - 标题/字幕样式选择 + 预览 + 字号调节,支持自定义字体库。 -- 🖼️ **作品预览一致性** - 标题/字幕预览按素材分辨率缩放,效果更接近成片。 -- 🎞️ **多素材多机位** - 支持多选素材 + 时间轴编辑器 (wavesurfer.js 波形可视化),拖拽分割线调整时长、拖拽排序切换机位、截取源视频片段。 +- 🖼️ **作品预览一致性** - 标题/字幕预览与 Remotion 成片统一响应式缩放和自动换行,窄屏画布也稳定显示。 +- 🎞️ **多素材多机位** - 支持多选素材 + 时间轴编辑器 (wavesurfer.js 波形可视化),拖拽分割线调整时长、拖拽排序切换机位、按 `source_start/source_end` 截取片段。 +- 📐 **画面比例控制** - 时间轴一键切换 `9:16 / 16:9` 输出比例,生成链路全程按目标比例处理。 - 💾 **用户偏好持久化** - 首页状态统一恢复/保存,刷新后延续上次配置。历史文案手动保存与加载。 - 🎵 **背景音乐** - 试听 + 音量控制 + 混音,保持配音音量稳定。 - 🤖 **AI 辅助创作** - 内置 GLM-4.7-Flash,支持 B站/抖音链接文案提取、AI 洗稿、标题/标签自动生成、9 语言翻译。