auto-save 2026-05-13 16:17 (~3)
This commit is contained in:
@@ -1868,6 +1868,19 @@
|
|||||||
"type": "session-heartbeat",
|
"type": "session-heartbeat",
|
||||||
"message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-13 16:06 (+1, ~3)",
|
"message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-13 16:06 (+1, ~3)",
|
||||||
"files_changed": 1
|
"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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
35
api/main.py
35
api/main.py
@@ -64,13 +64,21 @@ class GeneratedImage(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class StoryboardScene(BaseModel):
|
class StoryboardScene(BaseModel):
|
||||||
"""分镜头编排:每个 selected 分镜对应一个 scene 描述"""
|
"""分镜头编排:每个 selected 分镜对应一个 scene 描述
|
||||||
subject: str = "" # 主体(如:戴头带的骨架人)
|
v2: 4 图槽 + 时长(复制粘贴模式)— 主体 / 场景 / 产品 / 动作 各一张图
|
||||||
product: str = "" # 产品(如:Goli 营养软糖)
|
v1 字段保留兼容(subject/product/scene/action/reference_ids)"""
|
||||||
scene: str = "" # 场景(如:药店柜台)
|
duration: float = 0
|
||||||
action: str = "" # 在干什么(如:递给顾客一瓶软糖)
|
# 4 图槽:dict 含 {kind, frame_idx, element_id?, cutout_id?, label}
|
||||||
duration: float = 0 # 视频片段时长(秒)
|
subject_image: dict | None = None
|
||||||
reference_ids: list[str] = [] # 参考图:选用该分镜里已提取的 element ids 作 reference
|
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):
|
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):
|
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 = ""
|
subject: str = ""
|
||||||
product: str = ""
|
product: str = ""
|
||||||
scene: str = ""
|
scene: str = ""
|
||||||
action: str = ""
|
action: str = ""
|
||||||
duration: float = 0
|
|
||||||
reference_ids: list[str] = []
|
reference_ids: list[str] = []
|
||||||
|
|
||||||
|
|
||||||
@@ -1434,11 +1447,15 @@ def update_storyboard(job_id: str, idx: int, req: UpdateStoryboardReq) -> Job:
|
|||||||
for f in job.frames:
|
for f in job.frames:
|
||||||
if f.index == idx:
|
if f.index == idx:
|
||||||
f.storyboard = StoryboardScene(
|
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(),
|
subject=req.subject.strip(),
|
||||||
product=req.product.strip(),
|
product=req.product.strip(),
|
||||||
scene=req.scene.strip(),
|
scene=req.scene.strip(),
|
||||||
action=req.action.strip(),
|
action=req.action.strip(),
|
||||||
duration=max(0.0, float(req.duration)),
|
|
||||||
reference_ids=list(req.reference_ids),
|
reference_ids=list(req.reference_ids),
|
||||||
)
|
)
|
||||||
new_frames.append(f)
|
new_frames.append(f)
|
||||||
|
|||||||
@@ -47,13 +47,41 @@ export interface KeyElement {
|
|||||||
created_at?: number
|
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 {
|
export interface StoryboardScene {
|
||||||
subject: string
|
|
||||||
product: string
|
|
||||||
scene: string
|
|
||||||
action: string
|
|
||||||
duration: number
|
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 {
|
export interface KeyFrame {
|
||||||
|
|||||||
Reference in New Issue
Block a user