auto-save 2026-05-14 11:58 (~4)

This commit is contained in:
2026-05-14 11:58:48 +08:00
parent 801b194bff
commit f0c6c5b916
4 changed files with 220 additions and 215 deletions

View File

@@ -19,7 +19,7 @@ import { ThemeToggle } from "@/components/theme-toggle"
import { AudioStrip } from "@/components/audio-strip"
import {
addManualFrame, analyzeJob, createJob, getJob, listJobs, uploadJob, deleteJob, deleteFrame, deleteGeneratedImage,
deleteGeneratedVideo, deleteCutout, generateStoryboardVideo, createProductFusionGuide, triggerTranscribe,
deleteGeneratedVideo, deleteCutout, generateStoryboardVideo, triggerTranscribe,
type Job, type ImageRef, type ProductFusionShot, type StoryboardScene, type FrameExtractMode, type FrameExtractQuality, type FrameExtractTarget,
} from "@/lib/api"
import { TRANSPARENT_HUMAN_NEGATIVE_PROMPT, TRANSPARENT_HUMAN_VIDEO_PROMPT } from "@/lib/workflow-target"
@@ -520,46 +520,43 @@ export default function Home() {
if (!job) return
const frame = job.frames.find((f) => f.index === frameIdx)
if (!frame) return
if (!shot.product_image || !shot.person_image || !shot.scene_image || !shot.product_region || !shot.action_text?.trim()) {
toast.error("产品融合镜头缺少产品图、人物图、区域、场景图或描述词")
const productRefs = (shot.product_images ?? []).filter(Boolean).slice(0, 3) as ImageRef[]
if (!shot.first_image || !shot.last_image || productRefs.length < 3 || !shot.action_text?.trim()) {
toast.error("产品融合镜头缺少首帧、尾帧、三张产品角度图或描述词")
return
}
const duration = shot.duration && shot.duration > 0 ? shot.duration : 5
const labelOf = (ref?: ImageRef | null, fallback = "未提供") => ref?.label || fallback
try {
toast.info(`生成融合引导图 · GPT Image 2 位置约束 · 镜头 ${shot.id || ""}`)
const guideRef = await createProductFusionGuide(job.id, {
...shot,
image_model: "gpt-image-2",
video_model: "seedance",
})
const region = shot.product_region
const prompt = [
`竖屏 9:16${duration.toFixed(1)}Seedance 产品融合视频。`,
"图片模型固定为 GPT Image 2已根据白底人物图和手动画框生成产品融合引导图;引导图是产品尺寸、位置、贴合关系和起始构图的最高优先级参考。",
"视频模型固定为 Seedance生成单镜头连续视频,不跳切,不换主体,不改变产品身份。",
`产品区域x=${region.x.toFixed(3)}, y=${region.y.toFixed(3)}, w=${region.w.toFixed(3)}, h=${region.h.toFixed(3)}。产品只能在这个框对应的人物/身体/手部区域内融合,不能漂移到其他位置,也不能明显超出框架`,
`产品图${labelOf(shot.product_image, "SKG 白底产品图")}严格保持 SKG 产品外观、颜色、材质、U 形结构、按摩触点、按键和比例`,
`白底人物图${labelOf(shot.person_image, "人物姿态参考")}。人物姿态、手部接触点和产品佩戴关系以这张图为准`,
`场景图${labelOf(shot.scene_image, "场景参考")}。背景、空间、光线和气氛以这张图为准,但不要改变产品框内位置`,
`竖屏 9:16${duration.toFixed(1)}Seedance 图生视频。`,
"图片模型固定为 GPT Image 2首帧和尾帧已经由文字生图生成,用来锁定透明骨架人角色、场景构图和动作起止状态。",
"视频模型固定为 Seedance使用首帧作为起始画面、尾帧作为结束画面,并用三张同一 SKG 产品不同角度白底图作为垫图/产品身份参考。",
`首帧:${labelOf(shot.first_image, "透明骨架人首帧")}。起始人物形象、姿态、构图和场景氛围以这张图为准`,
`尾帧${labelOf(shot.last_image, "透明骨架人尾帧")}结束人物状态、画面落点和场景延续以这张图为准`,
`产品角度图 1${labelOf(productRefs[0], "SKG 产品正面/主视角")}`,
`产品角度图 2${labelOf(productRefs[1], "SKG 产品侧面/斜侧视角")}`,
`产品角度图 3${labelOf(productRefs[2], "SKG 产品背面/细节视角")}`,
`动作描述:${shot.action_text.trim()}`,
TRANSPARENT_HUMAN_VIDEO_PROMPT,
"融合要求:产品必须按引导图位置自然贴合人物或手部,尺寸可信,透视一致,边缘清晰,不能悬浮、穿帮、融化、扭曲或变成其他物体。",
"场景要求:把白底人物姿态自然放入场景图的环境中,光线方向和阴影要统一,背景不要出现水印、平台 UI、字幕或竞品包装。",
"融合要求:产品必须自然出现在透明骨架人动作中,尺寸可信,透视一致,贴合身体/手部/使用区域,不能悬浮、漂移、融化、扭曲或变成其他物体。",
"首尾连续性:镜头从首帧自然运动到尾帧,中间不要跳切,不换角色,不换产品,不突然改变场景。",
"产品一致性:严格保持 SKG 产品外观、颜色、材质、U 形结构、按摩触点、按键和比例;三张产品角度图是产品身份真源。",
"场景要求:背景、空间、光线和阴影要自然统一,不要出现水印、平台 UI、字幕或竞品包装。",
"商业质感:真实拍摄感、干净高级、产品清楚可辨、人物动作自然、镜头稳定。",
"禁止:文字、水印、随机品牌、非 SKG 产品、医学治疗承诺、夸张病症、恐怖元素、产品位置漂移、产品超过指定融合区域。",
"禁止:文字、水印、随机品牌、非 SKG 产品、医学治疗承诺、夸张病症、恐怖元素、产品位置漂移、透明衣服但非透明身体。",
TRANSPARENT_HUMAN_NEGATIVE_PROMPT,
].join("\n")
const updated = await generateStoryboardVideo(job.id, frameIdx, {
prompt,
duration,
first_image: guideRef,
last_image: null,
product_images: [shot.product_image, shot.person_image, shot.scene_image].filter(Boolean) as ImageRef[],
subject_image: shot.person_image,
scene_image: shot.scene_image,
product_image: shot.product_image,
action_image: guideRef,
first_image: shot.first_image,
last_image: shot.last_image,
product_images: productRefs,
subject_image: shot.first_image,
scene_image: shot.last_image,
product_image: productRefs[0] ?? null,
action_image: null,
source_ref: null,
model: "seedance",
size: "720x1280",