diff --git a/Docs/DEPLOY_MANUAL.md b/Docs/DEPLOY_MANUAL.md
index cb30132..3c2858c 100644
--- a/Docs/DEPLOY_MANUAL.md
+++ b/Docs/DEPLOY_MANUAL.md
@@ -258,7 +258,32 @@ chmod +x run_latentsync.sh
pm2 start ./run_latentsync.sh --name vigent2-latentsync
```
-### 4. 保存当前列表 (开机自启)
+### 4. 启动 Qwen3-TTS 声音克隆服务 (可选)
+
+> 如需使用声音克隆功能,需要启动此服务。
+
+1. 安装 HTTP 服务依赖:
+```bash
+conda activate qwen-tts
+pip install fastapi uvicorn python-multipart
+```
+
+2. 启动脚本位于项目根目录: `run_qwen_tts.sh`
+
+3. 使用 pm2 启动:
+```bash
+cd /home/rongye/ProgramFiles/ViGent2
+pm2 start ./run_qwen_tts.sh --name vigent2-qwen-tts
+pm2 save
+```
+
+4. 验证服务:
+```bash
+# 检查健康状态
+curl http://localhost:8009/health
+```
+
+### 5. 保存当前列表 (开机自启)
```bash
pm2 save
@@ -271,6 +296,7 @@ pm2 startup
pm2 status # 查看所有服务状态
pm2 logs # 查看所有日志
pm2 logs vigent2-backend # 查看后端日志
+pm2 logs vigent2-qwen-tts # 查看 Qwen3-TTS 日志
pm2 restart all # 重启所有服务
pm2 stop vigent2-latentsync # 停止 LatentSync 服务
pm2 delete all # 删除所有服务
@@ -370,6 +396,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
```
### 查看日志
@@ -379,6 +406,7 @@ sudo lsof -i :8007
pm2 logs vigent2-backend
pm2 logs vigent2-frontend
pm2 logs vigent2-latentsync
+pm2 logs vigent2-qwen-tts
```
### SSH 连接卡顿 / 系统响应慢
diff --git a/Docs/DevLogs/Day13.md b/Docs/DevLogs/Day13.md
new file mode 100644
index 0000000..efedf24
--- /dev/null
+++ b/Docs/DevLogs/Day13.md
@@ -0,0 +1,279 @@
+# Day 13 - 声音克隆功能集成完成
+
+**日期**:2026-01-29
+
+---
+
+## 🎙️ Qwen3-TTS 服务集成
+
+### 背景
+在 Day 12 完成 Qwen3-TTS 模型部署后,今日重点是将其集成到 ViGent2 系统中,提供完整的声音克隆功能。
+
+### 架构设计
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ 前端 (Next.js) │
+│ 参考音频上传 → TTS 模式选择 → 视频生成请求 │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ 后端 (FastAPI :8006) │
+│ ref-audios API → voice_clone_service → video_service │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Qwen3-TTS 服务 (FastAPI :8009) │
+│ HTTP /generate → 返回克隆音频 │
+└─────────────────────────────────────────────────────────────┘
+```
+
+### Qwen3-TTS HTTP 服务 (`qwen_tts_server.py`)
+
+创建独立的 FastAPI 服务,运行在 8009 端口:
+
+```python
+from fastapi import FastAPI, UploadFile, Form, HTTPException
+from fastapi.responses import Response
+import torch
+import soundfile as sf
+from qwen_tts import Qwen3TTSModel
+import io, os
+
+app = FastAPI(title="Qwen3-TTS Voice Clone Service")
+
+# GPU 配置
+GPU_ID = os.getenv("QWEN_TTS_GPU_ID", "0")
+model = None
+
+@app.on_event("startup")
+async def load_model():
+ global model
+ model = Qwen3TTSModel.from_pretrained(
+ "./checkpoints/0.6B-Base",
+ device_map=f"cuda:{GPU_ID}",
+ dtype=torch.bfloat16,
+ )
+
+@app.get("/health")
+async def health():
+ return {"service": "Qwen3-TTS", "ready": model is not None, "gpu_id": GPU_ID}
+
+@app.post("/generate")
+async def generate(
+ ref_audio: UploadFile,
+ text: str = Form(...),
+ ref_text: str = Form(""),
+ language: str = Form("Chinese"),
+):
+ # 保存临时参考音频
+ ref_path = f"/tmp/ref_{ref_audio.filename}"
+ with open(ref_path, "wb") as f:
+ f.write(await ref_audio.read())
+
+ # 生成克隆音频
+ wavs, sr = model.generate_voice_clone(
+ text=text,
+ language=language,
+ ref_audio=ref_path,
+ ref_text=ref_text or "一段参考音频。",
+ )
+
+ # 返回 WAV 音频
+ buffer = io.BytesIO()
+ sf.write(buffer, wavs[0], sr, format="WAV")
+ buffer.seek(0)
+ return Response(content=buffer.read(), media_type="audio/wav")
+```
+
+### 后端声音克隆服务 (`voice_clone_service.py`)
+
+通过 HTTP 调用 Qwen3-TTS 服务:
+
+```python
+import aiohttp
+from loguru import logger
+
+QWEN_TTS_URL = "http://localhost:8009"
+
+async def generate_cloned_audio(
+ ref_audio_path: str,
+ text: str,
+ output_path: str,
+ ref_text: str = "",
+) -> str:
+ """调用 Qwen3-TTS 服务生成克隆音频"""
+
+ async with aiohttp.ClientSession() as session:
+ with open(ref_audio_path, "rb") as f:
+ data = aiohttp.FormData()
+ data.add_field("ref_audio", f, filename="ref.wav")
+ data.add_field("text", text)
+ data.add_field("ref_text", ref_text)
+
+ async with session.post(f"{QWEN_TTS_URL}/generate", data=data) as resp:
+ if resp.status != 200:
+ raise Exception(f"Qwen3-TTS error: {resp.status}")
+
+ audio_data = await resp.read()
+ with open(output_path, "wb") as out:
+ out.write(audio_data)
+
+ return output_path
+```
+
+---
+
+## 📂 参考音频管理 API
+
+### 新增 API 端点 (`ref_audios.py`)
+
+| 端点 | 方法 | 功能 |
+|------|------|------|
+| `/api/ref-audios` | GET | 获取参考音频列表 |
+| `/api/ref-audios` | POST | 上传参考音频 |
+| `/api/ref-audios/{id}` | DELETE | 删除参考音频 |
+
+### Supabase Bucket 配置
+
+为参考音频创建独立存储桶:
+
+```sql
+-- 创建 ref-audios bucket
+INSERT INTO storage.buckets (id, name, public)
+VALUES ('ref-audios', 'ref-audios', true)
+ON CONFLICT (id) DO NOTHING;
+
+-- RLS 策略
+CREATE POLICY "Allow public uploads" ON storage.objects
+FOR INSERT TO anon WITH CHECK (bucket_id = 'ref-audios');
+
+CREATE POLICY "Allow public read" ON storage.objects
+FOR SELECT TO anon USING (bucket_id = 'ref-audios');
+
+CREATE POLICY "Allow public delete" ON storage.objects
+FOR DELETE TO anon USING (bucket_id = 'ref-audios');
+```
+
+---
+
+## 🎨 前端声音克隆 UI
+
+### TTS 模式选择
+
+在视频生成页面新增声音克隆选项:
+
+```tsx
+{/* TTS 模式选择 */}
+
+
+
+
+```
+
+### 参考音频管理
+
+新增参考音频上传和列表展示功能:
+
+| 功能 | 实现 |
+|------|------|
+| 音频上传 | 拖拽上传 WAV/MP3,直传 Supabase |
+| 列表展示 | 显示文件名、时长、上传时间 |
+| 快速选择 | 点击即选中作为参考音频 |
+| 删除功能 | 删除不需要的参考音频 |
+
+---
+
+## ✅ 端到端测试验证
+
+### 测试流程
+1. **上传参考音频**: 3 秒参考音频 → Supabase ref-audios bucket
+2. **选择声音克隆模式**: TTS 模式切换为 "声音克隆"
+3. **输入文案**: 测试口播文案
+4. **生成视频**:
+ - TTS 阶段调用 Qwen3-TTS (17.7s)
+ - LipSync 阶段调用 LatentSync (122.8s)
+5. **播放验证**: 视频声音与参考音色一致
+
+### 测试结果
+- ✅ 参考音频上传成功
+- ✅ Qwen3-TTS 生成克隆音频 (15s 推理,4.6s 音频)
+- ✅ LatentSync 唇形同步正常
+- ✅ 总生成时间 143.1s
+- ✅ 前端视频播放正常
+
+---
+
+## 🔧 PM2 服务配置
+
+### 新增 Qwen3-TTS 服务
+
+**前置依赖安装**:
+```bash
+conda activate qwen-tts
+pip install fastapi uvicorn python-multipart
+```
+
+启动脚本 `run_qwen_tts.sh` (位于项目**根目录**):
+```bash
+#!/bin/bash
+cd /home/rongye/ProgramFiles/ViGent2/models/Qwen3-TTS
+/home/rongye/ProgramFiles/miniconda3/envs/qwen-tts/bin/python qwen_tts_server.py
+```
+
+PM2 管理命令:
+```bash
+# 进入根目录启动
+cd /home/rongye/ProgramFiles/ViGent2
+pm2 start ./run_qwen_tts.sh --name vigent2-qwen-tts
+pm2 save
+
+# 查看状态
+pm2 status
+
+# 查看日志
+pm2 logs vigent2-qwen-tts --lines 50
+```
+
+### 完整服务列表
+
+| 服务名 | 端口 | 功能 |
+|--------|------|------|
+| vigent2-backend | 8006 | FastAPI 后端 |
+| vigent2-frontend | 3002 | Next.js 前端 |
+| vigent2-latentsync | 8007 | LatentSync 唇形同步 |
+| vigent2-qwen-tts | 8009 | Qwen3-TTS 声音克隆 |
+
+---
+
+## 📁 今日修改文件清单
+
+| 文件 | 变更类型 | 说明 |
+|------|----------|------|
+| `models/Qwen3-TTS/qwen_tts_server.py` | 新增 | Qwen3-TTS HTTP 推理服务 |
+| `run_qwen_tts.sh` | 新增 | PM2 启动脚本 (根目录) |
+| `backend/app/services/voice_clone_service.py` | 新增 | 声音克隆服务 (HTTP 调用) |
+| `backend/app/api/ref_audios.py` | 新增 | 参考音频管理 API |
+| `backend/app/main.py` | 修改 | 注册 ref-audios 路由 |
+| `frontend/src/app/page.tsx` | 修改 | TTS 模式选择 + 参考音频 UI |
+
+---
+
+## 🔗 相关文档
+
+- [task_complete.md](../task_complete.md) - 任务总览
+- [Day12.md](./Day12.md) - iOS 兼容与 Qwen3-TTS 部署
+- [QWEN3_TTS_DEPLOY.md](../QWEN3_TTS_DEPLOY.md) - Qwen3-TTS 部署指南
+- [DEPLOY_MANUAL.md](../DEPLOY_MANUAL.md) - 完整部署手册
diff --git a/Docs/FRONTEND_DEV.md b/Docs/FRONTEND_DEV.md
index f7e04a8..4adb8f5 100644
--- a/Docs/FRONTEND_DEV.md
+++ b/Docs/FRONTEND_DEV.md
@@ -180,3 +180,52 @@ const formatDate = (timestamp: number) => {
2. [ ] 所有 API 请求使用 `api.get/post/delete()` 而非原生 `fetch`
3. [ ] 日期格式化使用固定格式函数,不用 `toLocaleString()`
4. [ ] 添加 `'use client'` 指令(如需客户端交互)
+
+---
+
+## 声音克隆 (Voice Clone) 功能
+
+### API 端点
+
+| 接口 | 方法 | 功能 |
+|------|------|------|
+| `/api/ref-audios` | POST | 上传参考音频 (multipart/form-data: file + ref_text) |
+| `/api/ref-audios` | GET | 列出用户的参考音频 |
+| `/api/ref-audios/{id}` | DELETE | 删除参考音频 (id 需 encodeURIComponent) |
+
+### 视频生成 API 扩展
+
+```typescript
+// EdgeTTS 模式 (默认)
+await api.post('/api/videos/generate', {
+ material_path: '...',
+ text: '口播文案',
+ tts_mode: 'edgetts',
+ voice: 'zh-CN-YunxiNeural',
+});
+
+// 声音克隆模式
+await api.post('/api/videos/generate', {
+ material_path: '...',
+ text: '口播文案',
+ tts_mode: 'voiceclone',
+ ref_audio_id: 'user_id/timestamp_name.wav',
+ ref_text: '参考音频对应文字',
+});
+```
+
+### 在线录音
+
+使用 `MediaRecorder` API 录制音频,格式为 `audio/webm`,上传后后端自动转换为 WAV (16kHz mono)。
+
+```typescript
+// 录音需要用户授权麦克风
+const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
+```
+
+### UI 结构
+
+配音方式使用 Tab 切换:
+- **EdgeTTS 音色** - 预设音色 2x3 网格
+- **声音克隆** - 参考音频列表 + 在线录音 + 参考文字输入
diff --git a/Docs/Logs.md b/Docs/Logs.md
index 8cf5efd..2a0ea8b 100644
--- a/Docs/Logs.md
+++ b/Docs/Logs.md
@@ -1,29 +1,333 @@
-rongye@r730-ubuntu:~/ProgramFiles/Supabase$ docker compose up -d
-[+] up 136/136
- ✔ Image timberio/vector:0.28.1-alpine Pulled 63.3ss
- ✔ Image supabase/storage-api:v1.33.0 Pulled 78.6ss
- ✔ Image darthsim/imgproxy:v3.30.1 Pulled 151.9s
- ✔ Image supabase/postgres-meta:v0.95.1 Pulled 87.5ss
- ✔ Image supabase/logflare:1.27.0 Pulled 229.2s
- ✔ Image supabase/postgres:15.8.1.085 Pulled 268.3s
- ✔ Image supabase/supavisor:2.7.4 Pulled 101.6s
- ✔ Image supabase/realtime:v2.68.0 Pulled 56.5ss
- ✔ Image postgrest/postgrest:v14.1 Pulled 201.8s
- ✔ Image supabase/edge-runtime:v1.69.28 Pulled 254.0s
- ✔ Network supabase_default Created 0.1s
- ✔ Volume supabase_db-config Created 0.1s
- ✔ Container supabase-vector Healthy 16.9s
- ✔ Container supabase-imgproxy Created 7.4s
- ✔ Container supabase-db Healthy 20.6s
- ✔ Container supabase-analytics Created 0.4s
- ✔ Container supabase-edge-functions Created 1.8s
- ✔ Container supabase-auth Created 1.7s
- ✔ Container supabase-studio Created 2.0s
- ✔ Container realtime-dev.supabase-realtime Created 1.7s
- ✔ Container supabase-pooler Created 1.8s
- ✔ Container supabase-kong Created 1.7s
- ✔ Container supabase-meta Created 2.0s
- ✔ Container supabase-rest Created 0.9s
- ✔ Container supabase-storage Created 1.4s
-Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint supabase-analytics (2fd60a510a1f16bf29f8f5140f14ef457a284c5b65a2567b7be250a4f9708f34): failed to bind host port 0.0.0.0:4000/tcp: address already in use
-[ble: exit 1]
\ No newline at end of file
+rongye@r730-ubuntu:~$ pm2 logs vigent2-qwen-tts
+[TAILING] Tailing last 15 lines for [vigent2-qwen-tts] process (change the value with --lines option)
+/home/rongye/.pm2/logs/vigent2-qwen-tts-error.log last 15 lines:
+13|vigent2 | Setting `pad_token_id` to `eos_token_id`:2150 for open-end generation.
+
+/home/rongye/.pm2/logs/vigent2-qwen-tts-out.log last 15 lines:
+13|vigent2 | 🔄 Loading Qwen3-TTS model...
+13|vigent2 |
+13|vigent2 | ********
+13|vigent2 | Warning: flash-attn is not installed. Will only run the manual PyTorch version. Please install flash-attn for faster inference.
+13|vigent2 | ********
+13|vigent2 |
+13|vigent2 | ✅ Qwen3-TTS model loaded in 8.6s
+13|vigent2 | INFO: 127.0.0.1:56814 - "GET /health HTTP/1.1" 200 OK
+13|vigent2 | 🎤 Generating: 大家好,欢迎来到我的频道,今天给大家分享一些有趣的内容。...
+13|vigent2 | 📝 Ref text: 其实生活中有许多美好的瞬间,比如清晨的阳光,或者一杯温热的清茶。希望这次生成的音色能够自然、流畅,完...
+13|vigent2 | [WARNING] Min value of input waveform signal is -1.006709337234497
+13|vigent2 | [WARNING] Max value of input waveform signal is 1.0008893013000488
+13|vigent2 | ✅ Generated in 15.0s, duration: 4.6s
+13|vigent2 | INFO: 127.0.0.1:36556 - "POST /generate HTTP/1.1" 200 OK
+
+
+
+
+rongye@r730-ubuntu:~$ pm2 logs vigent2-backend --lines 400
+[TAILING] Tailing last 400 lines for [vigent2-backend] process (change the value with --lines option)
+/home/rongye/.pm2/logs/vigent2-backend-out.log last 400 lines:
+11|vigent2 | Storage endpoint URL should have a trailing slash.
+11|vigent2 | Storage endpoint URL should have a trailing slash.
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769651820268 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769651825016 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/publish/accounts HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769651828852 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769654501430 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769654987404 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "POST /api/ref-audios HTTP/1.1" 500 Internal Server Error
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769655093628 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "POST /api/ref-audios HTTP/1.1" 500 Internal Server Error
+11|vigent2 | Storage endpoint URL should have a trailing slash.
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769655569331 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "POST /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "POST /api/videos/generate HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | [Pipeline] TTS completed in 17.7s
+11|vigent2 | [LipSync] Health check: ready=True
+11|vigent2 | [LipSync] Starting LatentSync inference...
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | [Pipeline] LipSync completed in 122.8s
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | [Pipeline] Total generation time: 143.1s
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/tasks/bf4760b8-e338-49ee-9777-828c1ef0c855 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/publish/accounts HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769655769762 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/publish/accounts HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769655923194 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769655960629 HTTP/1.1" 403 Forbidden
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 403 Forbidden
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 403 Forbidden
+11|vigent2 | INFO: 27.17.161.128:0 - "POST /api/auth/logout HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "POST /api/auth/login HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769655964287 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/publish/accounts HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 403 Forbidden
+11|vigent2 | INFO: 27.17.161.128:0 - "POST /api/auth/logout HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/publish/accounts HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "POST /api/auth/login HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769656015718 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769656233290 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "POST /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/publish/accounts HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769656987465 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/materials?t=1769657141569 HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/videos/generated HTTP/1.1" 200 OK
+11|vigent2 | INFO: 27.17.161.128:0 - "GET /api/ref-audios HTTP/1.1" 200 OK
+
+/home/rongye/.pm2/logs/vigent2-backend-error.log last 400 lines:
+11|vigent2 | rnPaKCW6D20R7A4QpeumwXIRUkzHtaFASP40bWfE6KL05g4rq6VbZFQ9X4FVBZ2lbwW%2Faa32knjuye8aa1ejtZEGmyfXpfcryezEIy0gmYYjjT7lKB6HAupj9%2FCezQ%3D%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2BpEG8yoPZBpac674EsBSuEU0HhlEZGMLIKNfviY6GGLzzbk6%2BbdfmzJ5s1nr16B0NNWzywMDwDD00Cktdf8N50BWw0Pp7Xuy2cOM6L15tjqobzRZyayXyVA1o%2B5kHPODaa3yg4cjWjee8OqG1qRaX4EwOXc0YzPZI%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2BRY4oSsXW07D0HdYfXZhDpJS%2Fl%2F%2Bysns8Xand%2BMI7%2FJBIRw1RV%2FIJPzbTSpW8kmvwLCsUosyNPtsZbl3lGRDOM4YJIL%2BaFVjvjAWDo0WA89ezEeTVY9hzd9rwV3A6dbv5vJhrEdjolAkub50ItC47iV1fIGb%2FN3vI%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19r9Vr3ov%2BP30OgbarNSaCn6bebg11iU%2B8UV7b%2F116JurvSpJ77d%2FdZ62kjIP%2BMF3h3R9RathLKFQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19CE%2F3GmyTsZHCQhdFWdzYnJYPdvCMBFbM%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18t2%2FxrnN2HPEqTssR572nq%2FgCim9EQN7E%3D'}
+11|vigent2 | 2026-01-29 11:06:00.756 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/videos/generated
+11|vigent2 | 2026-01-29 11:06:00.757 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-fetch-dest': 'empty', 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1', 'accept': 'application/json, text/plain, */*', 'referer': 'https://vigent.hbyrkj.top/', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=3, i', 'accept-encoding': 'gzip, deflate, br', 'cookie': 'access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiN2JlNWIzNWYtMzQ0Ni00ZTIyLWEzMTktZTc5M2NlNDBmYTRiIiwiZXhwIjoxNzcwMTc4MTE0fQ.pk4sCAkd9hcN6fE5_8RXH42zfMl7YPSV5i1R9QeER4s; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1760758482696%2C%220199f562-16c1-7fcb-9bef-fdc33838b6a8%22%2C1760758470336%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_session=RudderEncrypt%3AU2FsdGVkX19ssBuVw9hBTDRVrnPaKCW6D20R7A4QpeumwXIRUkzHtaFASP40bWfE6KL05g4rq6VbZFQ9X4FVBZ2lbwW%2Faa32knjuye8aa1ejtZEGmyfXpfcryezEIy0gmYYjjT7lKB6HAupj9%2FCezQ%3D%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2BpEG8yoPZBpac674EsBSuEU0HhlEZGMLIKNfviY6GGLzzbk6%2BbdfmzJ5s1nr16B0NNWzywMDwDD00Cktdf8N50BWw0Pp7Xuy2cOM6L15tjqobzRZyayXyVA1o%2B5kHPODaa3yg4cjWjee8OqG1qRaX4EwOXc0YzPZI%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2BRY4oSsXW07D0HdYfXZhDpJS%2Fl%2F%2Bysns8Xand%2BMI7%2FJBIRw1RV%2FIJPzbTSpW8kmvwLCsUosyNPtsZbl3lGRDOM4YJIL%2BaFVjvjAWDo0WA89ezEeTVY9hzd9rwV3A6dbv5vJhrEdjolAkub50ItC47iV1fIGb%2FN3vI%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19r9Vr3ov%2BP30OgbarNSaCn6bebg11iU%2B8UV7b%2F116JurvSpJ77d%2FdZ62kjIP%2BMF3h3R9RathLKFQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19CE%2F3GmyTsZHCQhdFWdzYnJYPdvCMBFbM%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18t2%2FxrnN2HPEqTssR572nq%2FgCim9EQN7E%3D'}
+11|vigent2 | 2026-01-29 11:06:00.765 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/ref-audios - Status: 403 - Duration: 0.02s
+11|vigent2 | 2026-01-29 11:06:00.766 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/videos/generated - Status: 403 - Duration: 0.01s
+11|vigent2 | 2026-01-29 11:06:00.812 | INFO | app.main:dispatch:21 - START Request: POST https://vigent.hbyrkj.top/api/auth/logout
+11|vigent2 | 2026-01-29 11:06:00.812 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'content-length': '0', 'accept': '*/*', 'sec-fetch-site': 'same-origin', 'origin': 'https://vigent.hbyrkj.top', 'sec-fetch-mode': 'cors', 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1', 'referer': 'https://vigent.hbyrkj.top/', 'sec-fetch-dest': 'empty', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=3, i', 'accept-encoding': 'gzip, deflate, br', 'cookie': 'access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiN2JlNWIzNWYtMzQ0Ni00ZTIyLWEzMTktZTc5M2NlNDBmYTRiIiwiZXhwIjoxNzcwMTc4MTE0fQ.pk4sCAkd9hcN6fE5_8RXH42zfMl7YPSV5i1R9QeER4s; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1760758482696%2C%220199f562-16c1-7fcb-9bef-fdc33838b6a8%22%2C1760758470336%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_session=RudderEncrypt%3AU2FsdGVkX19ssBuVw9hBTDRVrnPaKCW6D20R7A4QpeumwXIRUkzHtaFASP40bWfE6KL05g4rq6VbZFQ9X4FVBZ2lbwW%2Faa32knjuye8aa1ejtZEGmyfXpfcryezEIy0gmYYjjT7lKB6HAupj9%2FCezQ%3D%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2BpEG8yoPZBpac674EsBSuEU0HhlEZGMLIKNfviY6GGLzzbk6%2BbdfmzJ5s1nr16B0NNWzywMDwDD00Cktdf8N50BWw0Pp7Xuy2cOM6L15tjqobzRZyayXyVA1o%2B5kHPODaa3yg4cjWjee8OqG1qRaX4EwOXc0YzPZI%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2BRY4oSsXW07D0HdYfXZhDpJS%2Fl%2F%2Bysns8Xand%2BMI7%2FJBIRw1RV%2FIJPzbTSpW8kmvwLCsUosyNPtsZbl3lGRDOM4YJIL%2BaFVjvjAWDo0WA89ezEeTVY9hzd9rwV3A6dbv5vJhrEdjolAkub50ItC47iV1fIGb%2FN3vI%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19r9Vr3ov%2BP30OgbarNSaCn6bebg11iU%2B8UV7b%2F116JurvSpJ77d%2FdZ62kjIP%2BMF3h3R9RathLKFQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19CE%2F3GmyTsZHCQhdFWdzYnJYPdvCMBFbM%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18t2%2FxrnN2HPEqTssR572nq%2FgCim9EQN7E%3D'}
+11|vigent2 | 2026-01-29 11:06:00.815 | INFO | app.main:dispatch:26 - END Request: POST https://vigent.hbyrkj.top/api/auth/logout - Status: 200 - Duration: 0.00s
+11|vigent2 | 2026-01-29 11:06:03.694 | INFO | app.main:dispatch:21 - START Request: POST https://vigent.hbyrkj.top/api/auth/login
+11|vigent2 | 2026-01-29 11:06:03.695 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'content-length': '58', 'accept': '*/*', 'content-type': 'application/json', 'sec-fetch-site': 'same-origin', 'origin': 'https://vigent.hbyrkj.top', 'sec-fetch-mode': 'cors', 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1', 'referer': 'https://vigent.hbyrkj.top/login', 'sec-fetch-dest': 'empty', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=3, i', 'accept-encoding': 'gzip, deflate, br', 'cookie': 'ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1760758482696%2C%220199f562-16c1-7fcb-9bef-fdc33838b6a8%22%2C1760758470336%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_session=RudderEncrypt%3AU2FsdGVkX19ssBuVw9hBTDRVrnPaKCW6D20R7A4QpeumwXIRUkzHtaFASP40bWfE6KL05g4rq6VbZFQ9X4FVBZ2lbwW%2Faa32knjuye8aa1ejtZEGmyfXpfcryezEIy0gmYYjjT7lKB6HAupj9%2FCezQ%3D%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2BpEG8yoPZBpac674EsBSuEU0HhlEZGMLIKNfviY6GGLzzbk6%2BbdfmzJ5s1nr16B0NNWzywMDwDD00Cktdf8N50BWw0Pp7Xuy2cOM6L15tjqobzRZyayXyVA1o%2B5kHPODaa3yg4cjWjee8OqG1qRaX4EwOXc0YzPZI%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2BRY4oSsXW07D0HdYfXZhDpJS%2Fl%2F%2Bysns8Xand%2BMI7%2FJBIRw1RV%2FIJPzbTSpW8kmvwLCsUosyNPtsZbl3lGRDOM4YJIL%2BaFVjvjAWDo0WA89ezEeTVY9hzd9rwV3A6dbv5vJhrEdjolAkub50ItC47iV1fIGb%2FN3vI%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19r9Vr3ov%2BP30OgbarNSaCn6bebg11iU%2B8UV7b%2F116JurvSpJ77d%2FdZ62kjIP%2BMF3h3R9RathLKFQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19CE%2F3GmyTsZHCQhdFWdzYnJYPdvCMBFbM%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18t2%2FxrnN2HPEqTssR572nq%2FgCim9EQN7E%3D'}
+11|vigent2 | 2026-01-29 11:06:04.185 | INFO | app.api.auth:login:157 - 用户登录: lamnickdavid@gmail.com
+11|vigent2 | 2026-01-29 11:06:04.185 | INFO | app.main:dispatch:26 - END Request: POST https://vigent.hbyrkj.top/api/auth/login - Status: 200 - Duration: 0.49s
+11|vigent2 | 2026-01-29 11:06:04.359 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/materials?t=1769655964287
+11|vigent2 | 2026-01-29 11:06:04.359 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-fetch-dest': 'empty', 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1', 'accept': 'application/json, text/plain, */*', 'referer': 'https://vigent.hbyrkj.top/', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=3, i', 'accept-encoding': 'gzip, deflate, br', 'cookie': 'access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMWE0NzczNTktZmMwZS00MjVhLTk3MGUtODc1ZTcyNjFjYWJiIiwiZXhwIjoxNzcwMjYwNzY0fQ.X-nGjaX_gwaJw995Zuw_fnj2oY_K-oM6tgwMDR4pDQk; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1760758482696%2C%220199f562-16c1-7fcb-9bef-fdc33838b6a8%22%2C1760758470336%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_session=RudderEncrypt%3AU2FsdGVkX19ssBuVw9hBTDRVrnPaKCW6D20R7A4QpeumwXIRUkzHtaFASP40bWfE6KL05g4rq6VbZFQ9X4FVBZ2lbwW%2Faa32knjuye8aa1ejtZEGmyfXpfcryezEIy0gmYYjjT7lKB6HAupj9%2FCezQ%3D%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2BpEG8yoPZBpac674EsBSuEU0HhlEZGMLIKNfviY6GGLzzbk6%2BbdfmzJ5s1nr16B0NNWzywMDwDD00Cktdf8N50BWw0Pp7Xuy2cOM6L15tjqobzRZyayXyVA1o%2B5kHPODaa3yg4cjWjee8OqG1qRaX4EwOXc0YzPZI%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2BRY4oSsXW07D0HdYfXZhDpJS%2Fl%2F%2Bysns8Xand%2BMI7%2FJBIRw1RV%2FIJPzbTSpW8kmvwLCsUosyNPtsZbl3lGRDOM4YJIL%2BaFVjvjAWDo0WA89ezEeTVY9hzd9rwV3A6dbv5vJhrEdjolAkub50ItC47iV1fIGb%2FN3vI%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19r9Vr3ov%2BP30OgbarNSaCn6bebg11iU%2B8UV7b%2F116JurvSpJ77d%2FdZ62kjIP%2BMF3h3R9RathLKFQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19CE%2F3GmyTsZHCQhdFWdzYnJYPdvCMBFbM%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18t2%2FxrnN2HPEqTssR572nq%2FgCim9EQN7E%3D'}
+11|vigent2 | 2026-01-29 11:06:04.377 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/ref-audios
+11|vigent2 | 2026-01-29 11:06:04.377 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-fetch-dest': 'empty', 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1', 'accept': 'application/json, text/plain, */*', 'referer': 'https://vigent.hbyrkj.top/', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=3, i', 'accept-encoding': 'gzip, deflate, br', 'cookie': 'access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMWE0NzczNTktZmMwZS00MjVhLTk3MGUtODc1ZTcyNjFjYWJiIiwiZXhwIjoxNzcwMjYwNzY0fQ.X-nGjaX_gwaJw995Zuw_fnj2oY_K-oM6tgwMDR4pDQk; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1760758482696%2C%220199f562-16c1-7fcb-9bef-fdc33838b6a8%22%2C1760758470336%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_session=RudderEncrypt%3AU2FsdGVkX19ssBuVw9hBTDRVrnPaKCW6D20R7A4QpeumwXIRUkzHtaFASP40bWfE6KL05g4rq6VbZFQ9X4FVBZ2lbwW%2Faa32knjuye8aa1ejtZEGmyfXpfcryezEIy0gmYYjjT7lKB6HAupj9%2FCezQ%3D%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2BpEG8yoPZBpac674EsBSuEU0HhlEZGMLIKNfviY6GGLzzbk6%2BbdfmzJ5s1nr16B0NNWzywMDwDD00Cktdf8N50BWw0Pp7Xuy2cOM6L15tjqobzRZyayXyVA1o%2B5kHPODaa3yg4cjWjee8OqG1qRaX4EwOXc0YzPZI%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2BRY4oSsXW07D0HdYfXZhDpJS%2Fl%2F%2Bysns8Xand%2BMI7%2FJBIRw1RV%2FIJPzbTSpW8kmvwLCsUosyNPtsZbl3lGRDOM4YJIL%2BaFVjvjAWDo0WA89ezEeTVY9hzd9rwV3A6dbv5vJhrEdjolAkub50ItC47iV1fIGb%2FN3vI%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19r9Vr3ov%2BP30OgbarNSaCn6bebg11iU%2B8UV7b%2F116JurvSpJ77d%2FdZ62kjIP%2BMF3h3R9RathLKFQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19CE%2F3GmyTsZHCQhdFWdzYnJYPdvCMBFbM%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18t2%2FxrnN2HPEqTssR572nq%2FgCim9EQN7E%3D'}
+11|vigent2 | 2026-01-29 11:06:04.392 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/videos/generated
+11|vigent2 | 2026-01-29 11:06:04.392 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-fetch-dest': 'empty', 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1', 'accept': 'application/json, text/plain, */*', 'referer': 'https://vigent.hbyrkj.top/', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=3, i', 'accept-encoding': 'gzip, deflate, br', 'cookie': 'access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMWE0NzczNTktZmMwZS00MjVhLTk3MGUtODc1ZTcyNjFjYWJiIiwiZXhwIjoxNzcwMjYwNzY0fQ.X-nGjaX_gwaJw995Zuw_fnj2oY_K-oM6tgwMDR4pDQk; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1760758482696%2C%220199f562-16c1-7fcb-9bef-fdc33838b6a8%22%2C1760758470336%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_session=RudderEncrypt%3AU2FsdGVkX19ssBuVw9hBTDRVrnPaKCW6D20R7A4QpeumwXIRUkzHtaFASP40bWfE6KL05g4rq6VbZFQ9X4FVBZ2lbwW%2Faa32knjuye8aa1ejtZEGmyfXpfcryezEIy0gmYYjjT7lKB6HAupj9%2FCezQ%3D%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2BpEG8yoPZBpac674EsBSuEU0HhlEZGMLIKNfviY6GGLzzbk6%2BbdfmzJ5s1nr16B0NNWzywMDwDD00Cktdf8N50BWw0Pp7Xuy2cOM6L15tjqobzRZyayXyVA1o%2B5kHPODaa3yg4cjWjee8OqG1qRaX4EwOXc0YzPZI%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2BRY4oSsXW07D0HdYfXZhDpJS%2Fl%2F%2Bysns8Xand%2BMI7%2FJBIRw1RV%2FIJPzbTSpW8kmvwLCsUosyNPtsZbl3lGRDOM4YJIL%2BaFVjvjAWDo0WA89ezEeTVY9hzd9rwV3A6dbv5vJhrEdjolAkub50ItC47iV1fIGb%2FN3vI%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19r9Vr3ov%2BP30OgbarNSaCn6bebg11iU%2B8UV7b%2F116JurvSpJ77d%2FdZ62kjIP%2BMF3h3R9RathLKFQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19CE%2F3GmyTsZHCQhdFWdzYnJYPdvCMBFbM%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18t2%2FxrnN2HPEqTssR572nq%2FgCim9EQN7E%3D'}
+11|vigent2 | 2026-01-29 11:06:04.478 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/materials?t=1769655964287 - Status: 200 - Duration: 0.12s
+11|vigent2 | 2026-01-29 11:06:04.491 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/videos/generated - Status: 200 - Duration: 0.10s
+11|vigent2 | 2026-01-29 11:06:04.614 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/ref-audios - Status: 200 - Duration: 0.24s
+11|vigent2 | 2026-01-29 11:06:16.329 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/publish/accounts
+11|vigent2 | 2026-01-29 11:06:16.329 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/publish', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiNDk2N2QwNjMtNjhhZC00NzFkLThhMWQtOGE1MmJhODAxZjBjIiwiZXhwIjoxNzcwMTc4MzM4fQ.k9JOPKwqHrNTTNOsUQlMuA63rOETStl7uWXAIIDLGtA'}
+11|vigent2 | 2026-01-29 11:06:16.333 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/videos/generated
+11|vigent2 | 2026-01-29 11:06:16.333 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/publish', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiNDk2N2QwNjMtNjhhZC00NzFkLThhMWQtOGE1MmJhODAxZjBjIiwiZXhwIjoxNzcwMTc4MzM4fQ.k9JOPKwqHrNTTNOsUQlMuA63rOETStl7uWXAIIDLGtA'}
+11|vigent2 | 2026-01-29 11:06:16.342 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/publish/accounts - Status: 200 - Duration: 0.01s
+11|vigent2 | 2026-01-29 11:06:16.343 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/videos/generated - Status: 403 - Duration: 0.01s
+11|vigent2 | 2026-01-29 11:06:16.397 | INFO | app.main:dispatch:21 - START Request: POST https://vigent.hbyrkj.top/api/auth/logout
+11|vigent2 | 2026-01-29 11:06:16.397 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'content-length': '0', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'accept': '*/*', 'origin': 'https://vigent.hbyrkj.top', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/publish', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiNDk2N2QwNjMtNjhhZC00NzFkLThhMWQtOGE1MmJhODAxZjBjIiwiZXhwIjoxNzcwMTc4MzM4fQ.k9JOPKwqHrNTTNOsUQlMuA63rOETStl7uWXAIIDLGtA'}
+11|vigent2 | 2026-01-29 11:06:16.398 | INFO | app.main:dispatch:26 - END Request: POST https://vigent.hbyrkj.top/api/auth/logout - Status: 200 - Duration: 0.00s
+11|vigent2 | 2026-01-29 11:06:28.685 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/videos/generated
+11|vigent2 | 2026-01-29 11:06:28.686 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-fetch-dest': 'empty', 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1', 'accept': 'application/json, text/plain, */*', 'referer': 'https://vigent.hbyrkj.top/publish', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=3, i', 'accept-encoding': 'gzip, deflate, br', 'cookie': 'access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMWE0NzczNTktZmMwZS00MjVhLTk3MGUtODc1ZTcyNjFjYWJiIiwiZXhwIjoxNzcwMjYwNzY0fQ.X-nGjaX_gwaJw995Zuw_fnj2oY_K-oM6tgwMDR4pDQk; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1760758482696%2C%220199f562-16c1-7fcb-9bef-fdc33838b6a8%22%2C1760758470336%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_session=RudderEncrypt%3AU2FsdGVkX19ssBuVw9hBTDRVrnPaKCW6D20R7A4QpeumwXIRUkzHtaFASP40bWfE6KL05g4rq6VbZFQ9X4FVBZ2lbwW%2Faa32knjuye8aa1ejtZEGmyfXpfcryezEIy0gmYYjjT7lKB6HAupj9%2FCezQ%3D%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2BpEG8yoPZBpac674EsBSuEU0HhlEZGMLIKNfviY6GGLzzbk6%2BbdfmzJ5s1nr16B0NNWzywMDwDD00Cktdf8N50BWw0Pp7Xuy2cOM6L15tjqobzRZyayXyVA1o%2B5kHPODaa3yg4cjWjee8OqG1qRaX4EwOXc0YzPZI%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2BRY4oSsXW07D0HdYfXZhDpJS%2Fl%2F%2Bysns8Xand%2BMI7%2FJBIRw1RV%2FIJPzbTSpW8kmvwLCsUosyNPtsZbl3lGRDOM4YJIL%2BaFVjvjAWDo0WA89ezEeTVY9hzd9rwV3A6dbv5vJhrEdjolAkub50ItC47iV1fIGb%2FN3vI%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19r9Vr3ov%2BP30OgbarNSaCn6bebg11iU%2B8UV7b%2F116JurvSpJ77d%2FdZ62kjIP%2BMF3h3R9RathLKFQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19CE%2F3GmyTsZHCQhdFWdzYnJYPdvCMBFbM%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18t2%2FxrnN2HPEqTssR572nq%2FgCim9EQN7E%3D'}
+11|vigent2 | 2026-01-29 11:06:28.704 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/publish/accounts
+11|vigent2 | 2026-01-29 11:06:28.705 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-fetch-dest': 'empty', 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1', 'accept': 'application/json, text/plain, */*', 'referer': 'https://vigent.hbyrkj.top/publish', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=3, i', 'accept-encoding': 'gzip, deflate, br', 'cookie': 'access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMWE0NzczNTktZmMwZS00MjVhLTk3MGUtODc1ZTcyNjFjYWJiIiwiZXhwIjoxNzcwMjYwNzY0fQ.X-nGjaX_gwaJw995Zuw_fnj2oY_K-oM6tgwMDR4pDQk; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1760758482696%2C%220199f562-16c1-7fcb-9bef-fdc33838b6a8%22%2C1760758470336%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_session=RudderEncrypt%3AU2FsdGVkX19ssBuVw9hBTDRVrnPaKCW6D20R7A4QpeumwXIRUkzHtaFASP40bWfE6KL05g4rq6VbZFQ9X4FVBZ2lbwW%2Faa32knjuye8aa1ejtZEGmyfXpfcryezEIy0gmYYjjT7lKB6HAupj9%2FCezQ%3D%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX1%2BpEG8yoPZBpac674EsBSuEU0HhlEZGMLIKNfviY6GGLzzbk6%2BbdfmzJ5s1nr16B0NNWzywMDwDD00Cktdf8N50BWw0Pp7Xuy2cOM6L15tjqobzRZyayXyVA1o%2B5kHPODaa3yg4cjWjee8OqG1qRaX4EwOXc0YzPZI%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2BRY4oSsXW07D0HdYfXZhDpJS%2Fl%2F%2Bysns8Xand%2BMI7%2FJBIRw1RV%2FIJPzbTSpW8kmvwLCsUosyNPtsZbl3lGRDOM4YJIL%2BaFVjvjAWDo0WA89ezEeTVY9hzd9rwV3A6dbv5vJhrEdjolAkub50ItC47iV1fIGb%2FN3vI%3D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19r9Vr3ov%2BP30OgbarNSaCn6bebg11iU%2B8UV7b%2F116JurvSpJ77d%2FdZ62kjIP%2BMF3h3R9RathLKFQ%3D%3D; rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX19CE%2F3GmyTsZHCQhdFWdzYnJYPdvCMBFbM%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX18t2%2FxrnN2HPEqTssR572nq%2FgCim9EQN7E%3D'}
+11|vigent2 | 2026-01-29 11:06:28.710 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/publish/accounts - Status: 200 - Duration: 0.01s
+11|vigent2 | 2026-01-29 11:06:28.745 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/videos/generated - Status: 200 - Duration: 0.06s
+11|vigent2 | 2026-01-29 11:06:51.021 | INFO | app.main:dispatch:21 - START Request: POST https://vigent.hbyrkj.top/api/auth/login
+11|vigent2 | 2026-01-29 11:06:51.021 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'content-length': '58', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'content-type': 'application/json', 'sec-ch-ua-mobile': '?0', 'accept': '*/*', 'origin': 'https://vigent.hbyrkj.top', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/login', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D'}
+11|vigent2 | 2026-01-29 11:06:51.624 | INFO | app.api.auth:login:157 - 用户登录: lamnickdavid@gmail.com
+11|vigent2 | 2026-01-29 11:06:51.625 | INFO | app.main:dispatch:26 - END Request: POST https://vigent.hbyrkj.top/api/auth/login - Status: 200 - Duration: 0.60s
+11|vigent2 | 2026-01-29 11:06:51.806 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/materials?t=1769656015718
+11|vigent2 | 2026-01-29 11:06:51.806 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:06:51.820 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/videos/generated
+11|vigent2 | 2026-01-29 11:06:51.821 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:06:51.834 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/ref-audios
+11|vigent2 | 2026-01-29 11:06:51.834 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:06:51.865 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/materials?t=1769656015718 - Status: 200 - Duration: 0.06s
+11|vigent2 | 2026-01-29 11:06:51.941 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/videos/generated - Status: 200 - Duration: 0.12s
+11|vigent2 | 2026-01-29 11:06:52.076 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/ref-audios - Status: 200 - Duration: 0.24s
+11|vigent2 | 2026-01-29 11:10:29.354 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/materials?t=1769656233290
+11|vigent2 | 2026-01-29 11:10:29.354 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:10:29.405 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/ref-audios
+11|vigent2 | 2026-01-29 11:10:29.406 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:10:29.423 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/videos/generated
+11|vigent2 | 2026-01-29 11:10:29.423 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:10:29.474 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/materials?t=1769656233290 - Status: 200 - Duration: 0.12s
+11|vigent2 | 2026-01-29 11:10:29.535 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/videos/generated - Status: 200 - Duration: 0.11s
+11|vigent2 | 2026-01-29 11:10:29.653 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/ref-audios - Status: 200 - Duration: 0.25s
+11|vigent2 | 2026-01-29 11:11:20.032 | INFO | app.main:dispatch:21 - START Request: POST https://vigent.hbyrkj.top/api/ref-audios
+11|vigent2 | 2026-01-29 11:11:20.032 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'content-length': '204514', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryBGjf99CuOGlQdB7a', 'sec-ch-ua-mobile': '?0', 'origin': 'https://vigent.hbyrkj.top', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:11:20.536 | INFO | app.services.storage:upload_file:97 - Storage upload success: 94cd91e3-7d89-45e8-9d85-e8ba0660d74c/1769656280_myvoice.wav
+11|vigent2 | 2026-01-29 11:11:20.576 | INFO | app.services.storage:upload_file:97 - Storage upload success: 94cd91e3-7d89-45e8-9d85-e8ba0660d74c/1769656280_myvoice.json
+11|vigent2 | 2026-01-29 11:11:20.584 | INFO | app.main:dispatch:26 - END Request: POST https://vigent.hbyrkj.top/api/ref-audios - Status: 200 - Duration: 0.55s
+11|vigent2 | 2026-01-29 11:11:20.638 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/ref-audios
+11|vigent2 | 2026-01-29 11:11:20.638 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:11:21.086 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/ref-audios - Status: 200 - Duration: 0.45s
+11|vigent2 | 2026-01-29 11:22:58.683 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/videos/generated
+11|vigent2 | 2026-01-29 11:22:58.684 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/publish', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:22:58.742 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/publish/accounts
+11|vigent2 | 2026-01-29 11:22:58.743 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/publish', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:22:58.747 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/publish/accounts - Status: 200 - Duration: 0.01s
+11|vigent2 | 2026-01-29 11:22:58.798 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/videos/generated - Status: 200 - Duration: 0.11s
+11|vigent2 | 2026-01-29 11:23:03.509 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/materials?t=1769656987465
+11|vigent2 | 2026-01-29 11:23:03.510 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:23:03.535 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/videos/generated
+11|vigent2 | 2026-01-29 11:23:03.535 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:23:03.551 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/ref-audios
+11|vigent2 | 2026-01-29 11:23:03.552 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:23:03.569 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/materials?t=1769656987465 - Status: 200 - Duration: 0.06s
+11|vigent2 | 2026-01-29 11:23:03.658 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/videos/generated - Status: 200 - Duration: 0.12s
+11|vigent2 | 2026-01-29 11:23:03.996 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/ref-audios - Status: 200 - Duration: 0.44s
+11|vigent2 | 2026-01-29 11:25:37.605 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/ref-audios
+11|vigent2 | 2026-01-29 11:25:37.605 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:25:37.653 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/materials?t=1769657141569
+11|vigent2 | 2026-01-29 11:25:37.653 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:25:37.673 | INFO | app.main:dispatch:21 - START Request: GET https://vigent.hbyrkj.top/api/videos/generated
+11|vigent2 | 2026-01-29 11:25:37.674 | INFO | app.main:dispatch:22 - HEADERS: {'connection': 'upgrade', 'host': 'vigent.hbyrkj.top', 'x-real-ip': '27.17.161.128', 'x-forwarded-for': '27.17.161.128', 'x-forwarded-proto': 'https', 'sec-ch-ua-platform': '"Windows"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36', 'accept': 'application/json, text/plain, */*', 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"', 'sec-ch-ua-mobile': '?0', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://vigent.hbyrkj.top/', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9,zh;q=0.8,zh-CN;q=0.7', 'priority': 'u=1, i', 'cookie': 'rl_page_init_referrer=RudderEncrypt%3AU2FsdGVkX1%2FRWtsIwIaguDp15em58SDrIwOvRJVXeK4%3D; rl_page_init_referring_domain=RudderEncrypt%3AU2FsdGVkX1%2BVZN6tniQmiO5L2fGVdcrYOkqG%2BRHkNFw%3D; ph_phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo_posthog=%7B%22distinct_id%22%3A%22f6a1ba3602218bc1551bb81b48167bf7484eeb86ed8ee9484fa83f1267023264%230d2437ec-d81b-491c-991f-0b6559daa00d%22%2C%22%24sesid%22%3A%5B1762504332341%2C%22019a5d6c-ae21-7d75-8919-11e9621a135f%22%2C1762503994906%5D%2C%22%24epp%22%3Atrue%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fn8n.hbyrkj.top%2Fsignin%3Fredirect%3D%25252F%22%7D%7D; rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX19aTpWYcHFt3zhITFSLk1XAMr9V2jBWQwsuLDNLXh93pTlQ%2FUpvwmv6h%2Fl1bW4xH83hrkWPCTkSYg%3D%3D; rl_group_id=RudderEncrypt%3AU2FsdGVkX1%2BhywVlN3t3ypqwAMgqlh7ZRNLMKnFMhxA%3D; rl_group_trait=RudderEncrypt%3AU2FsdGVkX19jAKNOmR%2FnSngsWGVcmYB2qyvsbh3wQc0%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX180hU8QtHwPe3dPd1o7rEP7efRzFgCIvuIPRwbE3dWE0aEQCCMpQTN%2B7AGEtH6mjRvEuqcbfOdaX4TtJGL2jHbdcZUuA7Mpjf0uvsZ15LToi0zM1NWR7i6wE2z4vcYyFaBdB1uTJq3SxhX2WsqWe4YiT12vld0E%2F5w%3D; rl_trait=RudderEncrypt%3AU2FsdGVkX19tgs8QN46oDejOzAvFyTx%2FIVRu7LAGDzh2eg%2FAhV8eY%2FyjW12D%2BtSOVq6NLF2lSZcY40rlQ%2B1fUc3DAe2euuWhIECOtlxtY5Hho11ZdHGB8lZ4CSLo%2BWmSIjzmkQ33RgkeNF9eYV4AV1PpdZZ%2Fjyl%2BVjCQtaNVV5c%3D; rl_session=RudderEncrypt%3AU2FsdGVkX19jm82pV3xfWHI%2FE6QaUo5xFQZuXuYh%2FkUCBhyJGY7TqzAK3YDkYppIpUipS7LtUSxm6iWAAp3vGhbB58MN7hrVa8imlwsuL7ceFNN%2BR1uTEvKTR8wWKaii2Xzs%2FYnhG3X8kmImIfYZgg%3D%3D; access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5NGNkOTFlMy03ZDg5LTQ1ZTgtOWQ4NS1lOGJhMDY2MGQ3NGMiLCJzZXNzaW9uX3Rva2VuIjoiMjFjMmEwMmItYjY5Ny00MGVjLWIwMmItMjI1YzJjOWUyZGMzIiwiZXhwIjoxNzcwMjYwODExfQ.MpOjnbwllAzarfaoTk1SzYVMqAEXBEMyRt5UyiJ90Qw'}
+11|vigent2 | 2026-01-29 11:25:37.781 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/materials?t=1769657141569 - Status: 200 - Duration: 0.13s
+11|vigent2 | 2026-01-29 11:25:37.792 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/videos/generated - Status: 200 - Duration: 0.12s
+11|vigent2 | 2026-01-29 11:25:38.087 | INFO | app.main:dispatch:26 - END Request: GET https://vigent.hbyrkj.top/api/ref-audios - Status: 200 - Duration: 0.48s
+
+
+
+
+
+rongye@r730-ubuntu:~$ pm2 logs vigent2-latentsync
+[TAILING] Tailing last 15 lines for [vigent2-latentsync] process (change the value with --lines option)
+/home/rongye/.pm2/logs/vigent2-latentsync-out.log last 15 lines:
+/home/rongye/.pm2/logs/vigent2-latentsync-error.log last 15 lines:
\ No newline at end of file
diff --git a/Docs/QWEN3_TTS_DEPLOY.md b/Docs/QWEN3_TTS_DEPLOY.md
index b37e970..14bda2e 100644
--- a/Docs/QWEN3_TTS_DEPLOY.md
+++ b/Docs/QWEN3_TTS_DEPLOY.md
@@ -169,24 +169,106 @@ python test_inference.py
---
+## 步骤 6: 安装 HTTP 服务依赖
+
+```bash
+conda activate qwen-tts
+pip install fastapi uvicorn python-multipart
+```
+
+---
+
+## 步骤 7: 启动服务 (PM2 管理)
+
+### 手动测试
+
+```bash
+conda activate qwen-tts
+cd /home/rongye/ProgramFiles/ViGent2/models/Qwen3-TTS
+python qwen_tts_server.py
+```
+
+访问 http://localhost:8009/health 验证服务状态。
+
+### PM2 常驻服务
+
+> ⚠️ **注意**:启动脚本 `run_qwen_tts.sh` 位于项目**根目录**,而非 models/Qwen3-TTS 目录。
+
+1. 使用启动脚本:
+```bash
+cd /home/rongye/ProgramFiles/ViGent2
+pm2 start ./run_qwen_tts.sh --name vigent2-qwen-tts
+pm2 save
+```
+
+2. 查看日志:
+```bash
+pm2 logs vigent2-qwen-tts
+```
+
+3. 重启服务:
+```bash
+pm2 restart vigent2-qwen-tts
+```
+
+---
+
## 目录结构
部署完成后,目录结构应如下:
```
-/home/rongye/ProgramFiles/ViGent2/models/Qwen3-TTS/
-├── checkpoints/
-│ ├── Tokenizer/ # 语音编解码器
-│ └── 0.6B-Base/ # 声音克隆模型
-├── qwen_tts/ # 源码
-│ ├── inference/
-│ ├── models/
-│ └── ...
-├── examples/
-│ └── myvoice.wav # 参考音频
-├── pyproject.toml
-├── requirements.txt
-└── test_inference.py # 测试脚本
+/home/rongye/ProgramFiles/ViGent2/
+├── run_qwen_tts.sh # PM2 启动脚本 (根目录)
+└── models/Qwen3-TTS/
+ ├── checkpoints/
+ │ ├── Tokenizer/ # 语音编解码器
+ │ └── 0.6B-Base/ # 声音克隆模型
+ ├── qwen_tts/ # 源码
+ │ ├── inference/
+ │ ├── models/
+ │ └── ...
+ ├── examples/
+ │ └── myvoice.wav # 参考音频
+ ├── qwen_tts_server.py # HTTP 推理服务 (端口 8009)
+ ├── pyproject.toml
+ ├── requirements.txt
+ └── test_inference.py # 测试脚本
+```
+
+---
+
+## API 参考
+
+### 健康检查
+
+```
+GET http://localhost:8009/health
+```
+
+响应:
+```json
+{
+ "service": "Qwen3-TTS Voice Clone",
+ "model": "0.6B-Base",
+ "ready": true,
+ "gpu_id": 0
+}
+```
+
+### 声音克隆生成
+
+```
+POST http://localhost:8009/generate
+Content-Type: multipart/form-data
+
+Fields:
+ - ref_audio: 参考音频文件 (WAV)
+ - text: 要合成的文本
+ - ref_text: 参考音频的转写文字
+ - language: 语言 (默认 Chinese)
+
+Response: audio/wav 文件
```
---
@@ -244,6 +326,46 @@ Qwen3-TTS 0.6B 通常只需要 4-6GB VRAM。如果遇到 OOM:
---
+## 后端 ViGent2 集成
+
+### 声音克隆服务 (`voice_clone_service.py`)
+
+后端通过 HTTP 调用 Qwen3-TTS 服务:
+
+```python
+import aiohttp
+
+QWEN_TTS_URL = "http://localhost:8009"
+
+async def generate_cloned_audio(ref_audio_path: str, text: str, output_path: str):
+ async with aiohttp.ClientSession() as session:
+ with open(ref_audio_path, "rb") as f:
+ data = aiohttp.FormData()
+ data.add_field("ref_audio", f, filename="ref.wav")
+ data.add_field("text", text)
+
+ async with session.post(f"{QWEN_TTS_URL}/generate", data=data) as resp:
+ audio_data = await resp.read()
+ with open(output_path, "wb") as out:
+ out.write(audio_data)
+ return output_path
+```
+
+### 参考音频 Supabase Bucket
+
+```sql
+-- 创建 ref-audios bucket
+INSERT INTO storage.buckets (id, name, public)
+VALUES ('ref-audios', 'ref-audios', true)
+ON CONFLICT (id) DO NOTHING;
+
+-- RLS 策略
+CREATE POLICY "Allow public uploads" ON storage.objects
+FOR INSERT TO anon WITH CHECK (bucket_id = 'ref-audios');
+```
+
+---
+
## 参考链接
- [Qwen3-TTS GitHub](https://github.com/QwenLM/Qwen3-TTS)
@@ -251,3 +373,4 @@ Qwen3-TTS 0.6B 通常只需要 4-6GB VRAM。如果遇到 OOM:
- [HuggingFace 模型](https://huggingface.co/collections/Qwen/qwen3-tts)
- [技术报告](https://arxiv.org/abs/2601.15621)
- [官方博客](https://qwen.ai/blog?id=qwen3tts-0115)
+
diff --git a/Docs/implementation_plan.md b/Docs/implementation_plan.md
index 283d0a7..de16596 100644
--- a/Docs/implementation_plan.md
+++ b/Docs/implementation_plan.md
@@ -47,7 +47,7 @@
| **任务队列** | Celery + Redis | RQ / Dramatiq |
| **唇形同步** | **LatentSync 1.6** | MuseTalk / Wav2Lip |
| **TTS 配音** | EdgeTTS | CosyVoice |
-| **声音克隆** | GPT-SoVITS (可选) | - |
+| **声音克隆** | **Qwen3-TTS 0.6B** ✅ | GPT-SoVITS |
| **视频处理** | FFmpeg | MoviePy |
| **自动发布** | social-auto-upload | 自行实现 |
| **数据库** | SQLite → PostgreSQL | MySQL |
@@ -323,6 +323,17 @@ cp -r SuperIPAgent/social-auto-upload backend/social_upload
- [x] 端口冲突解决 (3003/8008/8444)
- [x] Basic Auth 管理后台保护
+### 阶段十七:声音克隆功能集成 (Day 13) ✅
+
+> **目标**:实现用户自定义声音克隆能力
+
+- [x] Qwen3-TTS HTTP 服务 (独立 FastAPI,端口 8009)
+- [x] 声音克隆服务封装 (voice_clone_service.py)
+- [x] 参考音频管理 API (上传/列表/删除)
+- [x] 前端 TTS 模式选择 UI
+- [x] Supabase ref-audios Bucket 配置
+- [x] 端到端测试验证
+
---
## 项目目录结构 (最终)
diff --git a/Docs/serene-conjuring-volcano.md b/Docs/serene-conjuring-volcano.md
new file mode 100644
index 0000000..35d3b59
--- /dev/null
+++ b/Docs/serene-conjuring-volcano.md
@@ -0,0 +1,220 @@
+# Qwen3-TTS 声音克隆集成到 ViGent2
+
+## 需求概述
+1. 前端支持上传/在线录制参考音频(wav, mp3, m4a 等)
+2. EdgeTTS 音色保留,增加 Qwen3-TTS 声音克隆界面
+3. 两种 TTS 方式做成统一界面(Tab 切换)
+4. 声音克隆使用相同的口播文案输入
+
+## 架构设计
+
+### GPU 分配
+| GPU | 服务 | 模型 |
+|-----|------|------|
+| GPU0 | Qwen3-TTS | 0.6B-Base (声音克隆) |
+| GPU1 | LatentSync | 1.6 (唇形同步) |
+
+### 存储
+- 新增 Supabase bucket: `ref_audios`
+- 路径格式: `{user_id}/{timestamp}_{filename}.wav`
+
+---
+
+## 实现步骤
+
+### 1. 后端:新建声音克隆服务
+**文件**: `backend/app/services/voice_clone_service.py`
+
+```python
+class VoiceCloneService:
+ def __init__(self):
+ self.gpu_id = 0
+ self.model_path = "models/Qwen3-TTS/checkpoints/0.6B-Base"
+ self._model = None
+ self._lock = asyncio.Lock()
+
+ async def generate_audio(self, text, ref_audio_path, ref_text, output_path, language="Chinese"):
+ # 使用 Qwen3TTSModel.generate_voice_clone()
+```
+
+### 2. 后端:新建参考音频 API
+**文件**: `backend/app/api/ref_audios.py`
+
+| 接口 | 方法 | 功能 |
+|------|------|------|
+| `/api/ref-audios` | POST | 上传参考音频 + ref_text |
+| `/api/ref-audios` | GET | 列出用户的参考音频 |
+| `/api/ref-audios/{id}` | DELETE | 删除参考音频 |
+
+上传时自动转换为 wav (16kHz mono),存储 ref_text 元数据。
+
+### 3. 后端:修改视频生成 API
+**文件**: `backend/app/api/videos.py`
+
+扩展 GenerateRequest:
+```python
+class GenerateRequest(BaseModel):
+ text: str
+ voice: str = "zh-CN-YunxiNeural"
+ material_path: str
+ # 新增
+ tts_mode: str = "edgetts" # "edgetts" | "voiceclone"
+ ref_audio_id: Optional[str] = None
+ ref_text: Optional[str] = None
+```
+
+修改 `_process_video_generation()`:
+```python
+if req.tts_mode == "voiceclone":
+ await voice_clone_service.generate_audio(...)
+else:
+ await tts_service.generate_audio(...)
+```
+
+### 4. 后端:注册路由
+**文件**: `backend/app/main.py`
+
+```python
+from app.api import ref_audios
+app.include_router(ref_audios.router, prefix="/api/ref-audios", tags=["ref-audios"])
+```
+
+### 5. 前端:改造音色选择区域
+**文件**: `frontend/src/app/page.tsx`
+
+**新增状态**:
+```typescript
+const [ttsMode, setTtsMode] = useState<'edgetts' | 'voiceclone'>('edgetts');
+const [refAudios, setRefAudios] = useState([]);
+const [selectedRefAudio, setSelectedRefAudio] = useState(null);
+const [refText, setRefText] = useState('');
+
+// 在线录音相关
+const [isRecording, setIsRecording] = useState(false);
+const [recordedBlob, setRecordedBlob] = useState(null);
+const [recordingTime, setRecordingTime] = useState(0);
+const mediaRecorderRef = useRef(null);
+```
+
+**UI 结构**:
+```
+┌─────────────────────────────────────┐
+│ 🎙️ 选择配音方式 │
+├─────────────────────────────────────┤
+│ [EdgeTTS 音色] [声音克隆] ← Tab │
+├─────────────────────────────────────┤
+│ Tab 1: 现有音色 2x3 网格 │
+│ │
+│ Tab 2: 声音克隆 │
+│ ┌───────────────────────────────┐ │
+│ │ 📁 我的参考音频 │ │
+│ │ [ref1] [ref2] [+上传] │ │
+│ └───────────────────────────────┘ │
+│ ┌───────────────────────────────┐ │
+│ │ 🎤 或在线录音 │ │
+│ │ [开始录音] [停止] 时长: 0:05 │ │
+│ │ (录音完成后显示试听和使用按钮) │ │
+│ └───────────────────────────────┘ │
+│ ┌───────────────────────────────┐ │
+│ │ 📝 参考音频文字 (必填) │ │
+│ │ [textarea] │ │
+│ └───────────────────────────────┘ │
+└─────────────────────────────────────┘
+```
+
+**在线录音逻辑**:
+```typescript
+const startRecording = async () => {
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+ const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
+ const chunks: BlobPart[] = [];
+
+ mediaRecorder.ondataavailable = (e) => chunks.push(e.data);
+ mediaRecorder.onstop = () => {
+ const blob = new Blob(chunks, { type: 'audio/webm' });
+ setRecordedBlob(blob);
+ stream.getTracks().forEach(track => track.stop());
+ };
+
+ mediaRecorder.start();
+ setIsRecording(true);
+ mediaRecorderRef.current = mediaRecorder;
+};
+
+const stopRecording = () => {
+ mediaRecorderRef.current?.stop();
+ setIsRecording(false);
+};
+
+const useRecording = async () => {
+ // 将录音 Blob 上传到后端
+ const formData = new FormData();
+ formData.append('file', recordedBlob, 'recording.webm');
+ formData.append('ref_text', refText);
+ const { data } = await api.post('/api/ref-audios', formData);
+ // 上传成功后刷新列表并选中
+ fetchRefAudios();
+ setSelectedRefAudio(data);
+};
+```
+
+### 6. 前端:修改生成请求
+```typescript
+const handleGenerate = async () => {
+ const payload = {
+ material_path: materialObj.path,
+ text: text,
+ tts_mode: ttsMode,
+ ...(ttsMode === 'edgetts'
+ ? { voice }
+ : { ref_audio_id: selectedRefAudio.id, ref_text: refText })
+ };
+ await api.post('/api/videos/generate', payload);
+};
+```
+
+---
+
+## 文件清单
+
+### 新建
+| 文件 | 描述 |
+|------|------|
+| `backend/app/services/voice_clone_service.py` | 声音克隆服务 |
+| `backend/app/api/ref_audios.py` | 参考音频管理 API |
+
+### 修改
+| 文件 | 修改内容 |
+|------|----------|
+| `backend/app/api/videos.py` | 扩展 GenerateRequest,修改 TTS 调用逻辑 |
+| `backend/app/main.py` | 注册 ref_audios 路由 |
+| `backend/app/services/storage.py` | 添加 BUCKET_REF_AUDIOS |
+| `frontend/src/app/page.tsx` | Tab 切换 UI、参考音频选择、refText 输入 |
+
+---
+
+## 验证方法
+
+1. **后端测试**:
+ ```bash
+ # 启动后端
+ cd backend && uvicorn app.main:app --port 8006
+
+ # 测试参考音频上传
+ curl -X POST http://localhost:8006/api/ref-audios \
+ -F "file=@test.wav" -F "ref_text=测试文字"
+
+ # 测试声音克隆生成
+ curl -X POST http://localhost:8006/api/videos/generate \
+ -H "Content-Type: application/json" \
+ -d '{"text":"测试文案","tts_mode":"voiceclone","ref_audio_id":"xxx","ref_text":"参考文字","material_path":"..."}'
+ ```
+
+2. **前端测试**:
+ - 打开首页,确认 Tab 切换正常
+ - 上传参考音频,确认列表显示
+ - 选择声音克隆模式,填写参考文字,点击生成
+ - 确认生成的视频使用克隆的声音
+
+3. **端到端测试**:
+ - 上传参考音频 → 选择声音克隆 → 输入口播文案 → 生成视频 → 播放验证声音
diff --git a/Docs/task_complete.md b/Docs/task_complete.md
index 9d6f4fb..7ca70ca 100644
--- a/Docs/task_complete.md
+++ b/Docs/task_complete.md
@@ -2,21 +2,21 @@
**项目**:ViGent2 数字人口播视频生成系统
**服务器**:Dell R730 (2× RTX 3090 24GB)
-**更新时间**:2026-01-28
-**整体进度**:100%(Day 12 iOS 兼容、移动端优化、Qwen3-TTS 部署)
+**更新时间**:2026-01-29
+**整体进度**:100%(Day 13 声音克隆功能集成完成)
## 📖 快速导航
| 章节 | 说明 |
|------|------|
-| [已完成任务](#-已完成任务) | Day 1-12 完成的功能 |
+| [已完成任务](#-已完成任务) | Day 1-13 完成的功能 |
| [后续规划](#️-后续规划) | 待办项目 |
| [进度统计](#-进度统计) | 各模块完成度 |
| [里程碑](#-里程碑) | 关键节点 |
| [时间线](#-时间线) | 开发历程 |
**相关文档**:
-- [Day 日志](file:///d:/CodingProjects/Antigravity/ViGent2/Docs/DevLogs/) (Day1-Day12)
+- [Day 日志](file:///d:/CodingProjects/Antigravity/ViGent2/Docs/DevLogs/) (Day1-Day13)
- [部署指南](file:///d:/CodingProjects/Antigravity/ViGent2/Docs/DEPLOY_MANUAL.md)
- [Qwen3-TTS 部署](file:///d:/CodingProjects/Antigravity/ViGent2/Docs/QWEN3_TTS_DEPLOY.md)
@@ -169,15 +169,23 @@
- [x] **发布页面 UI 重构** (立即发布/定时发布按钮分离,防误触设计)
- [x] **Qwen3-TTS 0.6B 部署** (声音克隆模型,GPU0,3秒参考音频快速克隆)
+### 阶段二十:声音克隆功能集成 (Day 13)
+- [x] **Qwen3-TTS HTTP 服务** (独立 FastAPI 服务,端口 8009)
+- [x] **声音克隆服务** (voice_clone_service.py,HTTP 调用封装)
+- [x] **参考音频管理 API** (上传/列表/删除)
+- [x] **前端 TTS 模式选择** (EdgeTTS / 声音克隆切换)
+- [x] **Supabase ref-audios Bucket** (参考音频存储桶 + RLS 策略)
+- [x] **端到端测试验证** (声音克隆完整流程测试通过)
+
---
## 🛤️ 后续规划
### 🔴 优先待办
-- [ ] **Qwen3-TTS 集成到 ViGent2** - 前端 UI + 后端服务集成
- [ ] 批量视频生成架构设计
### 🟠 功能完善
+- [x] Qwen3-TTS 集成到 ViGent2 ✅ Day 13 完成
- [x] 定时发布功能 ✅ Day 7 完成
- [ ] **后端定时发布** - 替代平台端定时,使用 APScheduler 实现任务调度
- [ ] 批量视频生成
@@ -358,3 +366,11 @@ Day 12: iOS 兼容与移动端优化 ✅ 完成
- **Qwen3-TTS 0.6B 部署** (声音克隆模型,GPU0)
- **部署文档** (QWEN3_TTS_DEPLOY.md)
+Day 13: 声音克隆功能集成 ✅ 完成
+ - Qwen3-TTS HTTP 服务 (独立 FastAPI,端口 8009)
+ - 声音克隆服务 (voice_clone_service.py)
+ - 参考音频管理 API (上传/列表/删除)
+ - 前端 TTS 模式选择 (EdgeTTS / 声音克隆)
+ - Supabase ref-audios Bucket 配置
+ - 端到端测试验证通过
+
diff --git a/README.md b/README.md
index 0338e33..acd7961 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@
- 🎬 **唇形同步** - LatentSync 1.6 驱动,512×512 高分辨率 Diffusion 模型
- 🎙️ **TTS 配音** - EdgeTTS 多音色支持(云溪、晓晓等)
+- 🔊 **声音克隆** - Qwen3-TTS 0.6B,3秒参考音频快速克隆 🆕
- 📱 **全自动发布** - 扫码登录 + Cookie持久化,支持多平台(B站/抖音/小红书)定时发布
- 🖥️ **Web UI** - Next.js 现代化界面,iOS/Android 移动端适配
- 🔐 **用户系统** - Supabase + JWT 认证,支持管理员后台、注册/登录
@@ -27,6 +28,7 @@
| 认证 | **JWT** + HttpOnly Cookie |
| 唇形同步 | **LatentSync 1.6** (Latent Diffusion, 512×512) |
| TTS | EdgeTTS |
+| 声音克隆 | **Qwen3-TTS 0.6B** |
| 视频处理 | FFmpeg |
| 自动发布 | Playwright |
@@ -141,7 +143,8 @@ nohup python -m scripts.server > server.log 2>&1 &
| **API 服务** | `http://<服务器IP>:8006` | 后端 Swagger |
| **认证管理 (Studio)** | `https://supabase.hbyrkj.top` | 需要 Basic Auth |
| **认证 API (Kong)** | `https://api.hbyrkj.top` | Supabase 接口 |
-| **模型服务** | `http://<服务器IP>:8007` | LatentSync |
+| **唇形同步服务** | `http://<服务器IP>:8007` | LatentSync |
+| **声音克隆服务** | `http://<服务器IP>:8009` | Qwen3-TTS |
---
diff --git a/backend/app/api/ref_audios.py b/backend/app/api/ref_audios.py
new file mode 100644
index 0000000..f36758a
--- /dev/null
+++ b/backend/app/api/ref_audios.py
@@ -0,0 +1,276 @@
+"""
+参考音频管理 API
+支持上传/列表/删除参考音频,用于 Qwen3-TTS 声音克隆
+"""
+from fastapi import APIRouter, UploadFile, File, Form, HTTPException, Depends
+from pydantic import BaseModel
+from typing import List, Optional
+from pathlib import Path
+from loguru import logger
+import time
+import json
+import subprocess
+import tempfile
+import os
+import re
+
+from app.core.deps import get_current_user
+from app.services.storage import storage_service
+
+router = APIRouter()
+
+# 支持的音频格式
+ALLOWED_AUDIO_EXTENSIONS = {'.wav', '.mp3', '.m4a', '.webm', '.ogg', '.flac', '.aac'}
+
+# 参考音频 bucket
+BUCKET_REF_AUDIOS = "ref-audios"
+
+
+class RefAudioResponse(BaseModel):
+ id: str
+ name: str
+ path: str # signed URL for playback
+ ref_text: str
+ duration_sec: float
+ created_at: int
+
+
+class RefAudioListResponse(BaseModel):
+ items: List[RefAudioResponse]
+
+
+def sanitize_filename(filename: str) -> str:
+ """清理文件名,移除特殊字符"""
+ safe_name = re.sub(r'[<>:"/\\|?*\s]', '_', filename)
+ if len(safe_name) > 50:
+ ext = Path(safe_name).suffix
+ safe_name = safe_name[:50 - len(ext)] + ext
+ return safe_name
+
+
+def get_audio_duration(file_path: str) -> float:
+ """获取音频时长 (秒)"""
+ try:
+ result = subprocess.run(
+ ['ffprobe', '-v', 'quiet', '-show_entries', 'format=duration',
+ '-of', 'csv=p=0', file_path],
+ capture_output=True, text=True, timeout=10
+ )
+ return float(result.stdout.strip())
+ except Exception as e:
+ logger.warning(f"获取音频时长失败: {e}")
+ return 0.0
+
+
+def convert_to_wav(input_path: str, output_path: str) -> bool:
+ """将音频转换为 WAV 格式 (16kHz, mono)"""
+ try:
+ subprocess.run([
+ 'ffmpeg', '-y', '-i', input_path,
+ '-ar', '16000', # 16kHz 采样率
+ '-ac', '1', # 单声道
+ '-acodec', 'pcm_s16le', # 16-bit PCM
+ output_path
+ ], capture_output=True, timeout=60, check=True)
+ return True
+ except Exception as e:
+ logger.error(f"音频转换失败: {e}")
+ return False
+
+
+@router.post("", response_model=RefAudioResponse)
+async def upload_ref_audio(
+ file: UploadFile = File(...),
+ ref_text: str = Form(...),
+ user: dict = Depends(get_current_user)
+):
+ """
+ 上传参考音频
+
+ - file: 音频文件 (支持 wav, mp3, m4a, webm 等)
+ - ref_text: 参考音频的转写文字 (必填)
+ """
+ user_id = user["id"]
+
+ # 验证文件扩展名
+ ext = Path(file.filename).suffix.lower()
+ if ext not in ALLOWED_AUDIO_EXTENSIONS:
+ raise HTTPException(
+ status_code=400,
+ detail=f"不支持的音频格式: {ext}。支持的格式: {', '.join(ALLOWED_AUDIO_EXTENSIONS)}"
+ )
+
+ # 验证 ref_text
+ if not ref_text or len(ref_text.strip()) < 2:
+ raise HTTPException(status_code=400, detail="参考文字不能为空")
+
+ try:
+ # 创建临时文件
+ with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as tmp_input:
+ content = await file.read()
+ tmp_input.write(content)
+ tmp_input_path = tmp_input.name
+
+ # 转换为 WAV 格式
+ tmp_wav_path = tmp_input_path + ".wav"
+ if ext != '.wav':
+ if not convert_to_wav(tmp_input_path, tmp_wav_path):
+ raise HTTPException(status_code=500, detail="音频格式转换失败")
+ else:
+ # 即使是 wav 也要标准化格式
+ convert_to_wav(tmp_input_path, tmp_wav_path)
+
+ # 获取音频时长
+ duration = get_audio_duration(tmp_wav_path)
+ if duration < 1.0:
+ raise HTTPException(status_code=400, detail="音频时长过短,至少需要 1 秒")
+ if duration > 60.0:
+ raise HTTPException(status_code=400, detail="音频时长过长,最多 60 秒")
+
+ # 生成存储路径
+ timestamp = int(time.time())
+ safe_name = sanitize_filename(Path(file.filename).stem)
+ storage_path = f"{user_id}/{timestamp}_{safe_name}.wav"
+
+ # 上传 WAV 文件到 Supabase
+ with open(tmp_wav_path, 'rb') as f:
+ wav_data = f.read()
+
+ await storage_service.upload_file(
+ bucket=BUCKET_REF_AUDIOS,
+ path=storage_path,
+ file_data=wav_data,
+ content_type="audio/wav"
+ )
+
+ # 上传元数据 JSON
+ metadata = {
+ "ref_text": ref_text.strip(),
+ "original_filename": file.filename,
+ "duration_sec": duration,
+ "created_at": timestamp
+ }
+ metadata_path = f"{user_id}/{timestamp}_{safe_name}.json"
+ await storage_service.upload_file(
+ bucket=BUCKET_REF_AUDIOS,
+ path=metadata_path,
+ file_data=json.dumps(metadata, ensure_ascii=False).encode('utf-8'),
+ content_type="application/json"
+ )
+
+ # 获取签名 URL
+ signed_url = await storage_service.get_signed_url(BUCKET_REF_AUDIOS, storage_path)
+
+ # 清理临时文件
+ os.unlink(tmp_input_path)
+ if os.path.exists(tmp_wav_path):
+ os.unlink(tmp_wav_path)
+
+ return RefAudioResponse(
+ id=storage_path,
+ name=file.filename,
+ path=signed_url,
+ ref_text=ref_text.strip(),
+ duration_sec=duration,
+ created_at=timestamp
+ )
+
+ except HTTPException:
+ raise
+ except Exception as e:
+ logger.error(f"上传参考音频失败: {e}")
+ raise HTTPException(status_code=500, detail=f"上传失败: {str(e)}")
+
+
+@router.get("", response_model=RefAudioListResponse)
+async def list_ref_audios(user: dict = Depends(get_current_user)):
+ """列出当前用户的所有参考音频"""
+ user_id = user["id"]
+
+ try:
+ # 列出用户目录下的文件
+ files = await storage_service.list_files(BUCKET_REF_AUDIOS, user_id)
+
+ # 过滤出 .wav 文件并获取对应的 metadata
+ items = []
+ for f in files:
+ name = f.get("name", "")
+ if not name.endswith(".wav"):
+ continue
+
+ storage_path = f"{user_id}/{name}"
+
+ # 尝试读取 metadata
+ metadata_name = name.replace(".wav", ".json")
+ metadata_path = f"{user_id}/{metadata_name}"
+
+ ref_text = ""
+ duration_sec = 0.0
+ created_at = 0
+
+ try:
+ # 获取 metadata 内容
+ metadata_url = await storage_service.get_signed_url(BUCKET_REF_AUDIOS, metadata_path)
+ import httpx
+ async with httpx.AsyncClient() as client:
+ resp = await client.get(metadata_url)
+ if resp.status_code == 200:
+ metadata = resp.json()
+ ref_text = metadata.get("ref_text", "")
+ duration_sec = metadata.get("duration_sec", 0.0)
+ created_at = metadata.get("created_at", 0)
+ except Exception as e:
+ logger.warning(f"读取 metadata 失败: {e}")
+ # 从文件名提取时间戳
+ try:
+ created_at = int(name.split("_")[0])
+ except:
+ pass
+
+ # 获取音频签名 URL
+ signed_url = await storage_service.get_signed_url(BUCKET_REF_AUDIOS, storage_path)
+
+ items.append(RefAudioResponse(
+ id=storage_path,
+ name=name,
+ path=signed_url,
+ ref_text=ref_text,
+ duration_sec=duration_sec,
+ created_at=created_at
+ ))
+
+ # 按创建时间倒序排列
+ items.sort(key=lambda x: x.created_at, reverse=True)
+
+ return RefAudioListResponse(items=items)
+
+ except Exception as e:
+ logger.error(f"列出参考音频失败: {e}")
+ raise HTTPException(status_code=500, detail=f"获取列表失败: {str(e)}")
+
+
+@router.delete("/{audio_id:path}")
+async def delete_ref_audio(audio_id: str, user: dict = Depends(get_current_user)):
+ """删除参考音频"""
+ user_id = user["id"]
+
+ # 安全检查:确保只能删除自己的文件
+ if not audio_id.startswith(f"{user_id}/"):
+ raise HTTPException(status_code=403, detail="无权删除此文件")
+
+ try:
+ # 删除 WAV 文件
+ await storage_service.delete_file(BUCKET_REF_AUDIOS, audio_id)
+
+ # 删除 metadata JSON
+ metadata_path = audio_id.replace(".wav", ".json")
+ try:
+ await storage_service.delete_file(BUCKET_REF_AUDIOS, metadata_path)
+ except:
+ pass # metadata 可能不存在
+
+ return {"success": True, "message": "删除成功"}
+
+ except Exception as e:
+ logger.error(f"删除参考音频失败: {e}")
+ raise HTTPException(status_code=500, detail=f"删除失败: {str(e)}")
diff --git a/backend/app/api/videos.py b/backend/app/api/videos.py
index da936b8..03ea7e7 100644
--- a/backend/app/api/videos.py
+++ b/backend/app/api/videos.py
@@ -11,6 +11,7 @@ import os
from app.services.tts_service import TTSService
from app.services.video_service import VideoService
from app.services.lipsync_service import LipSyncService
+from app.services.voice_clone_service import voice_clone_service
from app.services.storage import storage_service
from app.core.config import settings
from app.core.deps import get_current_user
@@ -21,6 +22,10 @@ class GenerateRequest(BaseModel):
text: str
voice: str = "zh-CN-YunxiNeural"
material_path: str
+ # 声音克隆模式新增字段
+ tts_mode: str = "edgetts" # "edgetts" | "voiceclone"
+ ref_audio_id: Optional[str] = None # 参考音频 storage path
+ ref_text: Optional[str] = None # 参考音频的转写文字
tasks = {} # In-memory task store
@@ -95,13 +100,42 @@ async def _process_video_generation(task_id: str, req: GenerateRequest, user_id:
await _download_material(req.material_path, input_material_path)
# 1. TTS - 进度 5% -> 25%
- tasks[task_id]["message"] = "正在生成语音 (TTS)..."
+ tasks[task_id]["message"] = "正在生成语音..."
tasks[task_id]["progress"] = 10
- tts = TTSService()
- audio_path = temp_dir / f"{task_id}_audio.mp3"
+ audio_path = temp_dir / f"{task_id}_audio.wav"
temp_files.append(audio_path)
- await tts.generate_audio(req.text, req.voice, str(audio_path))
+
+ if req.tts_mode == "voiceclone":
+ # 声音克隆模式
+ if not req.ref_audio_id or not req.ref_text:
+ raise ValueError("声音克隆模式需要提供参考音频和参考文字")
+
+ tasks[task_id]["message"] = "正在下载参考音频..."
+
+ # 从 Supabase 下载参考音频
+ ref_audio_local = temp_dir / f"{task_id}_ref.wav"
+ temp_files.append(ref_audio_local)
+
+ ref_audio_url = await storage_service.get_signed_url(
+ bucket="ref-audios",
+ path=req.ref_audio_id
+ )
+ await _download_material(ref_audio_url, ref_audio_local)
+
+ tasks[task_id]["message"] = "正在克隆声音 (Qwen3-TTS)..."
+ await voice_clone_service.generate_audio(
+ text=req.text,
+ ref_audio_path=str(ref_audio_local),
+ ref_text=req.ref_text,
+ output_path=str(audio_path),
+ language="Chinese"
+ )
+ else:
+ # EdgeTTS 模式 (默认)
+ tasks[task_id]["message"] = "正在生成语音 (EdgeTTS)..."
+ tts = TTSService()
+ await tts.generate_audio(req.text, req.voice, str(audio_path))
tts_time = time.time() - start_time
print(f"[Pipeline] TTS completed in {tts_time:.1f}s")
@@ -217,6 +251,12 @@ async def lipsync_health():
return await lipsync.check_health()
+@router.get("/voiceclone/health")
+async def voiceclone_health():
+ """获取声音克隆服务健康状态"""
+ return await voice_clone_service.check_health()
+
+
@router.get("/generated")
async def list_generated_videos(current_user: dict = Depends(get_current_user)):
"""从 Storage 读取当前用户生成的视频列表"""
diff --git a/backend/app/main.py b/backend/app/main.py
index 88752e2..93c2af0 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -2,7 +2,7 @@ from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from app.core import config
-from app.api import materials, videos, publish, login_helper, auth, admin
+from app.api import materials, videos, publish, login_helper, auth, admin, ref_audios
from loguru import logger
import os
@@ -55,6 +55,7 @@ app.include_router(publish.router, prefix="/api/publish", tags=["Publish"])
app.include_router(login_helper.router, prefix="/api", tags=["LoginHelper"])
app.include_router(auth.router) # /api/auth
app.include_router(admin.router) # /api/admin
+app.include_router(ref_audios.router, prefix="/api/ref-audios", tags=["RefAudios"])
@app.on_event("startup")
diff --git a/backend/app/services/storage.py b/backend/app/services/storage.py
index 462b89c..519754b 100644
--- a/backend/app/services/storage.py
+++ b/backend/app/services/storage.py
@@ -16,6 +16,26 @@ class StorageService:
self.supabase: Client = get_supabase()
self.BUCKET_MATERIALS = "materials"
self.BUCKET_OUTPUTS = "outputs"
+ self.BUCKET_REF_AUDIOS = "ref-audios"
+ # 确保所有 bucket 存在
+ self._ensure_buckets()
+
+ def _ensure_buckets(self):
+ """确保所有必需的 bucket 存在"""
+ buckets = [self.BUCKET_MATERIALS, self.BUCKET_OUTPUTS, self.BUCKET_REF_AUDIOS]
+ try:
+ existing = self.supabase.storage.list_buckets()
+ existing_names = {b.name for b in existing} if existing else set()
+ for bucket_name in buckets:
+ if bucket_name not in existing_names:
+ try:
+ self.supabase.storage.create_bucket(bucket_name, options={"public": True})
+ logger.info(f"Created bucket: {bucket_name}")
+ except Exception as e:
+ # 可能已存在,忽略错误
+ logger.debug(f"Bucket {bucket_name} creation skipped: {e}")
+ except Exception as e:
+ logger.warning(f"Failed to ensure buckets: {e}")
def _convert_to_public_url(self, url: str) -> str:
"""将内部 URL 转换为公网可访问的 URL"""
diff --git a/backend/app/services/voice_clone_service.py b/backend/app/services/voice_clone_service.py
new file mode 100644
index 0000000..871850c
--- /dev/null
+++ b/backend/app/services/voice_clone_service.py
@@ -0,0 +1,110 @@
+"""
+声音克隆服务
+通过 HTTP 调用 Qwen3-TTS 独立服务 (端口 8009)
+"""
+import httpx
+from pathlib import Path
+from typing import Optional
+from loguru import logger
+
+from app.core.config import settings
+
+# Qwen3-TTS 服务地址
+QWEN_TTS_URL = "http://localhost:8009"
+
+
+class VoiceCloneService:
+ """声音克隆服务 - 调用 Qwen3-TTS HTTP API"""
+
+ def __init__(self):
+ self.base_url = QWEN_TTS_URL
+ # 健康状态缓存
+ self._health_cache: Optional[dict] = None
+ self._health_cache_time: float = 0
+
+ async def generate_audio(
+ self,
+ text: str,
+ ref_audio_path: str,
+ ref_text: str,
+ output_path: str,
+ language: str = "Chinese"
+ ) -> str:
+ """
+ 使用声音克隆生成语音
+
+ Args:
+ text: 要合成的文本
+ ref_audio_path: 参考音频本地路径
+ ref_text: 参考音频的转写文字
+ output_path: 输出 wav 路径
+ language: 语言 (Chinese/English/Auto)
+
+ Returns:
+ 输出文件路径
+ """
+ logger.info(f"🎤 Voice Clone: {text[:30]}...")
+ Path(output_path).parent.mkdir(parents=True, exist_ok=True)
+
+ # 读取参考音频
+ with open(ref_audio_path, "rb") as f:
+ ref_audio_data = f.read()
+
+ # 调用 Qwen3-TTS 服务
+ timeout = httpx.Timeout(300.0) # 5分钟超时
+ async with httpx.AsyncClient(timeout=timeout) as client:
+ try:
+ response = await client.post(
+ f"{self.base_url}/generate",
+ files={"ref_audio": ("ref.wav", ref_audio_data, "audio/wav")},
+ data={
+ "text": text,
+ "ref_text": ref_text,
+ "language": language
+ }
+ )
+ response.raise_for_status()
+
+ # 保存返回的音频
+ with open(output_path, "wb") as f:
+ f.write(response.content)
+
+ logger.info(f"✅ Voice clone saved: {output_path}")
+ return output_path
+
+ except httpx.HTTPStatusError as e:
+ logger.error(f"Qwen3-TTS API error: {e.response.status_code} - {e.response.text}")
+ raise RuntimeError(f"声音克隆服务错误: {e.response.text}")
+ except httpx.RequestError as e:
+ logger.error(f"Qwen3-TTS connection error: {e}")
+ raise RuntimeError("无法连接声音克隆服务,请检查服务是否启动")
+
+ async def check_health(self) -> dict:
+ """健康检查"""
+ import time
+
+ # 5分钟缓存
+ now = time.time()
+ if self._health_cache and (now - self._health_cache_time) < 300:
+ return self._health_cache
+
+ try:
+ async with httpx.AsyncClient(timeout=5.0) as client:
+ response = await client.get(f"{self.base_url}/health")
+ response.raise_for_status()
+ self._health_cache = response.json()
+ self._health_cache_time = now
+ return self._health_cache
+ except Exception as e:
+ logger.warning(f"Qwen3-TTS health check failed: {e}")
+ return {
+ "service": "Qwen3-TTS Voice Clone",
+ "model": "0.6B-Base",
+ "ready": False,
+ "gpu_id": 0,
+ "error": str(e)
+ }
+
+
+# 单例
+voice_clone_service = VoiceCloneService()
diff --git a/frontend/README.md b/frontend/README.md
index f697d3f..0097139 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -19,6 +19,11 @@ ViGent2 的前端界面,采用 Next.js 14 + TailwindCSS 构建。
- **发布配置**: 设置视频标题、标签、简介。
- **定时任务**: 支持 "立即发布" 或 "定时发布"。
+### 3. 声音克隆 [Day 13 新增]
+- **TTS 模式选择**: EdgeTTS (预设音色) / 声音克隆 (自定义音色) 切换。
+- **参考音频管理**: 上传/列表/删除参考音频 (3-20秒 WAV)。
+- **一键克隆**: 选择参考音频后自动调用 Qwen3-TTS 服务。
+
## 🛠️ 技术栈
- **框架**: Next.js 14 (App Router)
diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx
index e413e7f..9ecf4f8 100644
--- a/frontend/src/app/page.tsx
+++ b/frontend/src/app/page.tsx
@@ -1,7 +1,7 @@
"use client";
-import { useState, useEffect } from "react";
+import { useState, useEffect, useRef } from "react";
import Link from "next/link";
import api from "@/lib/axios";
@@ -34,6 +34,15 @@ interface GeneratedVideo {
created_at: number;
}
+interface RefAudio {
+ id: string;
+ name: string;
+ path: string;
+ ref_text: string;
+ duration_sec: number;
+ created_at: number;
+}
+
// 格式化日期(避免 Hydration 错误)
const formatDate = (timestamp: number) => {
const d = new Date(timestamp * 1000);
@@ -65,6 +74,21 @@ export default function Home() {
const [selectedVideoId, setSelectedVideoId] = useState(null);
+ // 声音克隆相关状态
+ const [ttsMode, setTtsMode] = useState<'edgetts' | 'voiceclone'>('edgetts');
+ const [refAudios, setRefAudios] = useState([]);
+ const [selectedRefAudio, setSelectedRefAudio] = useState(null);
+ const [refText, setRefText] = useState('其实生活中有许多美好的瞬间,比如清晨的阳光,或者一杯温热的清茶。希望这次生成的音色能够自然、流畅,完美还原出我最真实的声音状态。');
+ const [isUploadingRef, setIsUploadingRef] = useState(false);
+ const [uploadRefError, setUploadRefError] = useState(null);
+
+ // 在线录音相关
+ const [isRecording, setIsRecording] = useState(false);
+ const [recordedBlob, setRecordedBlob] = useState(null);
+ const [recordingTime, setRecordingTime] = useState(0);
+ const mediaRecorderRef = useRef(null);
+ const recordingIntervalRef = useRef(null);
+
// 可选音色
const voices = [
{ id: "zh-CN-YunxiNeural", name: "云溪 (男声-年轻)" },
@@ -74,10 +98,14 @@ export default function Home() {
{ id: "zh-CN-XiaoyiNeural", name: "晓伊 (女声-温柔)" },
];
+ // 声音克隆固定参考文字(用户录音/上传时需要读这段话)
+ const FIXED_REF_TEXT = "其实生活中有许多美好的瞬间,比如清晨的阳光,或者一杯温热的清茶。希望这次生成的音色能够自然、流畅,完美还原出我最真实的声音状态。";
+
// 加载素材列表和历史视频
useEffect(() => {
fetchMaterials();
fetchGeneratedVideos();
+ fetchRefAudios();
}, []);
const fetchMaterials = async () => {
@@ -111,6 +139,115 @@ export default function Home() {
}
};
+ // 获取参考音频列表
+ const fetchRefAudios = async () => {
+ try {
+ const { data } = await api.get('/api/ref-audios');
+ setRefAudios(data.items || []);
+ } catch (error) {
+ console.error("获取参考音频失败:", error);
+ }
+ };
+
+ // 上传参考音频(使用固定参考文字)
+ const uploadRefAudio = async (file: File) => {
+ const refTextInput = FIXED_REF_TEXT;
+
+ setIsUploadingRef(true);
+ setUploadRefError(null);
+
+ try {
+ const formData = new FormData();
+ formData.append('file', file);
+ formData.append('ref_text', refTextInput);
+
+ const { data } = await api.post('/api/ref-audios', formData, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ });
+
+ await fetchRefAudios();
+ setSelectedRefAudio(data);
+ setRefText(data.ref_text);
+ setIsUploadingRef(false);
+ } catch (err: any) {
+ console.error("Upload ref audio failed:", err);
+ setIsUploadingRef(false);
+ const errorMsg = err.response?.data?.detail || err.message || String(err);
+ setUploadRefError(`上传失败: ${errorMsg}`);
+ }
+ };
+
+ // 删除参考音频
+ const deleteRefAudio = async (audioId: string) => {
+ if (!confirm("确定要删除这个参考音频吗?")) return;
+ try {
+ await api.delete(`/api/ref-audios/${encodeURIComponent(audioId)}`);
+ fetchRefAudios();
+ if (selectedRefAudio?.id === audioId) {
+ setSelectedRefAudio(null);
+ setRefText('');
+ }
+ } catch (error) {
+ alert("删除失败: " + error);
+ }
+ };
+
+ // 开始录音
+ const startRecording = async () => {
+ try {
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+ const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
+ const chunks: BlobPart[] = [];
+
+ mediaRecorder.ondataavailable = (e) => chunks.push(e.data);
+ mediaRecorder.onstop = () => {
+ const blob = new Blob(chunks, { type: 'audio/webm' });
+ setRecordedBlob(blob);
+ stream.getTracks().forEach(track => track.stop());
+ };
+
+ mediaRecorder.start();
+ setIsRecording(true);
+ setRecordingTime(0);
+ mediaRecorderRef.current = mediaRecorder;
+
+ // 计时器
+ recordingIntervalRef.current = setInterval(() => {
+ setRecordingTime(prev => prev + 1);
+ }, 1000);
+ } catch (err) {
+ alert('无法访问麦克风,请检查权限设置');
+ console.error(err);
+ }
+ };
+
+ // 停止录音
+ const stopRecording = () => {
+ mediaRecorderRef.current?.stop();
+ setIsRecording(false);
+ if (recordingIntervalRef.current) {
+ clearInterval(recordingIntervalRef.current);
+ recordingIntervalRef.current = null;
+ }
+ };
+
+ // 使用录音(上传到后端,使用固定参考文字)
+ const useRecording = async () => {
+ if (!recordedBlob) return;
+
+ const file = new File([recordedBlob], 'recording.webm', { type: 'audio/webm' });
+ await uploadRefAudio(file);
+ setRecordedBlob(null);
+ setRecordingTime(0);
+ };
+
+ // 格式化录音时长
+ const formatRecordingTime = (seconds: number) => {
+ const mins = Math.floor(seconds / 60);
+ const secs = seconds % 60;
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
+ };
+
// 删除素材
const deleteMaterial = async (materialId: string) => {
if (!confirm("确定要删除这个素材吗?")) return;
@@ -195,6 +332,14 @@ export default function Home() {
return;
}
+ // 声音克隆模式校验
+ if (ttsMode === 'voiceclone') {
+ if (!selectedRefAudio) {
+ alert("请选择或上传参考音频");
+ return;
+ }
+ }
+
setIsGenerating(true);
setGeneratedVideo(null);
@@ -206,13 +351,22 @@ export default function Home() {
return;
}
- // 创建生成任务
- const { data } = await api.post('/api/videos/generate', {
+ // 构建请求参数
+ const payload: Record = {
material_path: materialObj.path,
text: text,
- voice: voice,
- add_subtitle: true,
- });
+ tts_mode: ttsMode,
+ };
+
+ if (ttsMode === 'edgetts') {
+ payload.voice = voice;
+ } else {
+ payload.ref_audio_id = selectedRefAudio!.id;
+ payload.ref_text = refText;
+ }
+
+ // 创建生成任务
+ const { data } = await api.post('/api/videos/generate', payload);
const taskId = data.task_id;
@@ -433,32 +587,207 @@ export default function Home() {
- {/* 音色选择 */}
+ {/* 配音方式选择 */}
- 🎙️ 选择配音音色
+ 🎙️ 选择配音方式
-
- {voices.map((v) => (
-
- ))}
+
+ {/* Tab 切换 */}
+
+
+
+
+ {/* EdgeTTS 音色列表 */}
+ {ttsMode === 'edgetts' && (
+
+ {voices.map((v) => (
+
+ ))}
+
+ )}
+
+ {/* 声音克隆区域 */}
+ {ttsMode === 'voiceclone' && (
+
+ {/* 参考音频列表 */}
+
+
+
📁 我的参考音频
+
+ {
+ const file = e.target.files?.[0];
+ if (file) {
+ uploadRefAudio(file);
+ }
+ e.target.value = '';
+ }}
+ className="hidden"
+ />
+
+
+
+
+
+ {isUploadingRef && (
+
+ ⏳ 上传中...
+
+ )}
+
+ {uploadRefError && (
+
+ ❌ {uploadRefError}
+
+
+ )}
+
+ {refAudios.length === 0 ? (
+
+ 暂无参考音频,请上传或录制
+
+ ) : (
+
+ {refAudios.map((audio) => (
+
{
+ setSelectedRefAudio(audio);
+ setRefText(audio.ref_text);
+ }}
+ >
+
{audio.name}
+
{audio.duration_sec.toFixed(1)}s
+
+
+ ))}
+
+ )}
+
+
+ {/* 在线录音 */}
+
+
🎤 或在线录音
+
+ {!isRecording ? (
+
+ ) : (
+
+ )}
+ {isRecording && (
+
+ 🔴 录音中 {formatRecordingTime(recordingTime)}
+
+ )}
+
+
+ {recordedBlob && !isRecording && (
+
+
+
✅ 录音完成 ({formatRecordingTime(recordingTime)})
+
+
+
+
+ )}
+
+
+ {/* 参考音频文字(固定,用户需要朗读此段落) */}
+
+
+
+ {FIXED_REF_TEXT}
+
+
+ 请清晰朗读上述内容完成录音,系统将以此为参考克隆您的声音
+
+
+
+ )}
{/* 生成按钮 */}
-
-
+
+
);
}
diff --git a/models/Qwen3-TTS/qwen_tts_server.py b/models/Qwen3-TTS/qwen_tts_server.py
new file mode 100644
index 0000000..910d4cc
--- /dev/null
+++ b/models/Qwen3-TTS/qwen_tts_server.py
@@ -0,0 +1,189 @@
+"""
+Qwen3-TTS 独立推理服务
+端口: 8009
+GPU: 0
+
+启动方式:
+ conda activate qwen-tts
+ python qwen_tts_server.py
+
+PM2 启动:
+ pm2 start qwen_tts_server.py --name qwen-tts --interpreter /home/rongye/ProgramFiles/miniconda3/envs/qwen-tts/bin/python
+"""
+import os
+import sys
+import tempfile
+import time
+from pathlib import Path
+from typing import Optional
+
+# 设置 GPU
+os.environ["CUDA_VISIBLE_DEVICES"] = "0"
+
+from fastapi import FastAPI, HTTPException, UploadFile, File, Form
+from fastapi.responses import FileResponse
+from pydantic import BaseModel
+import uvicorn
+
+app = FastAPI(title="Qwen3-TTS Voice Clone Service", version="1.0")
+
+# 模型路径
+MODEL_PATH = Path(__file__).parent / "checkpoints" / "0.6B-Base"
+
+# 全局模型实例
+_model = None
+_model_loaded = False
+
+
+def load_model():
+ """加载模型(启动时调用)"""
+ global _model, _model_loaded
+
+ if _model_loaded:
+ return
+
+ print("🔄 Loading Qwen3-TTS model...")
+ start = time.time()
+
+ import torch
+ from qwen_tts import Qwen3TTSModel
+
+ _model = Qwen3TTSModel.from_pretrained(
+ str(MODEL_PATH),
+ device_map="cuda:0",
+ dtype=torch.bfloat16,
+ )
+
+ _model_loaded = True
+ print(f"✅ Qwen3-TTS model loaded in {time.time() - start:.1f}s")
+
+
+class GenerateRequest(BaseModel):
+ text: str
+ ref_text: str
+ language: str = "Chinese"
+
+
+class HealthResponse(BaseModel):
+ service: str
+ model: str
+ ready: bool
+ gpu_id: int
+
+
+@app.on_event("startup")
+async def startup():
+ """服务启动时预加载模型"""
+ try:
+ load_model()
+ except Exception as e:
+ print(f"❌ Model loading failed: {e}")
+
+
+@app.get("/health", response_model=HealthResponse)
+async def health():
+ """健康检查"""
+ gpu_ok = False
+ try:
+ import torch
+ gpu_ok = torch.cuda.is_available()
+ except:
+ pass
+
+ return HealthResponse(
+ service="Qwen3-TTS Voice Clone",
+ model="0.6B-Base",
+ ready=_model_loaded and gpu_ok,
+ gpu_id=0
+ )
+
+
+@app.post("/generate")
+async def generate(
+ ref_audio: UploadFile = File(...),
+ text: str = Form(...),
+ ref_text: str = Form(...),
+ language: str = Form("Chinese")
+):
+ """
+ 声音克隆生成
+
+ Args:
+ ref_audio: 参考音频文件 (WAV)
+ text: 要合成的文本
+ ref_text: 参考音频的转写文字
+ language: 语言 (Chinese/English/Auto)
+
+ Returns:
+ 生成的音频文件 (WAV)
+ """
+ if not _model_loaded:
+ raise HTTPException(status_code=503, detail="Model not loaded")
+
+ import soundfile as sf
+
+ # 保存上传的参考音频到临时文件
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_ref:
+ content = await ref_audio.read()
+ tmp_ref.write(content)
+ ref_audio_path = tmp_ref.name
+
+ # 生成输出路径
+ output_path = tempfile.mktemp(suffix=".wav")
+
+ try:
+ print(f"🎤 Generating: {text[:30]}...")
+ print(f"📝 Ref text: {ref_text[:50]}...")
+
+ start = time.time()
+
+ wavs, sr = _model.generate_voice_clone(
+ text=text,
+ language=language,
+ ref_audio=ref_audio_path,
+ ref_text=ref_text,
+ )
+
+ sf.write(output_path, wavs[0], sr)
+
+ duration = len(wavs[0]) / sr
+ print(f"✅ Generated in {time.time() - start:.1f}s, duration: {duration:.1f}s")
+
+ # 返回音频文件
+ return FileResponse(
+ output_path,
+ media_type="audio/wav",
+ filename="output.wav",
+ background=None # 让客户端下载完再删除
+ )
+
+ except Exception as e:
+ print(f"❌ Generation failed: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+ finally:
+ # 清理参考音频临时文件
+ try:
+ os.unlink(ref_audio_path)
+ except:
+ pass
+
+
+@app.on_event("shutdown")
+async def shutdown():
+ """清理临时文件"""
+ # 清理 /tmp 中的残留文件
+ import glob
+ for f in glob.glob("/tmp/tmp*.wav"):
+ try:
+ os.unlink(f)
+ except:
+ pass
+
+
+if __name__ == "__main__":
+ uvicorn.run(
+ app,
+ host="0.0.0.0",
+ port=8009,
+ log_level="info"
+ )
diff --git a/run_qwen_tts.sh b/run_qwen_tts.sh
new file mode 100644
index 0000000..278db32
--- /dev/null
+++ b/run_qwen_tts.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# Qwen3-TTS 声音克隆服务启动脚本
+# 端口: 8009
+# GPU: 0
+
+cd /home/rongye/ProgramFiles/ViGent2/models/Qwen3-TTS
+
+# 使用 qwen-tts conda 环境的 Python
+/home/rongye/ProgramFiles/miniconda3/envs/qwen-tts/bin/python qwen_tts_server.py