diff --git a/.memory/worklog.json b/.memory/worklog.json index ae38ad0..f25eedc 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,18 +1,5 @@ { "entries": [ - { - "files_changed": 1, - "hash": "b4d31f6", - "message": "auto-save 2026-05-15 12:13 (~1)", - "ts": "2026-05-15T12:13:37+08:00", - "type": "commit" - }, - { - "files_changed": 1, - "message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-15 12:13 (~1)", - "ts": "2026-05-15T04:14:45Z", - "type": "session-heartbeat" - }, { "files_changed": 1, "hash": "2b3e7bd", @@ -3263,6 +3250,19 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 1 项未提交变更 · 最近提交:auto-save 2026-05-17 19:59 (~3)", "files_changed": 1 + }, + { + "ts": "2026-05-17T20:15:13+08:00", + "type": "commit", + "message": "auto-save 2026-05-17 20:15 (~4)", + "hash": "72aef99", + "files_changed": 4 + }, + { + "ts": "2026-05-17T12:18:29Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 4 项未提交变更 · 最近提交:auto-save 2026-05-17 20:15 (~4)", + "files_changed": 4 } ] } diff --git a/api/main.py b/api/main.py index 9e6df4c..d6b9be1 100644 --- a/api/main.py +++ b/api/main.py @@ -4569,15 +4569,13 @@ def analyze_product_view(ref_path: Path, index: int) -> dict: return fallback_product_view(index) img_b64 = base64.b64encode(ref_path.read_bytes()).decode("ascii") prompt = ( - "You are inspecting one reference image from a same-product image pool for a SKG neck-and-shoulder wearable massage device. Do not classify product identity or compare different products; all uploaded references belong to the same product. The background may be white, black, or simple studio color. " - "Classify the camera/view angle into exactly one enum: front, left_45, right_45, side_thickness, inner_contacts, back_bottom. " - "Classify background into exactly one enum: white, black, simple, complex, unknown. Do not request or perform background conversion. " - "Add use_tags from this enum only: hero_packshot, wearing_scale, inner_contact, side_thickness, asymmetry, button_detail, back_bottom, material_texture. " - "Also write a concise Chinese note for video generation, focused on visible structure, asymmetry, thickness, inner massage contacts, buttons, opening width, shoulder-neck wearing scale, and what this image is best used for in video generation. " - "Write risk in Chinese only if this reference may mislead video generation, such as cropped product, hand blocking, low detail, strong reflection, or background confusion; otherwise use empty string. " - "If uncertain, choose the closest useful view; do not ask the user. " + "你在识别同一款 SKG 挂脖肩颈按摩仪的一张产品参考图。它是套在脖子上的 U 形/围脖式按摩仪,不是耳机、头戴设备或护颈枕;所有上传图都属于同一产品,不要判断不同产品身份。 " + "必须使用产品坐标系:product_left=戴在真人脖子上时佩戴者左肩一侧,product_right=佩戴者右肩一侧,top=靠近下巴/脸/颈部上沿,bottom=靠近锁骨/肩部下沿,inner_side=贴颈皮肤/按摩触点,outer_side=外壳/按键/Logo。不要把图片左侧直接当产品左侧;在 orientation 里写清楚产品左/右/上/下对应图中哪边,不确定就说明不确定并写 risk。 " + "view 从 enum 选一个:front, left_45, right_45, side_thickness, inner_contacts, back_bottom。left_45/right_45 指佩戴者身体左右,不是画面左右。 " + "background 从 enum 选:white, black, simple, complex, unknown。use_tags 只能从 enum 选:hero_packshot, wearing_scale, inner_contact, side_thickness, asymmetry, button_detail, back_bottom, material_texture。 " + "landmarks 用中文短词列出可见结构,例如佩戴者左侧臂、佩戴者右侧臂、U形开口、贴颈内侧、按摩触点、侧边厚度、按键、充电口、底部、外壳材质、局部细节。note 用中文写给生视频模型,重点说明左/右/上/下、内/外侧、触点或局部细节。risk 只在可能误导生视频时写中文,否则为空。 " "Output one-line strict JSON only. Do not use markdown or line breaks. " - "{\"view\":\"front|left_45|right_45|side_thickness|inner_contacts|back_bottom\", \"background\":\"white|black|simple|complex|unknown\", \"use_tags\":[\"hero_packshot\"], \"note\":\"中文备注\", \"risk\":\"\", \"confidence\":0.0}." + "{\"view\":\"front|left_45|right_45|side_thickness|inner_contacts|back_bottom\",\"background\":\"white|black|simple|complex|unknown\",\"use_tags\":[\"hero_packshot\"],\"orientation\":{\"product_left\":\"图中哪一侧/不可见/不确定\",\"product_right\":\"图中哪一侧/不可见/不确定\",\"top\":\"图中哪一侧/不可见/不确定\",\"bottom\":\"图中哪一侧/不可见/不确定\",\"inner_side\":\"图中哪一侧/是否可见\",\"outer_side\":\"图中哪一侧/是否可见\",\"opening_direction\":\"U形开口朝图中哪一侧/不可见/不确定\"},\"landmarks\":[\"U形开口\"],\"note\":\"中文备注\",\"risk\":\"\",\"confidence\":0.0}." ) try: resp = llm().chat.completions.create( diff --git a/docs/source-analysis.html b/docs/source-analysis.html index 4e9b9af..0a1f358 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -589,7 +589,7 @@ web/next.config.mjsNext.js 构建配置:静态导出、图片不走优化、禁用开发环境左下角 Next Dev Indicator,并移除 Next 16 已不支持的 eslint 顶层配置,避免本地 dev 出现配置 Issue 提示。 web/app/globals.css全局主题变量、登录页视觉样式、ReactFlow 样式引用,以及本地开发态 nextjs-portal 遮挡隐藏规则。 web/app/page.tsx产品工作台主状态:jobs、activeJobId、生成任务状态;主渲染为全屏素材输入列 + 信息流广告复刻工作表;“开始”编排状态只负责在下载完成后自动触发 triggerTranscribe,不再默认触发抽帧、Vision 扫描或分镜初稿保存;底部吸附音频条不再从主界面渲染。 - web/components/ad-recreation-board.tsx信息流广告复刻工作表:左侧素材输入;右侧展示视频下载状态、默认折叠的音频文案依据,以及统一的音频解析结果面板;面板顶部是一行讲话人/节奏/背景音摘要,下方左侧为原视频播放器、右侧为逐句时间轴,底部横向音频波形用参考图式的连续灰色包络显示响度、停顿和密集爆点。视频播放时通过 requestAnimationFrame 平滑驱动波形播放线,同时同步高亮并滚动当前句;点击音频波形或字幕行会跳转原视频时间。音频结果下方是信息流复刻分镜工作台:顶部产品参考区是“同一产品素材池”,不限量上传产品图,不做不同产品身份判断;上传原图推荐长边 1200-2000px、短边至少 600px,但后端会统一生成最长边 1600px、JPEG 92 的 AI 工作副本,并回显尺寸、自动转换和风险标注;上传后自动识别正面/左右 45 度/厚度/内侧触点/背底等视角,并标注背景类型、用途标签、生成风险和备注,用户只检查备注,鼠标悬停通过固定浮层显示大图预览,能盖过滚动容器和分镜框架;缺视角补图失败时保留重试入口。每条音频分镜纵向排列,行内从左到右串起原内容、新口播文案、画面规划/产品融入、参考帧/关键元素和 6 个候选视频槽。单条生成会从产品素材池按分镜角色、视角优先级、用途标签、置信度和风险自动挑选最多 6 张相关产品图,不会把全部产品图提交给生视频模型,然后复用现有生视频接口提交 Seedance 候选。旧分镜卡、抽帧控制和视频生成组件仍保留在文件里,但当前主路径不渲染。 + web/components/ad-recreation-board.tsx信息流广告复刻工作表:左侧素材输入;右侧展示视频下载状态、默认折叠的音频文案依据,以及统一的音频解析结果面板;面板顶部是一行讲话人/节奏/背景音摘要,下方左侧为原视频播放器、右侧为逐句时间轴,底部横向音频波形用参考图式的连续灰色包络显示响度、停顿和密集爆点。视频播放时通过 requestAnimationFrame 平滑驱动波形播放线,同时同步高亮并滚动当前句;点击音频波形或字幕行会跳转原视频时间。音频结果下方是信息流复刻分镜工作台:顶部产品参考区是“同一产品素材池”,不限量上传产品图,不做不同产品身份判断;上传原图推荐长边 1200-2000px、短边至少 600px,但后端会统一生成最长边 1600px、JPEG 92 的 AI 工作副本,并回显尺寸、自动转换和风险标注;上传后按“套在脖子上的 U 形肩颈按摩仪”进行同一产品批量识别,左/右按佩戴者身体左右、上/下按佩戴方向,额外标注内外侧、开口方向、局部结构点、背景类型、用途标签、生成风险和备注,用户只检查备注,鼠标悬停通过固定浮层显示大图预览,能盖过滚动容器和分镜框架;缺视角补图失败时保留重试入口。每条音频分镜纵向排列,行内从左到右串起原内容、新口播文案、画面规划/产品融入、参考帧/关键元素和 6 个候选视频槽。单条生成会从产品素材池按分镜角色、视角优先级、用途标签、置信度和风险自动挑选最多 6 张相关产品图,不会把全部产品图提交给生视频模型,然后把产品坐标系、视角标注、方向、结构点和风险写入 Seedance 提示。旧分镜卡、抽帧控制和视频生成组件仍保留在文件里,但当前主路径不渲染。 web/app/login/page.tsx生产登录页:访问账号/访问密钥表单、保持登录、错误/成功状态;当前只在原版 Digital Oasis 动态背景上叠加一个组合登录框,桌面端左侧是动态角色,右侧是图标化登录表单;面板左上角展示官网 SKG 字标和中文“营销内容工作台”系统标识。 web/app/login/layout.tsx登录路由专属 layout:覆盖全站默认网页标题和描述为空,避免 /login 继承工作台 metadata 后在页面源码里继续出现登录界面文字以外的文案。 web/components/login/oasis-canvas.tsx登录页全屏动态视觉层:用 iframe 直接承载下载包 web/public/oasis-source/index.html 的原 WebGPU / Three.js 草场源码;父级登录页只覆盖自己的文案和表单,并在捕获阶段把全局鼠标坐标同时用原生事件和 postMessage 转发给 iframe,避免登录面板或输入框遮挡时草地失去鼠标响应。 @@ -791,6 +791,26 @@ SubjectAsset { actions[], warnings[], normalized +} + +
+

ProductViewAnalysisItem

+

产品素材池识别结果。它不判断不同产品身份,只服务同一款挂脖肩颈按摩仪的生视频选图和方向约束;左/右按佩戴者身体左右,不按图片左右。

+
ProductViewAnalysisItem {
+  index,
+  view: front | left_45 | right_45 | side_thickness | inner_contacts | back_bottom,
+  background,
+  use_tags[],
+  orientation: {
+    product_left, product_right,
+    top, bottom,
+    inner_side, outer_side,
+    opening_direction
+  },
+  landmarks[],
+  note,
+  risk,
+  confidence
 }
@@ -853,7 +873,7 @@ SubjectAsset { 首尾帧资产POST /frames/{idx}/scene-assetgenerateSceneAsset同一接口兼容旧场景图和新首尾帧;新流程传 asset_role=first_frame/last_frame,后端走文字生图,参考帧只用于理解透明骨架人形象、比例、机位和光线,生成结果仍保存在 scene_assets 并自动填入产品融合镜头。 产品图库GET /product-library/skglistProductLibrary读取内置 SKG 白底图库 manifest,返回产品标题、品类、尺寸、白底评分和预览图 URL。 产品图入库到 jobPOST /jobs/{id}/assetsPOST /jobs/{id}/assets/product-libraryuploadStoryboardAssetcopyProductLibraryAsset上传产品图或把内置产品图库条目复制为当前 job 的普通 asset。后端统一生成最长边 1600px、JPEG 92 的 AI 工作副本,透明底铺白,过大/过小图片会在 ImageRef.asset_meta 里返回转换动作和风险;黑底/白底背景本身不强行转换。 - 产品视角识别POST /jobs/{id}/assets/product-views/analyzeanalyzeProductViews读取同一产品素材池,不限制只看前 6 张;自动分类为正面、左右 45 度、侧面厚度、内侧触点或背面/底部,并返回背景类型、用途标签、中文视角备注、生成风险和置信度;前端不再要求用户手动选择视角,也不做不同产品身份判断。 + 产品视角识别POST /jobs/{id}/assets/product-views/analyzeanalyzeProductViews读取同一产品素材池,按批次把多张图一次性提交给视觉模型,不限制只看前 6 张;识别对象被固定为套在脖子上的 U 形肩颈按摩仪。返回 viewbackgrounduse_tagsorientationlandmarks、中文备注、生成风险和置信度;orientation 明确佩戴者左/右、上/下、内外侧和开口方向对应图中哪边,避免把图片左右误当产品左右。前端不再要求用户手动选择视角,也不做不同产品身份判断。 产品缺角度补图POST /jobs/{id}/assets/product-anglegenerateProductAngleAsset用当前产品白底图作为参考,通过图像模型自动补全缺失视角,输出新的 ImageRef(kind="asset")。Prompt 会约束白底产品图、左右非对称、厚度、内侧触点和肩颈真实佩戴比例;前端只在自动补图失败时暴露重试入口。 角色库GET /character-library/skglistCharacterLibrary读取内置 5 个透明骨架人角色 manifest,每个角色含正面、左右 45 度、侧面、背面、半身近景和背部特写 7 张参考图。 角色图入库到 jobPOST /jobs/{id}/assets/character-librarycopyCharacterLibraryAssets把所选角色的 7 张参考图复制为当前 job asset,返回 subject_images,产品融合生成视频时作为人物身份参考图提交。 @@ -964,6 +984,19 @@ SubjectAsset {

变更记录

这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。

+
+
+

2026-05-17 · 产品图识别加入肩颈按摩仪坐标系

+ API + UI + Workflow +
+
+

问题:旧识别只要求模型在“正面/左45/右45/侧厚/触点/背底”里选一类,没有说明这是套在脖子上的 U 形肩颈按摩仪,也没有定义佩戴者左/右、上/下、贴颈内侧和外壳外侧,容易把图片左右当成产品左右,后续生视频会把局部细节和方向搞乱。

+

改动:api/main.pyanalyzeProductViews 改为同一产品多图批量识别,prompt 固定产品为挂脖肩颈按摩仪,并新增 orientationlandmarks 输出;单图兜底 prompt 也同步要求产品坐标系。web/lib/api.ts 扩展 ProductViewAnalysisItemProductReferenceCard 展示“方向已识别”和结构点,悬停预览显示方向、结构和风险。

+

影响:单条视频生成时,buildStoryboardSceneFromAudioRow 会把“左/右按佩戴者身体左右,上/下按佩戴方向,内侧=贴颈触点,外侧=外壳按键”作为硬规则写入产品提示,并把每张选中产品图的方向和结构点一并交给视频模型。

+
+

2026-05-17 · 产品图悬停预览改为大尺寸顶层浮层

diff --git a/web/components/ad-recreation-board.tsx b/web/components/ad-recreation-board.tsx index 04137dc..14a019f 100644 --- a/web/components/ad-recreation-board.tsx +++ b/web/components/ad-recreation-board.tsx @@ -1214,6 +1214,8 @@ function AudioStoryboardPlanPanel({ analysis?.note, analysis?.background ?? "unknown", analysis?.use_tags, + analysis?.orientation, + analysis?.landmarks, analysis?.risk ?? "", analysis?.confidence, ) @@ -1236,7 +1238,7 @@ function AudioStoryboardPlanPanel({ }) working = [ ...working, - createProductRefItem(ref, working.length, "ai", slot.value, `AI 补齐:${slot.hint}`, "white", undefined, "", 1), + createProductRefItem(ref, working.length, "ai", slot.value, `AI 补齐:${slot.hint}`, "white", undefined, undefined, undefined, "", 1), ] setProductItems(working) } catch (e) { @@ -1338,7 +1340,7 @@ function AudioStoryboardPlanPanel({ target_view: slot.label, note: slot.hint, }) - setProductItems((prev) => [...prev, createProductRefItem(ref, prev.length, "ai", slot.value, `AI 补齐:${slot.hint}`, "white", undefined, "", 1)]) + setProductItems((prev) => [...prev, createProductRefItem(ref, prev.length, "ai", slot.value, `AI 补齐:${slot.hint}`, "white", undefined, undefined, undefined, "", 1)]) toast.success(`AI 已补全产品视角:${slot.label}`) } catch (e) { toast.error("AI 补角度失败:" + (e instanceof Error ? e.message : String(e))) @@ -1394,7 +1396,7 @@ function AudioStoryboardPlanPanel({ )}

- 推荐原图长边 1200-2000px、短边至少 600px;超出也能上传,系统会自动生成最长边 1600px、JPEG 92 的轻量 AI 工作副本。黑底/白底保留,透明底铺白;超高清不会更稳,低分辨率会放大并标注风险。每条视频只挑最多 {MAX_PRODUCT_REFS_PER_VIDEO} 张相关产品图。 + 产品固定按“套在脖子上的 U 形肩颈按摩仪”识别;左/右按佩戴者身体左右,上/下按佩戴方向,不按图片左右。推荐原图长边 1200-2000px、短边至少 600px;系统会生成最长边 1600px、JPEG 92 的 AI 工作副本。每条视频只挑最多 {MAX_PRODUCT_REFS_PER_VIDEO} 张相关产品图。

@@ -1559,6 +1561,7 @@ function ProductReferenceCard({ const tagLabels = item.useTags.map((tag) => PRODUCT_USE_TAG_LABELS[tag]).filter(Boolean) const assetWarnings = item.assetMeta?.warnings ?? [] const assetActions = item.assetMeta?.actions ?? [] + const orientationText = formatProductOrientation(item.orientation) const [previewPos, setPreviewPos] = useState<{ left: number; top: number } | null>(null) function updatePreviewPosition(event: ReactMouseEvent) { @@ -1590,6 +1593,8 @@ function ProductReferenceCard({ {productViewLabel(item.view)} · {productBackgroundLabel(item.background)} · {tagLabels.join(" / ")}
{item.note} + {orientationText ? <>
方向:{orientationText} : null} + {item.landmarks.length ? <>
结构:{item.landmarks.join(" / ")} : null} {item.risk ? <>
风险:{item.risk} : null} {assetWarnings.length ? <>
规格:{assetWarnings.join(";")} : null}
@@ -1623,13 +1628,17 @@ function ProductReferenceCard({ {tagLabels.slice(0, 3).map((tag) => ( {tag} ))} + {orientationText ? 方向已识别 : null} + {item.landmarks.slice(0, 2).map((landmark) => ( + {landmark} + ))} {item.risk || assetWarnings.length ? 需留意 : null} {assetActions.length ? 已标准化 : null} onPatch({ note: event.target.value })} - placeholder="检查备注:结构差异、触点、尺寸比例" + placeholder="检查备注:佩戴者左/右、上/下、触点、尺寸比例" className="mt-1 h-8 w-full rounded-md border border-white/10 bg-black/35 px-2 text-[11px] text-white outline-none placeholder:text-white/25 focus:border-cyan-300/50" />
{item.ref.label || "产品参考图"}