fix: dedupe suffixed video tasks

This commit is contained in:
2026-05-22 12:59:12 +08:00
parent 20d2d8fa68
commit 7abbb7d532

View File

@@ -2,7 +2,7 @@
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import type { AssetPack, AssetTemplate, GenImage, GenSession, PackKind, ToyAsset } from '@/lib/types';
import type { AssetPack, AssetTemplate, GenImage, GenSession, PackKind, ToyAsset, VideoTask } from '@/lib/types';
import { PACK_LABELS, PACK_ORDER, PACK_TEMPLATES, TEXT_TEMPLATES, VIDEO_TEMPLATES } from '@/lib/templates';
import { HoverImagePreview, HoverVideoPreview } from './HoverImagePreview';
@@ -35,6 +35,30 @@ function aspectCss(aspectRatio: AssetTemplate['aspectRatio'] | string | undefine
return aspectRatio.replace(':', ' / ');
}
function canonicalVideoTemplateId(templateId: string) {
return VIDEO_TEMPLATES.find(template => (
templateId === template.id || templateId.startsWith(`${template.id}_`)
))?.id ?? templateId;
}
function videoTaskScore(task: VideoTask) {
let score = 0;
if (task.videoUrl) score += 8;
if (task.status === 'succeeded') score += 4;
if (task.status === 'processing' || task.status === 'submitted') score += 2;
return score;
}
function selectVideoTaskForTemplate(tasks: VideoTask[], templateId: string) {
return tasks
.filter(task => canonicalVideoTemplateId(task.templateId) === templateId)
.sort((a, b) => {
const scoreDiff = videoTaskScore(b) - videoTaskScore(a);
if (scoreDiff) return scoreDiff;
return (b.updatedAt || b.submittedAt || 0) - (a.updatedAt || a.submittedAt || 0);
})[0];
}
type AssetDetail = {
template: AssetTemplate;
asset: ToyAsset | undefined;
@@ -379,20 +403,23 @@ function VideoSection({ videoLoading, primaryImage, locked, session, onGenerateV
onRefreshVideo: (taskId: string) => void;
}) {
const [showPromptId, setShowPromptId] = useState<string | null>(null);
const videoTasks = session.videoTasks ?? [];
const byTemplate = new Map(videoTasks.map(task => [task.templateId, task]));
const videoTasks = (session.videoTasks ?? []).filter(task => !/_part[12]$/.test(task.templateId));
const builtInIds = new Set<string>(VIDEO_TEMPLATES.map(template => template.id));
const extraTasks = videoTasks.filter(task => !builtInIds.has(task.templateId) && !/_part[12]$/.test(task.templateId));
const extraTasks = videoTasks.filter(task => !builtInIds.has(canonicalVideoTemplateId(task.templateId)));
const videoItems = [
...VIDEO_TEMPLATES.map(template => ({
...VIDEO_TEMPLATES.map(template => {
const task = selectVideoTaskForTemplate(videoTasks, template.id);
return {
id: template.id,
title: template.title,
description: template.description,
duration: template.duration,
ratio: template.ratio,
promptTemplate: template.promptTemplate,
title: task?.title ?? template.title,
description: task?.description ?? template.description,
duration: task?.duration ?? template.duration,
ratio: task?.ratio ?? template.ratio,
promptTemplate: task?.prompt ?? template.promptTemplate,
template,
})),
task,
};
}),
...extraTasks.map(task => ({
id: task.templateId,
title: task.title,
@@ -401,9 +428,10 @@ function VideoSection({ videoLoading, primaryImage, locked, session, onGenerateV
ratio: task.ratio,
promptTemplate: task.prompt,
template: null,
task,
})),
];
const submittedCount = videoItems.filter(item => byTemplate.has(item.id)).length;
const submittedCount = videoItems.filter(item => item.task).length;
const totalCount = Math.max(videoItems.length, 1);
return (
@@ -430,8 +458,8 @@ function VideoSection({ videoLoading, primaryImage, locked, session, onGenerateV
<div className="grid grid-cols-1 gap-2 border-t border-white/[0.05] p-3 2xl:grid-cols-2">
{videoItems.map(item => {
const isOpen = showPromptId === item.id;
const task = byTemplate.get(item.id);
const loadingThis = videoLoading === item.id;
const task = item.task;
const loadingThis = videoLoading === item.id || videoLoading === task?.templateId;
return (
<div key={item.id} className="grid min-w-0 grid-cols-[128px_minmax(0,1fr)] gap-3 rounded-[8px] bg-white/[0.025] p-2.5 ring-1 ring-white/[0.05] transition-all hover:ring-white/[0.12]">
<div className="relative h-24 overflow-hidden rounded-[8px] bg-black/70">