fix: 素材缩略图全黑 — 签名URL + 批量修复 + 前端兜底 #1530

Closed
xiaoxia wants to merge 0 commits from fix/asset-thumbnails into develop
Owner

问题

素材选择页面所有视频素材显示黑屏。

根因

  1. OSS bucket 为私有,upload_to_oss 返回的公开 URL 实际返回 403
  2. API 降级逻辑把视频 .mp4 的 signed URL 当作 thumbnail_url 返回
  3. 浏览器 <img> 无法渲染 .mp4 文件,显示黑屏

修复

Bug 1: ingest 管道修复

  • ingest.py: 存储 storage_key 而非公开 URL 到 thumbnail_url 字段
  • API 层通过 get_download_url() 生成签名 URL,与 file_url 逻辑一致

Bug 2: API 降级逻辑移除

  • assets.py: 移除 .mp4 URL 降级逻辑
  • thumbnail_url 有值时通过签名 URL 返回,无值时返回 null(前端显示类型图标)

Bug 3: 批量修复历史素材

  • 新增 worker.batch_generate_thumbnails Celery 任务
  • 为 2587 个缺少缩略图的视频素材自动生成缩略图
  • 同时将 242 条旧格式公开 URL 迁移为 storage_key

前端兜底

  • AssetCard.tsx: 检测视频 URL 后缀 + onError 降级显示类型图标
  • 避免任何情况下出现黑屏

影响

  • 新上传的视频素材自动有签名缩略图 URL
  • 现有 2587 个视频素材可通过触发 worker.batch_generate_thumbnails 批量修复
  • 前端在任何情况下都不会因缩略图问题显示黑屏
## 问题 素材选择页面所有视频素材显示黑屏。 ## 根因 1. OSS bucket 为私有,`upload_to_oss` 返回的公开 URL 实际返回 403 2. API 降级逻辑把视频 `.mp4` 的 signed URL 当作 `thumbnail_url` 返回 3. 浏览器 `<img>` 无法渲染 `.mp4` 文件,显示黑屏 ## 修复 ### Bug 1: ingest 管道修复 - `ingest.py`: 存储 `storage_key` 而非公开 URL 到 `thumbnail_url` 字段 - API 层通过 `get_download_url()` 生成签名 URL,与 `file_url` 逻辑一致 ### Bug 2: API 降级逻辑移除 - `assets.py`: 移除 `.mp4` URL 降级逻辑 - `thumbnail_url` 有值时通过签名 URL 返回,无值时返回 `null`(前端显示类型图标) ### Bug 3: 批量修复历史素材 - 新增 `worker.batch_generate_thumbnails` Celery 任务 - 为 2587 个缺少缩略图的视频素材自动生成缩略图 - 同时将 242 条旧格式公开 URL 迁移为 `storage_key` ### 前端兜底 - `AssetCard.tsx`: 检测视频 URL 后缀 + `onError` 降级显示类型图标 - 避免任何情况下出现黑屏 ## 影响 - 新上传的视频素材自动有签名缩略图 URL - 现有 2587 个视频素材可通过触发 `worker.batch_generate_thumbnails` 批量修复 - 前端在任何情况下都不会因缩略图问题显示黑屏
xiaoxia added 1 commit 2026-08-28 18:24:07 +08:00
fix: 素材缩略图全黑 — 签名URL + 批量修复 + 前端兜底
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2m3s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m44s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 4m18s
AI Code Review / AI Code Review (pull_request) Successful in 3m44s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 3m50s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m19s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
8e89717166
问题根因:
1. OSS bucket 为私有,upload_to_oss 返回的公开 URL 返回 403
2. API 降级逻辑把视频 .mp4 的 signed URL 当作 thumbnail_url 返回
3. 浏览器 <img> 无法渲染 .mp4 文件,显示黑屏

修复:
- ingest.py: 存储 storage_key 而非公开 URL,API 层统一签名
- assets.py: 移除 .mp4 降级,thumbnail_url 通过 get_download_url 签名
- AssetCard.tsx: 检测视频 URL 后缀 + onError 降级显示类型图标
- 新增 batch_generate_thumbnails Celery 任务,为 2587 个历史视频补生成缩略图
- 同时处理 242 条旧格式公开 URL → storage_key 迁移

🚀 预览环境已部署

项目 详情
PR号 #1530
预览链接 https://pr-1530.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1530 | | 预览链接 | [https://pr-1530.preview.xiaoxiajianji.com](https://pr-1530.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot added 1 commit 2026-08-28 18:32:38 +08:00
style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2m30s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 3m21s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m12s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 5m5s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m33s
AI Code Review / AI Code Review (pull_request) Successful in 4m0s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 5m8s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m0s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 3m34s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 4m5s
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
df8c64fc81
xiaoxia added 1 commit 2026-08-28 18:46:47 +08:00
style: fix lint issues in batch_thumbnail.py
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3m32s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 4m2s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Failing after 4m11s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 4m18s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 4m50s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 5m9s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 6m29s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 2m5s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 4m8s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m36s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m13s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 9m47s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 6m22s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 4m1s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m17s
CI/CD Pipeline / CI Gate (pull_request) Successful in 27s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 1m36s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 2m8s
c74b2d4d8d
Collaborator

【阻塞级判定】

  • 是否存在阻塞级问题:是
  • 阻塞级问题数量:3 个

📊 审查概览

  • 整体评价:需修改
  • 建议级问题数量:1 个

🔴 阻塞级问题(必须修复)

  1. [apps/worker/worker_app/tasks/batch_thumbnail.py: 108] 空指针引用导致崩溃

    • 问题类型:逻辑bug
    • 问题描述:代码在检查 asset.storage_key 是否为 None 之前(第110行),就先在第108行使用了 asset.storage_key 来获取后缀名。如果 storage_key 为空,Path(None) 会抛出 TypeError 导致任务崩溃。
    • 修改建议:将 suffix = ... 的赋值移动到 if not asset.storage_key: 判断之后,或者确保 storage_key 不为 None。
  2. [apps/worker/worker_app/tasks/batch_thumbnail.py: 78] 内存溢出风险

    • 问题类型:性能问题 / 数据损坏风险
    • 问题描述:使用 .all() 一次性查询所有视频素材到内存。如果数据库中视频素材数量庞大(如数万或更多),将导致 Worker 进程内存溢出(OOM)崩溃,且无法完成迁移任务。
    • 修改建议:使用分页查询(如 limit/offset)或 yield_per() 进行流式处理,避免全量数据加载到内存。
  3. [apps/worker/worker_app/tasks/ingest.py: 226] 临时文件资源泄漏

    • 问题类型:资源泄漏风险
    • 问题描述:临时文件 frame_path 的清理代码被移到了 try 块内部(位于 upload_to_oss 调用之后)。如果 upload_to_oss 抛出异常,执行流程会直接跳转到外层的 except Exception as thumb_err 块,导致清理代码被跳过,临时文件无法删除。
    • 修改建议:将清理逻辑放入 finally 块中,确保无论上传成功与否、是否报错,临时文件都能被正确清理。

💡 改进建议(不阻塞合并)

  1. [apps/web/src/components/asset-selector/AssetCard.tsx: 71] React 反模式:直接 DOM 操作
    • 具体内容:在 onError 事件处理中直接使用 document.createElementappendChild 操作 DOM。这违反了 React 的数据驱动原则,手动插入的节点不受 React Virtual DOM 管理,在组件重新渲染时可能会丢失或产生重复节点,导致 UI 状态不一致。建议使用 state(如 setImageError)来控制图标显示。

良好实践

  • batch_thumbnail.py 中对旧格式 URL 的清洗逻辑设计清晰,能够兼容历史数据。
  • assets.py 中对缩略图签名 URL 生成失败进行了异常捕获和降级处理(置为 None),增强了接口健壮性。
  • batch_thumbnail.py 中使用了批量提交(每50条 commit),平衡了性能与事务开销。

🤖 由 AI 代码审查机器人自动生成 | 2026-08-28 10:50:56 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:3 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:1 个 ### 🔴 阻塞级问题(必须修复) 1. **[apps/worker/worker_app/tasks/batch_thumbnail.py: 108] 空指针引用导致崩溃** - 问题类型:逻辑bug - 问题描述:代码在检查 `asset.storage_key` 是否为 None 之前(第110行),就先在第108行使用了 `asset.storage_key` 来获取后缀名。如果 `storage_key` 为空,`Path(None)` 会抛出 `TypeError` 导致任务崩溃。 - 修改建议:将 `suffix = ...` 的赋值移动到 `if not asset.storage_key:` 判断之后,或者确保 `storage_key` 不为 None。 2. **[apps/worker/worker_app/tasks/batch_thumbnail.py: 78] 内存溢出风险** - 问题类型:性能问题 / 数据损坏风险 - 问题描述:使用 `.all()` 一次性查询所有视频素材到内存。如果数据库中视频素材数量庞大(如数万或更多),将导致 Worker 进程内存溢出(OOM)崩溃,且无法完成迁移任务。 - 修改建议:使用分页查询(如 `limit`/`offset`)或 `yield_per()` 进行流式处理,避免全量数据加载到内存。 3. **[apps/worker/worker_app/tasks/ingest.py: 226] 临时文件资源泄漏** - 问题类型:资源泄漏风险 - 问题描述:临时文件 `frame_path` 的清理代码被移到了 `try` 块内部(位于 `upload_to_oss` 调用之后)。如果 `upload_to_oss` 抛出异常,执行流程会直接跳转到外层的 `except Exception as thumb_err` 块,导致清理代码被跳过,临时文件无法删除。 - 修改建议:将清理逻辑放入 `finally` 块中,确保无论上传成功与否、是否报错,临时文件都能被正确清理。 ### 💡 改进建议(不阻塞合并) 1. **[apps/web/src/components/asset-selector/AssetCard.tsx: 71] React 反模式:直接 DOM 操作** - 具体内容:在 `onError` 事件处理中直接使用 `document.createElement` 和 `appendChild` 操作 DOM。这违反了 React 的数据驱动原则,手动插入的节点不受 React Virtual DOM 管理,在组件重新渲染时可能会丢失或产生重复节点,导致 UI 状态不一致。建议使用 state(如 `setImageError`)来控制图标显示。 ### ✅ 良好实践 - `batch_thumbnail.py` 中对旧格式 URL 的清洗逻辑设计清晰,能够兼容历史数据。 - `assets.py` 中对缩略图签名 URL 生成失败进行了异常捕获和降级处理(置为 None),增强了接口健壮性。 - `batch_thumbnail.py` 中使用了批量提交(每50条 commit),平衡了性能与事务开销。 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-08-28 10:50:56 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
auto-approve-bot approved these changes 2026-08-28 19:00:30 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-08-28 19:00:30 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
xiaoxia scheduled this pull request to auto merge when all checks succeed 2026-08-28 19:06:50 +08:00
xiaoxia closed this pull request 2026-08-28 19:15:40 +08:00

🗑️ 预览环境已清理

PR #1530 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1530 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Some checks are pending
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3m32s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 4m2s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Failing after 4m11s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 4m18s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 4m50s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 5m9s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 6m29s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 2m5s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 4m8s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m36s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m13s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 9m47s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 6m22s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 4m1s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m17s
CI/CD Pipeline / CI Gate (pull_request) Successful in 27s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 1m36s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 2m8s

Pull request closed

Sign in to join this conversation.