Image editor: Seedance2.0 refs and audio changes
Adds Seedance 2.0 reference-media constraints and forbids video data URLs; updates docs/decision-log and pitfalls to record the new rules. Introduces typed audio params (type: one-shot|loop, tempo: number|null), generator-specific placeholder styles/avoidance and UI changes for generation panels. Implements related front-end changes (new crop/ raster edit components, many image-editor models, tests and workflow hooks) and server-side contract/handler updates across api-server, shared-contracts, platform-audio, platform-image and vector_engine_audio_generation to support the new submission/asset flow and validations.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
createEditorAsset,
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
deleteEditorAssetFolder,
|
||||
deleteEditorProject,
|
||||
editEditorImage,
|
||||
generateEditorCharacterAnimation,
|
||||
generateEditorBackgroundMusic,
|
||||
generateEditorCharacterAnimation,
|
||||
generateEditorIconSpritesheet,
|
||||
generateEditorImage,
|
||||
generateEditorSoundEffect,
|
||||
@@ -591,11 +591,13 @@ describe('editorProjectClient', () => {
|
||||
model: 'gemini-3.1-flash-image-preview',
|
||||
provider: 'VectorEngine',
|
||||
taskId: 'icon-spritesheet-task-1',
|
||||
priceMudPoints: 12,
|
||||
});
|
||||
|
||||
const result = await generateEditorIconSpritesheet({
|
||||
referenceImageSrc: 'data:image/png;base64,spec',
|
||||
iconDescriptions: ['返回按钮', '设置按钮'],
|
||||
priceMudPoints: 12,
|
||||
});
|
||||
|
||||
expect(result.taskId).toBe('icon-spritesheet-task-1');
|
||||
@@ -608,6 +610,7 @@ describe('editorProjectClient', () => {
|
||||
referenceImageSrc: 'data:image/png;base64,spec',
|
||||
iconDescriptions: ['返回按钮', '设置按钮'],
|
||||
model: 'gemini-3.1-flash-image-preview',
|
||||
priceMudPoints: 12,
|
||||
}),
|
||||
}),
|
||||
'生成图标素材失败',
|
||||
@@ -629,6 +632,7 @@ describe('editorProjectClient', () => {
|
||||
model: 'gpt-image-2',
|
||||
provider: 'VectorEngine',
|
||||
taskId: 'icon-spritesheet-task-2',
|
||||
priceMudPoints: 12,
|
||||
});
|
||||
|
||||
await generateEditorIconSpritesheet({
|
||||
@@ -637,6 +641,7 @@ describe('editorProjectClient', () => {
|
||||
model: 'gpt-image-2',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '2K',
|
||||
priceMudPoints: 12,
|
||||
});
|
||||
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
@@ -650,6 +655,7 @@ describe('editorProjectClient', () => {
|
||||
model: 'gpt-image-2',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '2K',
|
||||
priceMudPoints: 12,
|
||||
}),
|
||||
}),
|
||||
'生成图标素材失败',
|
||||
@@ -842,8 +848,12 @@ describe('editorProjectClient', () => {
|
||||
sound: 'off',
|
||||
priceMudPoints: 100,
|
||||
referenceImageSrcs: ['data:image/png;base64,image'],
|
||||
referenceVideoSrcs: ['data:video/mp4;base64,video'],
|
||||
referenceAudioSrcs: ['data:audio/mpeg;base64,audio'],
|
||||
referenceVideoSrcs: [
|
||||
'generated-character-drafts/editor/seedance-references/video/video.mp4',
|
||||
],
|
||||
referenceAudioSrcs: [
|
||||
'generated-character-drafts/editor/seedance-references/audio/audio.mp3',
|
||||
],
|
||||
});
|
||||
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
@@ -861,8 +871,12 @@ describe('editorProjectClient', () => {
|
||||
sound: 'off',
|
||||
priceMudPoints: 100,
|
||||
referenceImageSrcs: ['data:image/png;base64,image'],
|
||||
referenceVideoSrcs: ['data:video/mp4;base64,video'],
|
||||
referenceAudioSrcs: ['data:audio/mpeg;base64,audio'],
|
||||
referenceVideoSrcs: [
|
||||
'generated-character-drafts/editor/seedance-references/video/video.mp4',
|
||||
],
|
||||
referenceAudioSrcs: [
|
||||
'generated-character-drafts/editor/seedance-references/audio/audio.mp3',
|
||||
],
|
||||
}),
|
||||
}),
|
||||
'生成视频失败',
|
||||
@@ -887,8 +901,8 @@ describe('editorProjectClient', () => {
|
||||
|
||||
const result = await generateEditorSoundEffect({
|
||||
sound: '金币掉落叮当声',
|
||||
type: 'ui',
|
||||
tempo: 'fast',
|
||||
type: 'one-shot',
|
||||
tempo: 120,
|
||||
priceMudPoints: 10,
|
||||
});
|
||||
|
||||
@@ -900,8 +914,8 @@ describe('editorProjectClient', () => {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sound: '金币掉落叮当声',
|
||||
type: 'ui',
|
||||
tempo: 'fast',
|
||||
type: 'one-shot',
|
||||
tempo: 120,
|
||||
priceMudPoints: 10,
|
||||
}),
|
||||
}),
|
||||
@@ -913,6 +927,43 @@ describe('editorProjectClient', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps editor sound effect tempo as null when BPM is not selected', async () => {
|
||||
requestJsonMock.mockResolvedValueOnce({
|
||||
audioSrc: '/generated-character-drafts/editor-audios/sfx-null.mp3',
|
||||
width: 420,
|
||||
height: 120,
|
||||
sourceType: 'generated',
|
||||
prompt: '按钮确认短促音',
|
||||
actualPrompt: '按钮确认短促音',
|
||||
model: 'audio1.0',
|
||||
provider: 'VectorEngine',
|
||||
taskId: 'sound-task-null',
|
||||
priceMudPoints: 10,
|
||||
audioKind: 'sound-effect',
|
||||
});
|
||||
|
||||
await generateEditorSoundEffect({
|
||||
sound: '按钮确认短促音',
|
||||
type: 'one-shot',
|
||||
tempo: null,
|
||||
priceMudPoints: 10,
|
||||
});
|
||||
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
'/api/editor/audios/sound-effects/generations',
|
||||
expect.objectContaining({
|
||||
body: JSON.stringify({
|
||||
sound: '按钮确认短促音',
|
||||
type: 'one-shot',
|
||||
tempo: null,
|
||||
priceMudPoints: 10,
|
||||
}),
|
||||
}),
|
||||
'生成游戏音效失败',
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('generates editor background music through the backend BFF', async () => {
|
||||
requestJsonMock.mockResolvedValueOnce({
|
||||
audioSrc: '/generated-character-drafts/editor-audios/bgm.mp3',
|
||||
|
||||
Reference in New Issue
Block a user