From dcf77ad7a8a1f8e703a5c95ee7360bb7d0f5363f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 5 Aug 2026 15:22:24 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E9=9F=B3=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=97=B6=E9=95=BF=E5=B1=95=E7=A4=BA=E5=85=83=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除 EditorAsset、CanvasLayer 与画布布局中的通用 durationSeconds 链路。 将上传及生成请求时长统一写入 generationInputs.fields,并由服务端去重补齐。 音频播放只使用 loadedmetadata,素材详情、导出和精选成本读取展示字段。 补充前后端回归测试并同步架构文档与共享决策。 --- .../shared-memory/decision-log.md | 2 +- ...】server-rs与SpacetimeDB数据契约-2026-05-15.md | 2 +- .../src/character_animation_assets.rs | 5 +- .../crates/api-server/src/editor_project.rs | 99 ++++++++++++++++++- .../generation.rs | 8 +- .../creationShowcaseModel.test.ts | 29 ++++++ .../creation-home/creationShowcaseModel.ts | 11 ++- .../ImageCanvasEditorModel.test.ts | 13 ++- .../image-editor/ImageCanvasEditorModel.ts | 7 -- .../image-editor/ImageCanvasEditorTypes.ts | 2 - .../ImageCanvasEditorView.test.tsx | 2 +- .../ImageCanvasExportModel.test.ts | 4 +- .../image-editor/ImageCanvasExportModel.ts | 21 ++-- .../ImageCanvasGenerationDialogModel.test.ts | 4 +- .../ImageCanvasGenerationDialogModel.ts | 5 +- .../ImageCanvasGenerationLayerModel.test.ts | 16 ++- .../ImageCanvasGenerationLayerModel.ts | 2 - .../ImageCanvasGenerationModel.test.ts | 2 +- .../ImageCanvasGenerationModel.ts | 8 +- ...ageCanvasGenerationSubmissionModel.test.ts | 8 +- .../ImageCanvasGenerationSubmissionModel.ts | 1 + .../image-editor/ImageCanvasMediaModel.ts | 30 ------ .../ImageCanvasMetadataModalView.test.tsx | 10 +- .../ImageCanvasMetadataModalView.tsx | 17 +--- .../ImageCanvasUploadModel.test.ts | 14 +++ .../image-editor/ImageCanvasUploadModel.ts | 28 +++++- .../ImageCanvasWorldView.test.tsx | 20 +++- .../image-editor/ImageCanvasWorldView.tsx | 19 +--- ...anvasGenerationSubmissionWorkflow.test.tsx | 2 +- ...ImageCanvasGenerationSubmissionWorkflow.ts | 1 + .../useImageCanvasGenerationWorkflow.test.tsx | 4 +- .../useImageCanvasGenerationWorkflow.ts | 1 - .../useImageCanvasUploadWorkflow.test.tsx | 13 +++ .../useImageCanvasUploadWorkflow.ts | 8 ++ 34 files changed, 295 insertions(+), 123 deletions(-) diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index ae2538f78..83fa87b86 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -5809,7 +5809,7 @@ - 决策:`assetKind` 是编辑器资源和素材唯一权威媒体类别;不新增或返回 `mediaType`。`character-animation` 渲染为序列帧,`video` 渲染为视频,`audio/sound-effect/background-music` 渲染为音频,其余类别渲染为图片。前端内部可保留派生的 `CanvasMediaType` 选择渲染器,但不能把它作为后端事实。 - 持久化边界:`editor_project_resource` 与 `editor_asset` 表尾只保存 `image_sequence_frames_json` 与 `image_sequence_duration_ms`,默认均为 `None`;`editor_showcase_asset` 作为提交时冻结的审核与公开快照,同样在表尾保存这两个字段并从账号素材逐字段复制,旧行默认均为 `None`。正式帧对象和角色动作生成响应都不保存或返回 `frameIndex`,数组位置是唯一播放顺序;帧数取数组长度,FPS 由帧数和毫秒时长即时推导,不持久化 `frame_count`、`fps` 或通用 `duration_seconds`。后端处理抽帧和逐帧去背时仍保留内部 `frame_index`,仅用于乱序并发收口、OSS 命名、日志和错误定位;仓库内旧 helper 同步升级,不为它保留外部兼容字段。 -- 时长口径:`image_sequence_duration_ms` 只表示角色图片序列完整播放一次的毫秒时长,与音频 / 视频生成请求中的 `durationSeconds` 完全分离。角色动作与视频生成响应仍可携带各自既有的请求 / 结果级 `frameCount/fps/durationSeconds`,音视频画布布局也可保留既有运行时秒数;不得为音频生成响应新增 `durationSeconds`,也不得把任何音视频秒数写入资源 / 素材或图片序列字段。前端播放器使用 `imageSequenceDurationMs / imageSequenceFrames.length`,Spine 导出时才换算秒数并推导 FPS。 +- 时长口径:`image_sequence_duration_ms` 只表示角色图片序列完整播放一次的毫秒时长,与音频 / 视频生成请求中的 `durationSeconds` 完全分离。角色动作与视频生成响应仍可携带各自既有的请求 / 结果级 `frameCount/fps/durationSeconds`;通用音视频秒数不进入资源 / 素材正式列、`EditorAsset`、`CanvasLayer` 或画布 layout,只允许把上传探测值或生成请求值格式化为用户可见字符串后写入 `generation_inputs_json.fields[]` 供素材详情展示。素材放置与工程恢复不做媒体探测,也不从 layout / resource 回退;音频播放只使用媒体 `loadedmetadata.duration`。不得为音频生成响应新增 `durationSeconds`,也不得把音视频秒数写入图片序列字段。前端角色动作播放器使用 `imageSequenceDurationMs / imageSequenceFrames.length`,Spine 导出时才换算秒数并推导 FPS。 - 写入与复用边界:`assetKind=character-animation` 必须在 SpacetimeDB storage/procedure 边界同时提供至少两帧有效数组与大于 0 的图片序列毫秒时长;其他类别不得携带图片序列字段。同项目同源同媒体资源只允许 `None → Some` 单调回填,非空冲突失败关闭,延迟重试的 `updated_at` 取请求时间与既有时间的较大值。 - 生成关联边界:角色动作生成响应同时返回已经持久化的最终 `resource` / `asset`;带项目上下文时前端结果图层必须直接使用 `resource.resourceId` 及其预览视频来源血缘,缺少 resource 直接失败。无项目放置也必须绑定响应中的正式账号素材,不能从响应 `frames` 构造无资产真相的本地动作结果。 - repair 幂等边界:legacy 音频 repair 不再派生或回填任何资源级通用时长,因此新列上线前已完成 repair 的重放继续按原字段精确匹配;图片序列字段在音频行上均为 `None`。 diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index ba7230363..613415146 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -429,7 +429,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 - `asset_kind` 是资源 / 素材唯一权威媒体类别,不新增或返回 `media_type` / `mediaType`。角色动作预览 MP4 使用 `asset_kind = video`,最终透明帧集使用 `asset_kind = character-animation`;前端据此派生具体渲染器。 - `editor_project_resource` 与 `editor_asset` 表尾只追加 `image_sequence_frames_json: Option` 和 `image_sequence_duration_ms: Option`;`editor_showcase_asset` 作为提交时冻结的审核与公开快照,也在表尾追加并从账号素材复制相同两字段。前者保存完整有效帧数组,数组位置是唯一播放顺序,正式帧对象不保存或返回 `frameIndex`;后者只表示该图片序列完整播放一次的毫秒时长。帧数始终取数组长度,FPS 在播放或导出时即时推导,不持久化 `frame_count` 或 `fps`。 -- 图片序列时长不能复用音频 / 视频生成请求的 `durationSeconds`,资源和素材也不保存通用 `duration_seconds`。角色动作与视频生成响应保留各自既有的请求 / 结果级秒数,音频 / 视频图层的既有运行时秒数可留在画布布局中;音频生成响应不得新增 `durationSeconds`,音频实际时长仍可由 `