249 lines
11 KiB
Markdown
249 lines
11 KiB
Markdown
## 🐛 缺陷修复:视频生成与持久化回归 (Day 21)
|
||
|
||
### 概述
|
||
本日修复 Day 20 优化后引入的 3 个回归缺陷:Remotion 渲染崩溃容错、首页作品选择持久化、发布页作品选择持久化。
|
||
|
||
---
|
||
|
||
### 已完成修复
|
||
|
||
#### BUG-1: Remotion 渲染进程崩溃导致标题/字幕丢失
|
||
- **现象**: 视频生成后没有标题和字幕,回退到纯 FFmpeg 合成。
|
||
- **根因**: Remotion Node.js 进程在渲染完成(100%)后以 SIGABRT (code -6) 退出,Python 端将其视为失败。
|
||
- **修复**: `remotion_service.py` 在进程非零退出时,先检查输出文件是否存在且大小合理(>1KB),若存在则视为成功。
|
||
- **文件**: `backend/app/services/remotion_service.py`
|
||
|
||
```python
|
||
if process.returncode != 0:
|
||
output_file = Path(output_path)
|
||
if output_file.exists() and output_file.stat().st_size > 1024:
|
||
logger.warning(
|
||
f"Remotion process exited with code {process.returncode}, "
|
||
f"but output file exists ({output_file.stat().st_size} bytes). Treating as success."
|
||
)
|
||
return output_path
|
||
raise RuntimeError(...)
|
||
```
|
||
|
||
#### BUG-2: 首页历史作品选择刷新后不保持
|
||
- **现象**: 用户选择某个历史作品后刷新页面,总是回到第一个视频。
|
||
- **根因**: `fetchGeneratedVideos()` 在初始加载时无条件自动选中第一个视频,覆盖了 `useHomePersistence` 的恢复值。
|
||
- **修复**: `fetchGeneratedVideos` 增加 `preferVideoId` 参数,仅在明确指定时才自动选中;新增 `"__latest__"` 哨兵值用于生成完成后选中最新。
|
||
- **文件**: `frontend/src/features/home/model/useGeneratedVideos.ts`, `frontend/src/features/home/model/useHomeController.ts`
|
||
|
||
```typescript
|
||
// 任务完成 → 自动选中最新
|
||
useEffect(() => {
|
||
if (prevIsGenerating.current && !isGenerating) {
|
||
if (currentTask?.status === "completed") {
|
||
void fetchGeneratedVideos("__latest__");
|
||
} else {
|
||
void fetchGeneratedVideos();
|
||
}
|
||
}
|
||
prevIsGenerating.current = isGenerating;
|
||
}, [isGenerating, currentTask, fetchGeneratedVideos]);
|
||
```
|
||
|
||
#### BUG-3: 发布页作品选择刷新后不保持(根因:签名 URL 不稳定)
|
||
- **现象**: 发布管理页选择视频后刷新,选择丢失(无任何视频被选中)。
|
||
- **根因**: 后端 `/api/videos/generated` 返回的 `path` 是 Supabase 签名 URL,每次请求都会变化。发布页用 `path` 作为选择标识存入 localStorage,刷新后新的 `path` 与保存值永远不匹配。首页不受影响是因为使用稳定的 `video.id`。
|
||
- **修复**: 发布页全面改用 `id`(稳定标识)替代 `path`(签名 URL)进行选择、持久化和比较。
|
||
- **文件**:
|
||
- `frontend/src/shared/types/publish.ts` — `PublishVideo` 新增 `id` 字段
|
||
- `frontend/src/features/publish/model/usePublishController.ts` — `selectedVideo` 存储 `id`,发布时根据 `id` 查找 `path`
|
||
- `frontend/src/features/publish/ui/PublishPage.tsx` — `key`/`onClick`/选中比较改用 `v.id`
|
||
- `frontend/src/features/home/model/useHomeController.ts` — 预取缓存加入 `id` 字段
|
||
|
||
```typescript
|
||
// 类型定义新增 id
|
||
export interface PublishVideo {
|
||
id: string; // 稳定标识符
|
||
name: string;
|
||
path: string; // 签名 URL(仅用于播放/发布)
|
||
}
|
||
|
||
// 发布时根据 id 查找 path
|
||
const video = videos.find(v => v.id === selectedVideo);
|
||
await api.post('/api/publish', { video_path: video.path, ... });
|
||
```
|
||
|
||
---
|
||
|
||
### 涉及文件汇总
|
||
|
||
| 文件 | 变更 |
|
||
|------|------|
|
||
| `backend/app/services/remotion_service.py` | Remotion 崩溃容错 |
|
||
| `frontend/src/features/home/model/useGeneratedVideos.ts` | 首页视频选择不自动覆盖 |
|
||
| `frontend/src/features/home/model/useHomeController.ts` | 任务完成监听 + 预取缓存加 id |
|
||
| `frontend/src/shared/types/publish.ts` | PublishVideo 新增 id 字段 |
|
||
| `frontend/src/features/publish/model/usePublishController.ts` | 选择/持久化/发布改用 id |
|
||
| `frontend/src/features/publish/ui/PublishPage.tsx` | UI 选择比较改用 id |
|
||
|
||
### 关键教训
|
||
|
||
> **签名 URL 不可作为持久化标识**。Supabase Storage 的签名 URL 包含时间戳和签名参数,每次请求都不同。任何需要跨请求/跨刷新保持的标识,必须使用后端返回的稳定 `id` 字段。
|
||
|
||
### 重启要求
|
||
```bash
|
||
pm2 restart vigent2-backend # Remotion 容错
|
||
npm run build && pm2 restart vigent2-frontend # 前端持久化修复
|
||
```
|
||
|
||
---
|
||
|
||
## 🎨 浮动样式预览窗口优化 (Day 21)
|
||
|
||
### 概述
|
||
标题与字幕面板中的预览区域原本是内联折叠的,展开后调节下方滑块时看不到预览效果。改为 `position: fixed` 浮动窗口,固定在视口左上角,滚动页面时预览始终可见,边调边看。
|
||
|
||
### 已完成优化
|
||
|
||
#### 1. 新建浮动预览组件 `FloatingStylePreview.tsx`
|
||
- `createPortal(jsx, document.body)` 渲染到 body 层级,脱离面板 DOM 树
|
||
- `position: fixed` + 左上角固定定位,滚动时不移动
|
||
- `z-index: 150`(低于 VideoPreviewModal 的 200)
|
||
- 顶部标题栏 + X 关闭按钮,ESC 键关闭
|
||
- 桌面端固定宽度 280px,移动端自适应(最大 360px)
|
||
- `previewScale = windowWidth / previewBaseWidth` 自行计算缩放
|
||
- `maxHeight: calc(100dvh - 32px)` 防止超出视口
|
||
|
||
#### 2. 修改 `TitleSubtitlePanel.tsx`
|
||
- 删除内联预览区域(`ref={previewContainerRef}` 整块 JSX)
|
||
- 条件渲染 `<FloatingStylePreview />`,按钮文本保持"预览样式"/"收起预览"
|
||
- 移除 `previewScale`、`previewAspectRatio`、`previewContainerRef` props
|
||
- 保留 `previewBaseWidth/Height`(浮动窗口需要原始尺寸计算 scale)
|
||
|
||
#### 3. 清理 `useHomeController.ts`
|
||
- 移除 `previewContainerWidth` 状态
|
||
- 移除 `titlePreviewContainerRef` ref
|
||
- 移除 ResizeObserver useEffect(浮动窗口自管尺寸,不再需要)
|
||
|
||
#### 4. 简化 `HomePage.tsx` 传参
|
||
- 移除 `previewContainerWidth`、`titlePreviewContainerRef` 解构
|
||
- 移除 `previewScale`、`previewAspectRatio`、`previewContainerRef` prop 传递
|
||
|
||
#### 5. 移动端适配
|
||
- `ScriptEditor.tsx`:标题行改为 `flex-wrap`,"AI生成标题标签"按钮不再溢出
|
||
- 预览默认比例从 1280×720 (16:9) 改为 1080×1920 (9:16),符合抖音竖屏视频
|
||
|
||
### 涉及文件汇总
|
||
|
||
| 文件 | 变更 |
|
||
|------|------|
|
||
| `frontend/src/features/home/ui/FloatingStylePreview.tsx` | **新建** 浮动预览组件 |
|
||
| `frontend/src/features/home/ui/TitleSubtitlePanel.tsx` | 移除内联预览,渲染浮动组件 |
|
||
| `frontend/src/features/home/model/useHomeController.ts` | 移除 preview 容器相关状态和 ResizeObserver |
|
||
| `frontend/src/features/home/ui/HomePage.tsx` | 简化 props 传递,默认比例改 9:16 |
|
||
| `frontend/src/features/home/ui/ScriptEditor.tsx` | 移动端按钮换行适配 |
|
||
|
||
### 重启要求
|
||
```bash
|
||
npm run build && pm2 restart vigent2-frontend
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 多平台发布体系重构:用户隔离与抖音刷脸验证 (Day 21)
|
||
|
||
### 概述
|
||
重构发布系统的两大核心问题:① 多用户场景下 Cookie/会话缺乏隔离,② 抖音登录新增刷脸验证步骤无法处理。同时修复了平台配置混用和微信视频号发布流程问题。
|
||
|
||
---
|
||
|
||
### 一、平台配置独立化
|
||
|
||
#### 问题
|
||
所有平台(抖音、微信、B站、小红书)共用 WEIXIN_* 配置,导致 User-Agent、Headless 模式等设置不匹配。
|
||
|
||
#### 修复 — `config.py`
|
||
- 新增 `DOUYIN_*` 独立配置项:`DOUYIN_HEADLESS_MODE`、`DOUYIN_USER_AGENT`(Chrome/144)、`DOUYIN_LOCALE`、`DOUYIN_TIMEZONE_ID`、`DOUYIN_CHROME_PATH`、`DOUYIN_FORCE_SWIFTSHADER`、调试开关等
|
||
- 微信保持已有 `WEIXIN_*` 配置
|
||
- B站/小红书使用通用默认值
|
||
|
||
#### 修复 — `qr_login_service.py` 平台配置映射
|
||
```python
|
||
# 之前:所有平台都用 WEIXIN 设置
|
||
# 之后:每个平台独立配置
|
||
PLATFORM_CONFIGS = {
|
||
"douyin": { headless, user_agent, locale, timezone... },
|
||
"weixin": { headless, user_agent, locale, timezone... },
|
||
"bilibili": { 通用配置 },
|
||
"xiaohongshu": { 通用配置 },
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 二、用户隔离的 Cookie 管理
|
||
|
||
#### 问题
|
||
多用户共享同一套 Cookie 文件,用户 A 的登录态可能被用户 B 覆盖。
|
||
|
||
#### 修复 — `publish_service.py`
|
||
- `_get_cookies_dir(user_id)` → `backend/user_data/{uuid}/cookies/`
|
||
- `_get_cookie_path(user_id, platform)` → 按用户+平台返回独立 Cookie 文件路径
|
||
- `_get_session_key(user_id, platform)` → `"{user_id}_{platform}"` 格式的会话 key
|
||
- 登录/发布流程全链路传入 `user_id`,清理残留会话避免干扰
|
||
|
||
---
|
||
|
||
### 三、抖音刷脸验证二维码
|
||
|
||
#### 问题
|
||
抖音扫码登录后可能弹出刷脸验证窗口,内含新的二维码需要用户再次扫描,前端无法感知和展示。
|
||
|
||
#### 修复 — 后端 `qr_login_service.py`
|
||
- 扩展 QR 选择器:支持跨 iframe 搜索二维码元素
|
||
- 抖音 API 拦截:监听 `check_qrconnect` 响应,检测 `redirect_url`
|
||
- 检测 "完成验证" / "请前往APP完成验证" 文案
|
||
- 在验证弹窗内找到正方形二维码(排除头像),截图返回给前端
|
||
- API 确认后直接导航到 redirect_url(不重新加载 QR 页,避免销毁会话)
|
||
|
||
#### 修复 — 后端 `publish_service.py`
|
||
- `get_login_session_status()` 新增 `face_verify_qr` 字段返回
|
||
- 登录成功且 Cookie 保存后自动清理会话
|
||
|
||
#### 修复 — 前端
|
||
- `usePublishController.ts`:新增 `faceVerifyQr` 状态,轮询时获取 `face_verify_qr` 字段
|
||
- `PublishPage.tsx`:QR 弹窗优先展示刷脸验证二维码,附提示文案
|
||
|
||
```tsx
|
||
{faceVerifyQr ? (
|
||
<>
|
||
<Image src={`data:image/png;base64,${faceVerifyQr}`} />
|
||
<p>需要身份验证,请用抖音APP扫描上方二维码完成刷脸验证</p>
|
||
</>
|
||
) : /* 普通登录二维码 */ }
|
||
```
|
||
|
||
---
|
||
|
||
### 四、微信视频号发布流程优化
|
||
|
||
#### 修复 — `weixin_uploader.py`
|
||
- 添加 `user_id` 参数支持,发布截图目录隔离
|
||
- 新增 `post_create` API 响应监听,精准判断发布成功
|
||
- 发布结果判定:URL 离开创建页 或 API 确认提交 → 视为成功
|
||
- 标题/标签处理改为统一写入"视频描述"字段(不再单独填写 title/tags)
|
||
|
||
---
|
||
|
||
### 涉及文件汇总
|
||
|
||
| 文件 | 变更 |
|
||
|------|------|
|
||
| `backend/app/core/config.py` | 新增 DOUYIN_* 独立配置项 |
|
||
| `backend/app/services/qr_login_service.py` | 平台配置拆分、刷脸验证二维码、跨 iframe 选择器 |
|
||
| `backend/app/services/publish_service.py` | 用户隔离 Cookie 管理、刷脸验证状态返回 |
|
||
| `backend/app/services/uploader/weixin_uploader.py` | user_id 支持、post_create API 监听、描述字段合并 |
|
||
| `frontend/src/features/publish/model/usePublishController.ts` | faceVerifyQr 状态 |
|
||
| `frontend/src/features/publish/ui/PublishPage.tsx` | 刷脸验证二维码展示 |
|
||
|
||
### 重启要求
|
||
```bash
|
||
pm2 restart vigent2-backend # 发布服务 + QR登录
|
||
npm run build && pm2 restart vigent2-frontend # 刷脸验证UI
|
||
```
|