收紧编辑器媒体引用契约
禁止当前画布与外部编辑器接口接收 Data URL 和 Blob URL 生成请求统一先上传 OSS 并使用稳定资源引用 角色动画和视频签名前校验资源归属并支持资源与素材 ID 同步前后端 256KB 引用限制及相关文档测试
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { requestJson } from '../apiClient';
|
||||
import type { ExternalGenerationJobStatusRecord } from '../../../packages/shared/src/contracts/externalGeneration';
|
||||
import { requestJson } from '../apiClient';
|
||||
import { EDITOR_REQUEST_RETRY_OPTIONS } from './editorRetryOptions';
|
||||
|
||||
const EDITOR_PROJECT_API_BASE = '/api/editor/projects';
|
||||
@@ -25,8 +25,38 @@ const EDITOR_BACKGROUND_MUSIC_GENERATION_API =
|
||||
'/api/editor/audios/background-music/generations';
|
||||
const EDITOR_GENERATION_PRICING_API = '/api/editor/generation-pricing';
|
||||
const EDITOR_IMAGE_MODEL_NANOBANANA2 = 'gemini-3.1-flash-image-preview';
|
||||
const EDITOR_VIDEO_REFERENCE_REQUEST_LIMIT_BYTES = 256 * 1024;
|
||||
const DEFAULT_PROJECT_TITLE = '未命名画布';
|
||||
|
||||
function assertStableEditorMediaReference(value: string, fieldLabel: string) {
|
||||
const normalized = value.trimStart().toLowerCase();
|
||||
if (normalized.startsWith('data:') || normalized.startsWith('blob:')) {
|
||||
throw new Error(
|
||||
`${fieldLabel}必须先上传 OSS,并使用 objectKey、项目资源、素材 ID 或支持的 URL 引用。`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function assertStableEditorMediaReferences(
|
||||
values: readonly string[] | undefined,
|
||||
fieldLabel: string,
|
||||
) {
|
||||
values?.forEach((value) => assertStableEditorMediaReference(value, fieldLabel));
|
||||
}
|
||||
|
||||
function assertStableEditorVideoReferences(input: EditorVideoGenerationInput) {
|
||||
const references = [
|
||||
...(input.referenceImageSrcs ?? []),
|
||||
...(input.referenceVideoSrcs ?? []),
|
||||
...(input.referenceAudioSrcs ?? []),
|
||||
];
|
||||
assertStableEditorMediaReferences(references, '视频参考素材');
|
||||
const requestBytes = new TextEncoder().encode(references.join('')).byteLength;
|
||||
if (requestBytes > EDITOR_VIDEO_REFERENCE_REQUEST_LIMIT_BYTES) {
|
||||
throw new Error('视频参考素材稳定引用字段总长度不能超过 256KB');
|
||||
}
|
||||
}
|
||||
|
||||
export type EditorCanvasViewport = {
|
||||
x: number;
|
||||
y: number;
|
||||
@@ -874,6 +904,7 @@ export async function toggleEditorShowcaseAssetLike(
|
||||
}
|
||||
|
||||
export async function generateEditorImage(input: EditorImageGenerationInput) {
|
||||
assertStableEditorMediaReferences(input.referenceImageSrcs, '生成参考图');
|
||||
return requestJson<EditorImageGenerationResponse>(
|
||||
EDITOR_IMAGE_GENERATION_API,
|
||||
jsonRequest('POST', {
|
||||
@@ -913,6 +944,11 @@ export async function generateEditorImage(input: EditorImageGenerationInput) {
|
||||
export async function generateEditorIconSpritesheet(
|
||||
input: EditorIconSpritesheetGenerationInput,
|
||||
) {
|
||||
assertStableEditorMediaReference(input.referenceImageSrc, '图标素材规范');
|
||||
assertStableEditorMediaReferences(
|
||||
input.referenceImageSrcs,
|
||||
'图标素材参考图',
|
||||
);
|
||||
return requestJson<EditorIconSpritesheetGenerationResponse>(
|
||||
EDITOR_ICON_SPRITESHEET_GENERATION_API,
|
||||
jsonRequest('POST', {
|
||||
@@ -962,6 +998,8 @@ export async function splitEditorIconSpritesheet(
|
||||
export async function extractEditorUiDesignAssets(
|
||||
input: EditorUiDesignAssetExtractionInput,
|
||||
) {
|
||||
assertStableEditorMediaReference(input.sourceImageSrc, 'UI设计图');
|
||||
assertStableEditorMediaReferences(input.referenceImageSrcs, 'UI素材参考图');
|
||||
return requestJson<EditorIconSpritesheetGenerationResponse>(
|
||||
EDITOR_UI_DESIGN_ASSET_EXTRACTION_API,
|
||||
jsonRequest('POST', {
|
||||
@@ -995,6 +1033,8 @@ export async function extractEditorUiDesignAssets(
|
||||
}
|
||||
|
||||
export async function editEditorImage(input: EditorImageEditInput) {
|
||||
assertStableEditorMediaReference(input.sourceImageSrc, '待修改图片');
|
||||
assertStableEditorMediaReferences(input.referenceImageSrcs, '修改参考图');
|
||||
return requestJson<EditorImageGenerationResponse>(
|
||||
EDITOR_IMAGE_EDIT_API,
|
||||
jsonRequest('POST', {
|
||||
@@ -1033,6 +1073,7 @@ export async function editEditorImage(input: EditorImageEditInput) {
|
||||
export async function removeEditorImageBackground(
|
||||
input: EditorBackgroundRemovalInput,
|
||||
) {
|
||||
assertStableEditorMediaReference(input.sourceImageSrc, '待去除背景图片');
|
||||
return requestJson<EditorBackgroundRemovalResponse>(
|
||||
EDITOR_BACKGROUND_REMOVAL_API,
|
||||
jsonRequest('POST', {
|
||||
@@ -1064,6 +1105,7 @@ export async function removeEditorImageBackground(
|
||||
export async function generateEditorCharacterAnimation(
|
||||
input: EditorCharacterAnimationGenerationInput,
|
||||
) {
|
||||
assertStableEditorMediaReference(input.sourceImageSrc, '角色动画源图');
|
||||
return requestJson<EditorCharacterAnimationGenerationResult>(
|
||||
EDITOR_CHARACTER_ANIMATION_GENERATION_API,
|
||||
jsonRequest('POST', {
|
||||
@@ -1088,6 +1130,7 @@ export async function generateEditorCharacterAnimation(
|
||||
}
|
||||
|
||||
export async function generateEditorVideo(input: EditorVideoGenerationInput) {
|
||||
assertStableEditorVideoReferences(input);
|
||||
return requestJson<EditorVideoGenerationResponse>(
|
||||
EDITOR_VIDEO_GENERATION_API,
|
||||
jsonRequest('POST', {
|
||||
|
||||
Reference in New Issue
Block a user