统一普通图片上传素材类型语义
修复普通图片类型迁移漏改上传工作流断言的问题。 普通静态图片的项目资源与账号素材创建请求统一省略 assetKind。 补充上传回归断言并同步图片画布上传契约文档。
This commit is contained in:
@@ -210,7 +210,7 @@
|
||||
- 快速编辑 / 重绘站内 public 示例图、历史 generated 图或 OSS generated 图时,优先复用当前图层已有 `objectKey` / `resourceId` / `sourceAssetId`;尚未登记且没有稳定引用的浏览器本地图片或普通 public 图片路径都必须先上传并取得 objectKey。前端不得再把正式对象下载成 `data:image/*;base64,...` 后提交,也不得把 Data URL / Blob URL 写入外部生成持久任务 JSON;后端收到引用后统一做 owner 归属校验并签名读取。
|
||||
- 快速编辑不保留用户可添加的额外参考图入口;点击修改时把原图已登记业务 ID 作为 `/api/editor/images/edits` 的 `sourceReferenceId`,红框序号标注图上传后作为辅助 `referenceImageSrcs`。
|
||||
- 素材文件夹可以新建、折叠、重命名和删除;删除普通文件夹后,其素材移动到“项目素材”。普通上传默认落入“上传素材”文件夹;素材库缺少该文件夹时,前端在首次普通上传前创建一次并复用,拖到指定文件夹或点击指定文件夹上传时仍进入目标文件夹。
|
||||
- 上传按钮和拖拽上传都支持多文件;底部工具栏的上传入口选择文件后直接进入“上传素材”并在当前画布视口中心创建画布图层,素材栏文件夹内的上传入口只写入对应素材文件夹、不自动入画布;拖到文件夹或该文件夹内素材时进入目标文件夹;拖到画布时进入“上传素材”并在投放点创建画布图层。上传图片必须在创建占位素材、画布图层和账号级素材记录前先读取原图 Resolution,图层宽高、`originalWidth/originalHeight` 和素材库 `width/height` 都使用图片本身尺寸;上传视频同样在创建素材和图层前读取视频 metadata 宽高,保证单层下载或 ZIP 导出的真实视频文件重新导入后仍按文件自身尺寸入画布;仅在无法解析尺寸时才使用对应媒体兜底尺寸。OSS 直传确认成功后,即时占位素材、即时画布图层和补充图片尺寸探测必须使用本次换签得到的 signed URL,不得把私有对象的裸 `/generated-*` 兼容路径交给浏览器读取;账号素材持久化仍只写 `legacyPublicPath`、`objectKey` 和 `assetObjectId`,禁止把会过期的 signed URL 写入资源或素材记录。
|
||||
- 上传按钮和拖拽上传都支持多文件;底部工具栏的上传入口选择文件后直接进入“上传素材”并在当前画布视口中心创建画布图层,素材栏文件夹内的上传入口只写入对应素材文件夹、不自动入画布;拖到文件夹或该文件夹内素材时进入目标文件夹;拖到画布时进入“上传素材”并在投放点创建画布图层。上传图片必须在创建占位素材、画布图层和账号级素材记录前先读取原图 Resolution,图层宽高、`originalWidth/originalHeight` 和素材库 `width/height` 都使用图片本身尺寸;上传视频同样在创建素材和图层前读取视频 metadata 宽高,保证单层下载或 ZIP 导出的真实视频文件重新导入后仍按文件自身尺寸入画布;仅在无法解析尺寸时才使用对应媒体兜底尺寸。普通静态图片上传创建项目资源或账号素材时必须省略 `assetKind`,持久化为 `null/None`;`"image"` 只是前端 `CanvasMediaType`,不是 `CanvasAssetKind`。OSS 直传确认成功后,即时占位素材、即时画布图层和补充图片尺寸探测必须使用本次换签得到的 signed URL,不得把私有对象的裸 `/generated-*` 兼容路径交给浏览器读取;账号素材持久化仍只写 `legacyPublicPath`、`objectKey` 和 `assetObjectId`,禁止把会过期的 signed URL 写入资源或素材记录。
|
||||
- 音频 / 视频素材卡和画布媒体图层必须提供稳定的非文字视觉预览:优先使用 `thumbnailSrc` / 视频 `poster`,没有真实首帧或音频封面时使用由媒体类型、素材名和地址派生的确定性视觉底图。视频图层使用原生 `<video controls preload="metadata" playsInline>` 播放,外层图层仍承接选择和拖拽语义;音频图层播放前继续通过 `/api/assets/read-url` 换签。画布素材导出按 `mediaType` 保留真实媒体格式:图片进入 `images/`,音频 / 视频进入 `media/`,文件扩展名从响应 MIME、`objectKey` 或源 URL 推断,不得把音频 / 视频导出成 PNG。
|
||||
- 画布素材 ZIP 的 `metadata.json` 只保存前端信息弹窗和导出文件列表可见的展示快照:项目标题、导出时间、图层标题、文件路径、类型、生成输入、模型显示名、Task 短 ID、Object 显示值、Resolution / 时长和导出错误。导出的生成输入只保留用户实际填写或选择的内容;系统默认兜底提示词、固定工作流提示词、内置图标描述、UI 提取素材固定提示词等内置提示词即使存在于历史 `generationInputs`,也不得写入导出元数据。不得把 `projectId`、`layerId`、`resourceId`、`sourceAssetId`、`sourceResourceId`、原始 `prompt` / `actualPrompt` / `provider` 或画布坐标、锁定、隐藏等布局状态写入导出元数据;Object 字段仅沿用信息弹窗当前可见值。
|
||||
- `生成角色动作` 的完成结果按序列帧素材处理:`assetKind` 固定为 `character-animation`,图层主 `src` 使用首帧,前端内部据 `assetKind` 派生 `CanvasMediaType="image-sequence"` 选择播放器。`previewVideoPath` 只作为上游来源,不作为画布主媒体。下载和 ZIP 导出必须得到序列帧 ZIP / frames 目录,不能回退为预览视频或首帧 PNG;后端抽帧后逐帧保存带绿幕源图,上传时消费并释放原帧字节,再用该对象的 600 秒 OSS URL 完成透明化并落盘透明帧素材。
|
||||
|
||||
@@ -14,6 +14,8 @@ export function resolveUploadedAssetKind(
|
||||
case 'image-sequence':
|
||||
throw new Error('序列帧素材不支持按单个媒体文件上传');
|
||||
case 'image':
|
||||
// 普通静态图片没有 CanvasAssetKind;返回 undefined 让创建请求省略该字段,
|
||||
// 服务端和 SpacetimeDB 按 None 持久化。
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,6 +631,9 @@ describe('useImageCanvasUploadWorkflow', () => {
|
||||
assetObjectId: 'assetobj-editor-image',
|
||||
}),
|
||||
);
|
||||
expect(createEditorAssetMock.mock.calls[0]?.[0]).not.toHaveProperty(
|
||||
'assetKind',
|
||||
);
|
||||
expect(measuredImageSources).toContain(
|
||||
'https://signed.example.com/editor/image/画布素材.png',
|
||||
);
|
||||
@@ -784,9 +787,11 @@ describe('useImageCanvasUploadWorkflow', () => {
|
||||
width: 420,
|
||||
height: 315,
|
||||
sourceType: 'uploaded',
|
||||
assetKind: 'image',
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
createEditorProjectResourceMock.mock.calls[0]?.[1],
|
||||
).not.toHaveProperty('assetKind');
|
||||
expect(createEditorAssetMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -349,7 +349,7 @@ export function useImageCanvasUploadWorkflow({
|
||||
width,
|
||||
height,
|
||||
sourceType: 'uploaded',
|
||||
assetKind,
|
||||
...(assetKind !== undefined ? { assetKind } : {}),
|
||||
generationInputs,
|
||||
});
|
||||
return createUploadedGenerationReference({
|
||||
@@ -383,7 +383,7 @@ export function useImageCanvasUploadWorkflow({
|
||||
width,
|
||||
height,
|
||||
sourceType: 'uploaded',
|
||||
assetKind,
|
||||
...(assetKind !== undefined ? { assetKind } : {}),
|
||||
generationInputs,
|
||||
});
|
||||
return createUploadedGenerationReference({
|
||||
@@ -1411,7 +1411,9 @@ export function useImageCanvasUploadWorkflow({
|
||||
width: uploadedAsset.width,
|
||||
height: uploadedAsset.height,
|
||||
sourceType: 'uploaded',
|
||||
assetKind: nextLayer.assetKind,
|
||||
...(nextLayer.assetKind != null
|
||||
? { assetKind: nextLayer.assetKind }
|
||||
: {}),
|
||||
generationInputs: nextLayer.generationInputs,
|
||||
})
|
||||
.then((asset) => {
|
||||
|
||||
Reference in New Issue
Block a user