修复序列帧去背景队列与幂等发布
序列帧去背景改用本地绿幕算法并在 durable 父任务内并行处理。 将去背景操作加入生成结果白名单,并按 operation_id 隔离帧对象路径。 去背景结果发布为新的序列资源、素材和画布图层,同时保留原始序列。 同步前端生成占位、相关测试及队列与资源契约文档。
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
closeGenerateComposerDialog,
|
||||
createAudioRedrawGenerationDialogDraft,
|
||||
createBackgroundMusicGenerationDialogDraft,
|
||||
createCharacterAnimationBackgroundRemovalDialogDraft,
|
||||
createCharacterAnimationGenerationDialogDraft,
|
||||
createCharacterAnimationPanelDraft,
|
||||
createCharacterGenerationDialogDraft,
|
||||
@@ -559,6 +560,33 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a separate character animation placement for background removal', () => {
|
||||
expect(
|
||||
createCharacterAnimationBackgroundRemovalDialogDraft({
|
||||
sourceLayer: createLayer({
|
||||
id: 'layer-animation',
|
||||
assetKind: 'character-animation',
|
||||
}),
|
||||
assetLabel: '源动画 去背景',
|
||||
}),
|
||||
).toMatchObject({
|
||||
mode: 'character-animation',
|
||||
prompt: '去除背景',
|
||||
assetLabel: '源动画 去背景',
|
||||
status: 'generating',
|
||||
composerOpen: false,
|
||||
sourceLayerId: 'layer-animation',
|
||||
placeholder: {
|
||||
x: 472,
|
||||
y: 140,
|
||||
width: 320,
|
||||
height: 240,
|
||||
originalWidth: 1024,
|
||||
originalHeight: 768,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('restores remodel prompts from user-visible generation input snapshots only', () => {
|
||||
const cases: Array<{
|
||||
fields: NonNullable<CanvasLayer['generationInputs']>['fields'];
|
||||
|
||||
@@ -869,9 +869,7 @@ function getNormalizedString(
|
||||
function getNormalizedStringArray(sourceLayer: CanvasLayer, id: string) {
|
||||
return (sourceLayer.generationInputs?.fields ?? [])
|
||||
.filter((field) => field.id === id)
|
||||
.flatMap((field) =>
|
||||
typeof field.value === 'string' ? [field.value] : [],
|
||||
);
|
||||
.flatMap((field) => (typeof field.value === 'string' ? [field.value] : []));
|
||||
}
|
||||
|
||||
function getNormalizedNumber(
|
||||
@@ -2196,6 +2194,33 @@ export function createQuickEditGenerationDialogDraft({
|
||||
};
|
||||
}
|
||||
|
||||
export function createCharacterAnimationBackgroundRemovalDialogDraft({
|
||||
sourceLayer,
|
||||
assetLabel,
|
||||
status = 'generating',
|
||||
}: {
|
||||
sourceLayer: CanvasLayer;
|
||||
assetLabel?: string;
|
||||
status?: CanvasGenerationDialogState['status'];
|
||||
}): Omit<CanvasGenerationDialogState, 'id'> {
|
||||
return {
|
||||
mode: 'character-animation',
|
||||
prompt: '去除背景',
|
||||
assetLabel,
|
||||
status,
|
||||
composerOpen: false,
|
||||
sourceLayerId: sourceLayer.id,
|
||||
placeholder: {
|
||||
x: sourceLayer.x + sourceLayer.width + 32,
|
||||
y: sourceLayer.y,
|
||||
width: sourceLayer.width,
|
||||
height: sourceLayer.height,
|
||||
originalWidth: sourceLayer.originalWidth || sourceLayer.width,
|
||||
originalHeight: sourceLayer.originalHeight || sourceLayer.height,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createLegacyImageRedrawGenerationDialogDraft({
|
||||
sourceLayer,
|
||||
canvasSize,
|
||||
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
closeGenerateComposerDialog,
|
||||
createAudioRedrawGenerationDialogDraft,
|
||||
createBackgroundMusicGenerationDialogDraft,
|
||||
createCharacterAnimationBackgroundRemovalDialogDraft,
|
||||
createCharacterAnimationGenerationDialogDraft,
|
||||
createCharacterGenerationDialogDraft,
|
||||
createEditDialogDraft,
|
||||
@@ -3697,13 +3698,33 @@ export function useImageCanvasGenerationWorkflow({
|
||||
setMetadataLayer(null);
|
||||
setCropExpandPanel(null);
|
||||
setQuickEditPanel(null);
|
||||
const assetLabel = `${sourceLayer.title} 去背景`;
|
||||
const backgroundRemovalDialog = applyProjectSnapshot
|
||||
? createCharacterAnimationBackgroundRemovalDialogDraft({
|
||||
sourceLayer,
|
||||
assetLabel,
|
||||
})
|
||||
: null;
|
||||
const backgroundRemovalPlacement = backgroundRemovalDialog
|
||||
? openPlacedCanvasGenerationDialog(backgroundRemovalDialog)
|
||||
: undefined;
|
||||
const backgroundRemovalDialogId = backgroundRemovalPlacement?.dialogId;
|
||||
try {
|
||||
const result = await removeEditorCharacterAnimationBackground({
|
||||
projectId,
|
||||
sourceLayerId: sourceLayer.id,
|
||||
sourceResourceId: sourceLayer.resourceId,
|
||||
assetFolderId,
|
||||
assetLabel: `${sourceLayer.title} 去背景`,
|
||||
assetLabel,
|
||||
...(backgroundRemovalPlacement?.placeholder
|
||||
? {
|
||||
canvasCompletion: {
|
||||
dialogId: backgroundRemovalDialogId,
|
||||
title: assetLabel,
|
||||
placeholder: backgroundRemovalPlacement.placeholder,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
generationInputs: buildDeterministicGenerationInputs(
|
||||
'character-animation.remove-background',
|
||||
sourceLayer,
|
||||
@@ -3712,10 +3733,17 @@ export function useImageCanvasGenerationWorkflow({
|
||||
await applyQueuedEditorGenerationProject(
|
||||
result,
|
||||
projectId,
|
||||
applyProjectSnapshot,
|
||||
(project) =>
|
||||
applyProjectSnapshot?.(project, {
|
||||
type: 'remove-background',
|
||||
count: 1,
|
||||
}),
|
||||
refreshTaskListForQueuedGeneration,
|
||||
onWalletBalanceMayHaveChanged,
|
||||
showGenerationWarning,
|
||||
backgroundRemovalDialogId,
|
||||
(project) =>
|
||||
preserveSourceLayerInProjectSnapshot(project, sourceLayer),
|
||||
);
|
||||
try {
|
||||
await Promise.resolve(refreshAssetLibrary?.());
|
||||
@@ -3723,6 +3751,19 @@ export function useImageCanvasGenerationWorkflow({
|
||||
// 服务端已原子落库成功;列表刷新失败不能把成功事实伪装成去背景失败。
|
||||
}
|
||||
} catch (error) {
|
||||
if (backgroundRemovalDialogId) {
|
||||
updateCanvasGenerationDialogById(
|
||||
backgroundRemovalDialogId,
|
||||
(dialog) => ({
|
||||
...dialog,
|
||||
status: 'failed',
|
||||
errorMessage:
|
||||
error instanceof Error && error.message.trim()
|
||||
? error.message
|
||||
: '去除序列帧背景失败',
|
||||
}),
|
||||
);
|
||||
}
|
||||
window.alert(
|
||||
error instanceof Error && error.message.trim()
|
||||
? error.message
|
||||
@@ -3747,6 +3788,7 @@ export function useImageCanvasGenerationWorkflow({
|
||||
assetFolderId,
|
||||
closeGenerationTransientState,
|
||||
onWalletBalanceMayHaveChanged,
|
||||
openPlacedCanvasGenerationDialog,
|
||||
projectId,
|
||||
refreshAssetLibrary,
|
||||
refreshTaskListForQueuedGeneration,
|
||||
@@ -3755,6 +3797,7 @@ export function useImageCanvasGenerationWorkflow({
|
||||
setMetadataLayer,
|
||||
setQuickEditPanel,
|
||||
showGenerationWarning,
|
||||
updateCanvasGenerationDialogById,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user