22 KiB
22 KiB
文档分层收敛 + 音色试听修复 + 录音弹窗重构 + 弹窗体系统一 (Day 31)
概述
今天的工作聚焦四件事:
- 清理并收敛根目录文档(README/DEV 职责边界、历史内容归档、参数描述与代码对齐)
- 完成 EdgeTTS 音色列表「一键试听」能力,并修复浏览器端试听失败问题
- 重构声音克隆录音交互:录音入口下沉到参考音频区域底部右侧,流程改为弹窗
- 抽离统一弹窗基座
AppModal,将主要弹窗迁移到同一视觉和交互规范
✅ 1) 文档体系与内容一致性优化
1.1 README / DEV 边界明确
- 为
FRONTEND_README.md、BACKEND_README.md、FRONTEND_DEV.md、BACKEND_DEV.md增加「文档定位」 - README 只保留稳定说明(功能、接口、运行),DEV 保留规范(约束、分层、Checklist)
- 将 README 中偏日志化内容(如 Day 标注)清理为稳定表述
1.2 部署与参数文档对齐当前代码
- 将唇形路由阈值文案统一为阈值驱动,并以当前
.env示例100为参考 - 修正旧编码描述(将 MuseTalk 合成描述对齐为 rawvideo 管道 +
libx264) - 修复文档中不存在的
.env.example指引,改为基于backend/.env的说明 - 将 Qwen3-TTS 文档标注为「历史归档(已停用)」并指向 CosyVoice 3.0
✅ 2) 音色试听能力落地与故障修复
2.1 功能实现
- 音色下拉项新增试听按钮(播放/暂停/加载态)
- 新增后端试听接口:
/api/videos/voice-preview - 试听文本按音色 locale 自动选择固定示例文案(9 国语言 + 中文兜底)
2.2 兼容与稳定性调整
- 保留
POST /api/videos/voice-preview(兼容) - 新增
GET /api/videos/voice-preview?voice=...,前端改为直接播放 GET 音频流,减少浏览器自动播放策略干扰
@router.get("/voice-preview")
async def preview_voice_get(voice: str, current_user: dict = Depends(get_current_user)):
voice_value = voice.strip()
if not voice_value:
raise HTTPException(status_code=400, detail="voice 不能为空")
text = _get_preview_text_for_voice(voice_value)
return await _render_voice_preview(voice=voice_value, text=text)
2.3 本次线上问题结论(已修复)
- 现象:浏览器端试听请求 404
- 根因:新增 GET 路由后,后端进程未重启,运行中的代码仍是旧版本
- 处理:
pm2 restart vigent2-backend后路由生效 - 补充:
curl返回 401(无 auth cookie)属于预期;浏览器同源请求会自动带 cookie
✅ 3) 录音交互重构(声音克隆)
3.1 入口重排
- 去掉参考音频面板内的独立录音大块区域
- 将「上传音频 / 录音」入口放到「我的参考音频」区域底部右侧
3.2 录音流程改为弹窗
- 录音弹窗支持:开始录音 / 停止录音 / 状态计时 / 试听
- 保留并强化「使用此录音」和「弃用本次录音」
- 关闭弹窗时若仍在录音,会先停止录音再关闭
- 修正弹窗挂载位置:从局部组件渲染改为
AppModalPortal 到document.body,确保是全页面弹窗体验 - 参考音频区按钮文案更新:
录音->在线录音
3.4 文案区按钮视觉统一
- 统一「文案提取与编辑」区按钮尺寸与圆角(
px-3 py-1.5 text-xs rounded-lg) - 将
AI智能改写、保存文案按钮改为与上传/在线录音同等级的视觉规格 - 同步统一图标尺寸与禁用态样式,消除“底部按钮偏小”问题
3.5 录音试听条 UI 美化
- 将录音完成后的原生白色
<audio controls>替换为项目深色风格的自定义试听条 - 新试听条包含:播放/暂停按钮、进度拖拽、当前时长/总时长显示
- 统一配色到当前页面(深色底 + 绿色强调),避免与整体 UI 风格割裂
3.6 录音上传关闭时机优化
- 原逻辑:点击「使用此录音」后,需等待上传+识别完成才关闭弹窗(体感卡顿)
- 新逻辑:点击后立即关闭弹窗,上传/识别在后台继续进行
- 状态反馈仍在参考音频区域显示(上传识别中的提示 + 失败错误提示)
✅ 5) 发布管理抖音登录「无法获取二维码」修复
问题定位
- 现象:发布管理中点击抖音登录,前端提示无法获取二维码
- 后端日志显示根因:
Page.goto: Timeout 30000ms exceeded- 导航目标:
https://creator.douyin.com/ - 等待条件:
wait_until="networkidle"
修复方案
- 抖音登录页改为与微信一致的更稳策略:
wait_until="domcontentloaded" - 对抖音导航超时增加容错:即使
goto超时,也继续执行二维码提取流程(避免长连接导致误失败)
验证
- 本地接口冒烟:
POST /api/publish/login/douyin返回success=true且包含qr_code - 已重启后端进程使修复生效:
pm2 restart vigent2-backend
3.3 状态逻辑补齐
- 新增
discardRecording():清空本次录音与计时 - 开始新录音前先清空旧录音,避免旧状态残留
✅ 4) 弹窗 UI/UX 统一(AppModal)
新增统一弹窗基座:frontend/src/shared/ui/AppModal.tsx
- 统一遮罩:
bg-black/80 + backdrop-blur-sm - 统一容器:深色半透明背景、
border-white/10、rounded-2xl、重阴影 - 统一 Header:标题/副标题/关闭按钮
- 统一行为:ESC 关闭、背景滚动锁定、按需控制 overlay 点击关闭
- 统一挂载:通过 Portal 渲染到
document.body,避免出现“看起来只在配音区弹出”的层叠问题 - 统一可访问性:补齐
role="dialog"+aria-modal="true" - 统一焦点管理:打开弹窗自动聚焦,关闭后恢复到打开前焦点元素
- 统一滚动锁计数:支持多弹窗并存,避免一个弹窗关闭后提前恢复页面滚动
已迁移弹窗:
- 视频预览(
VideoPreviewModal) - 文案提取(
ScriptExtractionModal) - AI 改写(
RewriteModal) - 截取设置(
ClipTrimmer) - 录音弹窗(
RefAudioPanel内) - 修改密码弹窗(
AccountSettingsDropdown) - 发布管理扫码登录弹窗(
PublishPage内 QR 登录弹窗)
✅ 6) 微信视频号登录二维码观感优化(“能扫但像被截断”)
问题现象
- 微信视频号登录二维码可扫码成功,但视觉上像“边缘不完整/被切掉”,观感不佳
修复方案
- 后端二维码提取策略增强(
qr_login_service.py):- 优先导出二维码原始 PNG 数据(
canvas.toDataURL('image/png')/img[data:image/png]),减少二次截图导致的边缘损失 - 微信回退截图时改为“按二维码 bbox 外扩留白裁剪”,避免贴边截取带来的不完整感
- 仅接受 PNG Data URL,避免把非 PNG(如 SVG 片段)直接当二维码返回造成边角异常
- 优先导出二维码原始 PNG 数据(
- 前端扫码弹窗展示优化(
PublishPage.tsx):- 取消二维码图片本体圆角裁切,改为外层白底容器 + 内边距(模拟 quiet zone)
- 同步调整二维码显示宽度与边框,提升完整感与观感一致性
验证
- 本地接口冒烟:
POST /api/publish/login/weixin返回success=true且包含qr_code - 解码后图片尺寸为
1000x1000,扫码仍正常 - 前后端进程已重启使修复生效:
pm2 restart vigent2-frontendpm2 restart vigent2-backend
✅ 7) 发布流程性能与日志可读性优化(双平台发布场景)
7.1 发布请求并发优化(前端)
- 原逻辑:发布页按平台串行
for...of await,多平台总耗时为各平台耗时累加 - 新逻辑:引入受限并发执行(并发度=2),两平台可并行发布,显著缩短总等待时长
- 结果列表仍按用户选择的平台顺序回填,避免并发返回导致顺序抖动
7.2 微信上传日志噪声优化(后端)
- 原逻辑:
set_input_files后若立即读不到input.files[0]就直接打 warning:[weixin][file_input] empty - 新逻辑:先轮询确认“是否已进入上传中状态”,再决定是否告警;非最后一次重试只记 info,最后一次才 warning
- 效果:减少误报警(实际已开始上传时不再刷 warning),排障日志更干净
验证
python -m py_compile backend/app/services/uploader/weixin_uploader.py✅npm run build(frontend)✅- 服务重启:
pm2 restart vigent2-frontend && pm2 restart vigent2-backend✅
✅ 8) 小红书发布链路对齐改造(启动模式 / Cookie 格式 / 成功截图)
8.1 启动模式与反检测参数对齐
- 在
config.py新增小红书 Playwright 配置:XIAOHONGSHU_HEADLESS_MODE(默认headless-new)XIAOHONGSHU_USER_AGENT / LOCALE / TIMEZONE_IDXIAOHONGSHU_CHROME_PATH / BROWSER_CHANNELXIAOHONGSHU_FORCE_SWIFTSHADER / DEBUG_ARTIFACTS
xiaohongshu_uploader.py改为与抖音/微信一致的可配置启动策略,并保留反检测基础参数(--disable-blink-features=AutomationControlled)
8.2 小红书 uploader 重构增强
- 重写小红书 uploader 主流程(参考抖音/微信模式):
- 上传入口/文件 input 多选择器回退
- 上传中/成功/失败状态轮询判定
- 标题与正文/话题填充容错
- 发布按钮多选择器与可点击检查
- 发布成功判定从“仅 URL”增强为“多信号组合”:
- URL 跳转判定
- 页面成功/失败文案判定
- 发布 API 响应监听(
publish/note create类接口)
- 发布成功后补齐截图能力并返回
screenshot_url(路径格式与抖音/微信一致):/api/publish/screenshot/{filename}
8.3 Cookie 保存格式统一
publish_service.save_cookie_string()调整:bilibili继续使用原有简化 cookie dict(兼容既有上传库)- 非
bilibili平台统一保存为 Playwrightstorage_state:{"cookies": [...], "origins": []}
- 补充平台默认 domain(抖音/微信/小红书),使 cookie 文件可直接用于
browser.new_context(storage_state=...)
8.4 验证与生效
python -m py_compile backend/app/core/config.py backend/app/services/publish_service.py backend/app/services/uploader/xiaohongshu_uploader.py✅pm2 restart vigent2-backend✅
✅ 9) 小红书登录二维码修复(默认短信登录需先切换)
问题现象
- 小红书创作平台
https://creator.xiaohongshu.com/默认落在“短信登录”视图 - 二维码需要先点击右上角切换图标才会出现,导致后端直接按二维码选择器抓取失败
修复方案(qr_login_service.py)
- 新增
_ensure_xiaohongshu_qr_mode():- 先检测是否处于短信登录(
input[placeholder*='手机号']) - 自动点击登录卡片右上角切换图标(优先稳定选择器,失败后用几何位置兜底)
- 切换后等待二维码渲染再进入提取流程
- 先检测是否处于短信登录(
- 扩展小红书二维码选择器集合:
- 增加登录卡片内二维码图片选择器(包含当前页面结构)
- 保留通用
img[src*='qr'/'qrcode']兜底
- 提高小红书候选过滤阈值(
min_side=120),避免误选右上角切换小图标 - 文本策略补充小红书关键词(如
APP扫一扫登录)
验证
- 本地接口冒烟:
POST /api/publish/login/xiaohongshu返回success=true且qr_code非空 - 后端日志确认修复链路生效:
已点击登录方式切换,等待二维码渲染策略1(CSS): 匹配成功
✅ 10) 小红书发布上传阶段修复(“发布笔记 - 上传视频”场景)
问题现象
- 小红书发布在“上传视频”阶段失败,页面停留在发布页,前端提示发布失败
- 后端日志显示
set_input_files触发成功,但短时间内未检测到上传状态,导致重复触发上传并误判失败 - 进一步定位到上传文件实际是 Supabase 本地对象文件(无后缀),日志里
file_input type=为空,平台可能无法正确识别视频 MIME
修复方案(xiaohongshu_uploader.py)
- 新增上传启动探测窗口
UPLOAD_SIGNAL_TIMEOUT=12s:set_input_files成功后给上传状态留出启动时间- 检测到“上传中/处理中/转码中”等信号即进入后续上传轮询
- 启动窗口内未出现明显信号时,不再立即判失败,转入主上传监控阶段继续等待
- 修正失败判定词:
- 从失败关键词中移除
重新上传(该文案在小红书页面常作为正常状态/操作入口,不能直接视为失败)
- 从失败关键词中移除
- 增补上传文件诊断日志:
- 输出
file_input选中文件名/大小/类型,便于确认文件是否真正注入 input - 上传失败命中时记录明确告警日志,便于线上快速定位
- 输出
- 增加无后缀视频文件兜底:
- 若原文件无后缀且父目录名带后缀(如
xxx.mp4/<uuid>),自动在/tmp/vigent_uploads生成同名临时文件(硬链接/软链接/复制兜底) - 上传改用带后缀临时文件,提升站点 MIME 识别稳定性
- 任务结束后自动清理临时上传文件
- 若原文件无后缀且父目录名带后缀(如
10.1 二次定位与加固(卡住复现后)
- 复现日志显示:即使传入了带后缀临时路径,
file_input中仍出现无后缀文件名,且长时间停留在等待上传状态... - 根因进一步确认:此前在跨设备场景下会走
symlink回退,浏览器实际取到原始目标文件名(无后缀),导致站点识别失败 - 加固修复:
- 去掉
symlink回退,仅保留hardlink -> copy,确保最终上传文件名稳定带.mp4 - 新增
file_input文件名后缀一致性校验:若与预期后缀不一致,直接重试并在最终失败时提前返回(不再无意义长时间等待) - 新增上传空转超时保护(
UPLOAD_IDLE_TIMEOUT=90s):长时间无有效上传信号时提前失败并保留调试截图,避免前端“看起来卡死” - 优化失败文案为“未能触发有效视频上传,请确认发布页状态及视频文件格式”
- 去掉
10.2 实时发布验证(修复后)
- 重新发起
POST /api/publish(小红书),后端完整走通上传+发布,接口返回200 - 本次实测耗时约
45.77s,属于上传与发布等待区间内的正常时长 - 发布成功截图可访问:
GET /api/publish/screenshot/xiaohongshu_success_20260303_115944_633.png返回200 - 关键日志链路:
正在上传->已设置上传文件->等待发布结果->Cookie 更新完毕
验证
python -m py_compile backend/app/services/uploader/xiaohongshu_uploader.py✅pm2 restart vigent2-backend✅curl http://127.0.0.1:8006/health返回{"status":"ok"}✅
📁 今日主要修改文件
| 文件 | 改动 |
|---|---|
backend/app/modules/videos/router.py |
新增/增强 voice-preview GET+POST,试听文本 locale 路由,临时文件清理 |
backend/app/modules/videos/schemas.py |
新增 VoicePreviewRequest |
frontend/src/features/home/ui/VoiceSelector.tsx |
音色下拉增加试听按钮,改为 GET 音频流播放 |
frontend/src/features/home/model/useHomeController.ts |
录音状态重置、discardRecording |
frontend/src/features/home/ui/HomePage.tsx |
透传录音弃用动作 |
frontend/src/features/home/ui/RefAudioPanel.tsx |
上传/录音入口重排;录音改弹窗;使用/弃用流程 |
frontend/src/features/home/ui/ScriptEditor.tsx |
文案编辑区按钮视觉统一(含 AI智能改写/保存文案) |
frontend/src/features/home/ui/RefAudioPanel.tsx |
录音完成试听条改为自定义深色播放器(替换原生白色控制条) |
frontend/src/features/home/ui/RefAudioPanel.tsx |
使用录音后弹窗立即关闭,上传识别后台进行(提升交互流畅度) |
frontend/src/features/publish/model/usePublishController.ts |
发布改为受限并发(并发度=2),缩短多平台发布总耗时 |
backend/app/core/config.py |
新增小红书 Playwright 配置(headless/UA/locale/timezone/chrome/debug) |
backend/app/services/uploader/xiaohongshu_uploader.py |
按抖音/微信模式重构;补充上传启动容错窗口、无后缀文件兜底(hardlink/copy)、后缀一致性校验、空转超时保护与上传诊断日志 |
backend/app/services/publish_service.py |
save_cookie_string 非 bilibili 统一存储为 Playwright storage_state;小红书 uploader 透传 user_id |
backend/app/services/qr_login_service.py |
抖音导航超时容错 + 微信二维码提取增强 + 小红书登录自动切换到扫码模式并提取二维码 |
backend/app/services/uploader/weixin_uploader.py |
file_input empty 告警策略优化:先检测上传信号,非最后一次重试降级为 info |
frontend/src/shared/ui/AppModal.tsx |
统一弹窗组件 + 无障碍语义 + 焦点管理 + 多弹窗滚动锁计数 |
frontend/src/components/VideoPreviewModal.tsx |
迁移到 AppModal |
frontend/src/features/home/ui/ScriptExtractionModal.tsx |
迁移到 AppModal |
frontend/src/features/home/ui/RewriteModal.tsx |
迁移到 AppModal |
frontend/src/features/home/ui/ClipTrimmer.tsx |
迁移到 AppModal |
frontend/src/components/AccountSettingsDropdown.tsx |
修改密码弹窗迁移到 AppModal |
frontend/src/features/publish/ui/PublishPage.tsx |
扫码登录(QR)弹窗迁移到 AppModal + 二维码白底留白容器优化(避免边缘观感被裁) |
Docs/FRONTEND_DEV.md |
新增统一弹窗规范(AppModal)和录音交互规范 |
Docs/FRONTEND_README.md |
增补录音入口与弹窗交互说明 |
Docs/BACKEND_README.md |
增补 voice-preview 接口说明;更新发布 API 路径(/login/{platform} 等)并链接发布专项文档 |
Docs/BACKEND_DEV.md |
更新后端规范中的发布器覆盖范围与小红书配置项;补充发布专项文档指引 |
Docs/PUBLISH_DEPLOY.md |
新增多平台发布专项文档(登录实现、自动化发布流程、部署要点与排障) |
Docs/DEPLOY_MANUAL.md |
部署参数与扫码说明补充小红书要点;新增发布专项文档入口 |
README.md |
文档中心新增 PUBLISH_DEPLOY.md 入口;发布结果可视化描述补齐小红书 |
Docs/TASK_COMPLETE.md |
新增 Day31 任务汇总,更新 Current 标签与更新时间 |
Docs/DOC_RULES.md |
增补“发布相关三检”(路由真值/专项文档/入口回写)、敏感信息处理规范,更新工具规范为 Read/Grep/apply_patch,并对齐 TASK_COMPLETE 检查清单 |
Docs/SUBTITLE_DEPLOY.md |
与当前阈值/参数说明对齐 |
Docs/LATENTSYNC_DEPLOY.md |
与当前阈值/参数说明对齐 |
Docs/COSYVOICE3_DEPLOY.md |
TTS 部署说明与当前运行路径对齐 |
Docs/QWEN3_TTS_DEPLOY.md |
标注为历史归档并指向 CosyVoice 3.0 |
🔍 验证记录
python -m py_compile backend/app/modules/videos/router.py backend/app/modules/videos/schemas.py✅python -m py_compile backend/app/services/qr_login_service.py✅python -m py_compile backend/app/services/uploader/weixin_uploader.py✅python -m py_compile backend/app/core/config.py backend/app/services/publish_service.py backend/app/services/uploader/xiaohongshu_uploader.py✅POST /api/publish/login/xiaohongshu冒烟返回success=true+qr_code✅python -m py_compile backend/app/services/uploader/xiaohongshu_uploader.py(上传阶段修复后)✅pm2 restart vigent2-backend(上传阶段修复后)✅curl http://127.0.0.1:8006/health返回{"status":"ok"}✅backend/venv/bin/python本地探针验证_prepare_upload_file():临时文件非 symlink、后缀.mp4、清理成功 ✅- 小红书发布实测:
POST /api/publish返回200(Duration: 45.77s)且成功截图接口返回200✅ - 新增
Docs/PUBLISH_DEPLOY.md(抖音/微信/B站/小红书登录与发布实现说明)✅ npm run build(frontend)✅POST /api/publish/login/weixin冒烟返回success=true+qr_code✅npx eslint定向检查以下文件通过:VoiceSelector.tsxRefAudioPanel.tsxHomePage.tsxuseHomeController.tsAppModal.tsxVideoPreviewModal.tsxScriptExtractionModal.tsxRewriteModal.tsxAccountSettingsDropdown.tsx
ClipTrimmer.tsx仍有仓库既有 lint 规则项(react-hooks/set-state-in-effect),与本次弹窗风格迁移无关- 音色试听线上问题经后端重启后已恢复可用(浏览器同源携带 cookie)
☑️ Day31 覆盖核对(今日新增补充)
已对照今天新增改动做二次核对,以下内容已写入本日志:
AppModal的可访问性与焦点/滚动锁稳健性增强- 微信视频号二维码“观感不完整”问题的后端提取修复
- 发布页二维码展示样式优化(白底留白、去除本体圆角裁切)
- 小红书 uploader 对齐重构(启动参数、发布判定、成功截图)
- 小红书“上传阶段卡住”二次定位与加固(文件名后缀一致性 + 空转超时)并完成实测发布成功
- 形成发布专项文档
Docs/PUBLISH_DEPLOY.md,沉淀四平台登录与自动化发布实现 - 回写
Docs/BACKEND_README.md/Docs/BACKEND_DEV.md/Docs/DEPLOY_MANUAL.md,统一发布 API 与部署说明口径 - 回写
README.md,补充发布专项文档入口与小红书发布成功截图能力描述 - 回写
Docs/TASK_COMPLETE.md,补齐 Day31 任务完成记录 - 回写
Docs/DOC_RULES.md,同步文档更新规则到当前文档结构与工具链 - 非 bilibili 平台 cookie 保存为
storage_state格式 - 小红书登录二维码自动切换(短信登录 -> 扫码登录)与提取修复
- 对应构建/重启/冒烟验证记录
- 今日运行期产物(
backend/user_data/**/cookies/*.json、watchdog.log)为会话副产物,不属于代码/文档变更项