feat(records): 视频卡片/详情弹窗用 thumbnail_url 显示首帧 poster

后端 GenerationRecord.thumbnail_url 字段早就被 tasks.py:_handle_completed (L109-111)
通过 ffmpeg 提取首帧 + 上传 TOS 填充,但只在 _serialize_task (生成页) 返回。
admin_records / team_records / profile_records 三个 view 都没回传,前端无从用。

后端:三个 records view 各加一行 'thumbnail_url': r.thumbnail_url or ''
前端:
- types/index.ts AdminRecord 加 thumbnail_url?: string
- 三处 <video> 加 poster={thumbnailUrl ? rewriteTosUrl(...) : undefined}
  - RecordDetailModal.tsx (消费记录详情)
  - VideoDetailModal.tsx (资产页/生成页详情)
  - GenerationCard.tsx (生成页卡片)

效果:卡片首屏直接显示首帧海报(几十 KB),不再等视频 metadata 加载完
才有视觉;hover 触发 v.play() 时再真正下载视频,UX 不变。
老记录 thumbnail_url='' 时 poster=undefined,行为同改前。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
seaislee1209 2026-05-12 18:17:16 +08:00
parent e86e3d45b1
commit 72f351d54f
5 changed files with 8 additions and 0 deletions

View File

@ -1819,6 +1819,7 @@ def admin_records_view(request):
'seed': r.seed,
'ark_task_id': r.ark_task_id or '',
'result_url': r.result_url or '',
'thumbnail_url': r.thumbnail_url or '',
})
return Response({
@ -1884,6 +1885,7 @@ def team_records_view(request):
'seed': r.seed,
'ark_task_id': r.ark_task_id or '',
'result_url': r.result_url or '',
'thumbnail_url': r.thumbnail_url or '',
})
return Response({
@ -2770,6 +2772,8 @@ def profile_records_view(request):
'resolution': r.resolution,
'status': r.status,
'error_message': r.error_message or '',
'result_url': r.result_url or '',
'thumbnail_url': r.thumbnail_url or '',
})
return Response({

View File

@ -476,6 +476,7 @@ export function GenerationCard({ task, onOpenDetail }: Props) {
<video
ref={videoRef}
src={rewriteTosUrl(task.resultUrl)}
poster={task.thumbnailUrl ? rewriteTosUrl(task.thumbnailUrl) : undefined}
className={styles.resultMedia}
loop
preload="metadata"

View File

@ -121,6 +121,7 @@ function MediaArea({ record: r }: { record: AdminRecord }) {
{r.status === 'completed' && r.result_url ? (
<video
src={rewriteTosUrl(r.result_url)}
poster={r.thumbnail_url ? rewriteTosUrl(r.thumbnail_url) : undefined}
style={mediaVideo}
controls
preload="metadata"

View File

@ -303,6 +303,7 @@ export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDele
<video
ref={videoRef}
src={rewriteTosUrl(task.resultUrl)}
poster={task.thumbnailUrl ? rewriteTosUrl(task.thumbnailUrl) : undefined}
className={styles.video}
onTimeUpdate={handleTimeUpdate}
onLoadedMetadata={handleLoadedMetadata}

View File

@ -218,6 +218,7 @@ export interface AdminRecord {
seed?: number;
ark_task_id?: string;
result_url?: string;
thumbnail_url?: string;
}
export interface SystemSettings {