Files
Genarrative/src/components/rpg-creation-asset-studio/useRoleAnimationWorkflow.ts
T
kdletters 387a1c26e3 完成全量检查整改
清理已跟踪原始日志与个人本地配置
修复前后端有效门禁、测试和构建问题
补齐生产 Jenkins、SpacetimeDB 本地命令与文档约束
收紧图片编辑器状态、附件与媒体引用测试
排除已下线旧创作入口测试并清理 warning
2026-07-17 16:56:46 +08:00

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,
};
}