fix: dedupe suffixed video tasks
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user