From f891cbc2e2c8142f655f0a65e04cbb3bd195edcc Mon Sep 17 00:00:00 2001 From: kang Date: Wed, 13 May 2026 16:18:05 +0800 Subject: [PATCH] auto-save 2026-05-13 16:17 (~3) --- .memory/worklog.json | 13 +++++++++++++ api/main.py | 35 ++++++++++++++++++++++++++--------- web/lib/api.ts | 38 +++++++++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 14 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index cab8eec..8e4b8d5 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1868,6 +1868,19 @@ "type": "session-heartbeat", "message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-13 16:06 (+1, ~3)", "files_changed": 1 + }, + { + "ts": "2026-05-13T16:12:33+08:00", + "type": "commit", + "message": "auto-save 2026-05-13 16:12 (~1)", + "hash": "3cea152", + "files_changed": 1 + }, + { + "ts": "2026-05-13T08:17:40Z", + "type": "session-heartbeat", + "message": "Claude 会话活跃 · 最近命令:claude · 3 项未提交变更 · 最近提交:auto-save 2026-05-13 16:12 (~1)", + "files_changed": 3 } ] } diff --git a/api/main.py b/api/main.py index ed6170b..0f69cf3 100644 --- a/api/main.py +++ b/api/main.py @@ -64,13 +64,21 @@ class GeneratedImage(BaseModel): class StoryboardScene(BaseModel): - """分镜头编排:每个 selected 分镜对应一个 scene 描述""" - subject: str = "" # 主体(如:戴头带的骨架人) - product: str = "" # 产品(如:Goli 营养软糖) - scene: str = "" # 场景(如:药店柜台) - action: str = "" # 在干什么(如:递给顾客一瓶软糖) - duration: float = 0 # 视频片段时长(秒) - reference_ids: list[str] = [] # 参考图:选用该分镜里已提取的 element ids 作 reference + """分镜头编排:每个 selected 分镜对应一个 scene 描述 + v2: 4 图槽 + 时长(复制粘贴模式)— 主体 / 场景 / 产品 / 动作 各一张图 + v1 字段保留兼容(subject/product/scene/action/reference_ids)""" + duration: float = 0 + # 4 图槽:dict 含 {kind, frame_idx, element_id?, cutout_id?, label} + subject_image: dict | None = None + scene_image: dict | None = None + product_image: dict | None = None + action_image: dict | None = None + # v1 兼容 + subject: str = "" + product: str = "" + scene: str = "" + action: str = "" + reference_ids: list[str] = [] class StoryboardImage(BaseModel): @@ -1413,11 +1421,16 @@ def delete_cutout(job_id: str, idx: int, element_id: str, cutout_id: str) -> Job class UpdateStoryboardReq(BaseModel): + duration: float = 0 + subject_image: dict | None = None + scene_image: dict | None = None + product_image: dict | None = None + action_image: dict | None = None + # v1 字段(前端可不传) subject: str = "" product: str = "" scene: str = "" action: str = "" - duration: float = 0 reference_ids: list[str] = [] @@ -1434,11 +1447,15 @@ def update_storyboard(job_id: str, idx: int, req: UpdateStoryboardReq) -> Job: for f in job.frames: if f.index == idx: f.storyboard = StoryboardScene( + duration=max(0.0, float(req.duration)), + subject_image=req.subject_image, + scene_image=req.scene_image, + product_image=req.product_image, + action_image=req.action_image, subject=req.subject.strip(), product=req.product.strip(), scene=req.scene.strip(), action=req.action.strip(), - duration=max(0.0, float(req.duration)), reference_ids=list(req.reference_ids), ) new_frames.append(f) diff --git a/web/lib/api.ts b/web/lib/api.ts index 5946258..31faf5b 100644 --- a/web/lib/api.ts +++ b/web/lib/api.ts @@ -47,13 +47,41 @@ export interface KeyElement { created_at?: number } +export interface ImageRef { + kind: "keyframe" | "cutout" + frame_idx: number + element_id?: string | null + cutout_id?: string | null + label?: string +} + export interface StoryboardScene { - subject: string - product: string - scene: string - action: string duration: number - reference_ids: string[] + subject_image?: ImageRef | null + scene_image?: ImageRef | null + product_image?: ImageRef | null + action_image?: ImageRef | null + // v1 兼容 + subject?: string + product?: string + scene?: string + action?: string + reference_ids?: string[] +} + +// 把 ImageRef 解析成可显示的 src URL +export function resolveImageRefUrl(jobId: string, ref: ImageRef): string { + if (ref.kind === "keyframe") { + return effectiveFrameUrl(jobId, { index: ref.frame_idx, cleaned_applied: false }) + } + if (ref.element_id && ref.cutout_id) { + if (ref.cutout_id === ref.element_id) { + // legacy v1 + return cutoutUrl(jobId, ref.frame_idx, ref.element_id) + } + return cutoutUrl(jobId, ref.frame_idx, ref.element_id, ref.cutout_id) + } + return "" } export interface KeyFrame {