387a1c26e3
清理已跟踪原始日志与个人本地配置 修复前后端有效门禁、测试和构建问题 补齐生产 Jenkins、SpacetimeDB 本地命令与文档约束 收紧图片编辑器状态、附件与媒体引用测试 排除已下线旧创作入口测试并清理 warning
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import type { CharacterAnimationGenerationPayload } from '../asset-studio/characterAssetWorkflowPersistence';
|
|
import { generateCharacterAnimationDraft } from '../asset-studio/characterAssetWorkflowPersistence';
|
|
import type {
|
|
CustomWorldAiActionConfig,
|
|
EditableCustomWorldRole,
|
|
} from './roleAssetStudioModel';
|
|
import { roleAssetStudioPublishClient } from './roleAssetStudioPublishClient';
|
|
|
|
export function useRoleAnimationWorkflow() {
|
|
const generateAnimationClipForRole = async (params: {
|
|
actionConfig: CustomWorldAiActionConfig;
|
|
animationPromptText: string;
|
|
characterBriefText: string;
|
|
role: EditableCustomWorldRole;
|
|
}) => {
|
|
const { actionConfig, animationPromptText, characterBriefText, role } =
|
|
params;
|
|
|
|
if (!role.imageSrc || !role.generatedVisualAssetId) {
|
|
throw new Error('请先生成角色形象,再生成动作。');
|
|
}
|
|
|
|
const result = await generateCharacterAnimationDraft({
|
|
characterId: role.id,
|
|
strategy: 'image-to-video',
|
|
animation: actionConfig.animation,
|
|
promptText: animationPromptText,
|
|
characterBriefText,
|
|
actionTemplateId: actionConfig.templateId,
|
|
visualSource: role.imageSrc,
|
|
referenceImageDataUrls: [],
|
|
referenceVideoDataUrls: [],
|
|
lastFrameImageDataUrl: role.imageSrc,
|
|
frameCount: actionConfig.frameCount,
|
|
fps: actionConfig.fps,
|
|
durationSeconds: actionConfig.durationSeconds,
|
|
loop: actionConfig.loop,
|
|
useChromaKey: true,
|
|
resolution: '480p',
|
|
ratio: '1:1',
|
|
imageSequenceModel: 'wan2.7-image-pro',
|
|
videoModel: 'doubao-seedance-2-0-fast-260128',
|
|
referenceVideoModel: 'wan2.7-r2v',
|
|
motionTransferModel: 'wan2.2-animate-move',
|
|
} satisfies CharacterAnimationGenerationPayload);
|
|
|
|
if (result.strategy !== 'image-to-video') {
|
|
throw new Error('当前自定义世界动作工坊只支持图生视频方案。');
|
|
}
|
|
|
|
return {
|
|
fps: actionConfig.fps,
|
|
loop: actionConfig.loop,
|
|
frameWidth: 192,
|
|
frameHeight: 256,
|
|
frameCount: actionConfig.frameCount,
|
|
applyChromaKey: true,
|
|
sampleStartRatio: actionConfig.loop ? 0.12 : 0,
|
|
sampleEndRatio: actionConfig.loop ? 0.94 : 1,
|
|
previewVideoPath: result.previewVideoPath,
|
|
};
|
|
};
|
|
|
|
const publishAnimationClipForRole = async (params: {
|
|
actionConfig: CustomWorldAiActionConfig;
|
|
clip: Awaited<ReturnType<typeof generateAnimationClipForRole>>;
|
|
role: EditableCustomWorldRole;
|
|
}) => {
|
|
const { actionConfig, clip, role } = params;
|
|
|
|
if (!role.generatedVisualAssetId) {
|
|
throw new Error('缺少角色主图资产,无法发布动作。');
|
|
}
|
|
|
|
return roleAssetStudioPublishClient.publishCharacterAnimationAssets({
|
|
characterId: role.id,
|
|
visualAssetId: role.generatedVisualAssetId,
|
|
animations: {
|
|
[actionConfig.animation]: {
|
|
framesDataUrls: [],
|
|
fps: clip.fps,
|
|
loop: clip.loop,
|
|
frameWidth: clip.frameWidth,
|
|
frameHeight: clip.frameHeight,
|
|
frameCount: clip.frameCount,
|
|
applyChromaKey: clip.applyChromaKey,
|
|
sampleStartRatio: clip.sampleStartRatio,
|
|
sampleEndRatio: clip.sampleEndRatio,
|
|
previewVideoPath: clip.previewVideoPath,
|
|
},
|
|
},
|
|
updateCharacterOverride: false,
|
|
});
|
|
};
|
|
|
|
return {
|
|
generateAnimationClipForRole,
|
|
publishAnimationClipForRole,
|
|
};
|
|
}
|