diff --git a/apps/web/src/pages/generate/hooks/generate-video/buildPayload.ts b/apps/web/src/pages/generate/hooks/generate-video/buildPayload.ts new file mode 100644 index 000000000..6040cf875 --- /dev/null +++ b/apps/web/src/pages/generate/hooks/generate-video/buildPayload.ts @@ -0,0 +1,76 @@ +import type { UseGenerateVideoProps } from "./types" +import { buildVoiceConfig } from "./voiceConfig" + +/** + * 构建 updateEditPlan 的 payload + * 从 props 中提取需要的字段,组装成 API 所需的 config 结构 + */ +export const buildEditPlanPayload = (props: UseGenerateVideoProps) => { + const { + titleSettings, + selectedMaterials, + materialMode, + smartSelectedIds, + voiceMode, + selectedVoice, + selectedClonedVoice, + coverSettings, + videoRatio, + style, + duration, + autoSubtitles, + bgm, + generateCount, + } = props + + const voiceConfig = buildVoiceConfig({ + voiceMode, + selectedVoice, + selectedClonedVoice, + }) + + return { + name: titleSettings.title.trim(), + config: { + asset_ids: materialMode === "auto" ? smartSelectedIds : selectedMaterials, + title_config: { + ai_auto_select: titleSettings.aiAutoSelect, + content: titleSettings.title, + position: titleSettings.position, + font_preset: titleSettings.font, + font_color: titleSettings.color, + font_size: titleSettings.size, + }, + cover_config: coverSettings, + ...voiceConfig, + ratio: videoRatio, + style, + duration, + auto_subtitles: autoSubtitles, + bgm, + generate_count: generateCount, + material_mode: materialMode, + }, + total_duration: duration, + status: "editing" as const, + } +} + +/** + * 生成前置校验 + * 返回错误信息,通过则返回 null + */ +export const validateGenerateInputs = (props: UseGenerateVideoProps): string | null => { + const { titleSettings, materialMode, selectedMaterials, voiceMode, selectedClonedVoice } = props + + if (!titleSettings.title.trim()) { + return "请先选择或输入标题" + } + if (materialMode === "manual" && selectedMaterials.length === 0) { + return "请至少选择一个素材" + } + if (voiceMode === "clone" && !selectedClonedVoice) { + return "请先选择一个克隆音色" + } + return null +} diff --git a/apps/web/src/pages/generate/hooks/useGenerateVideo.ts b/apps/web/src/pages/generate/hooks/useGenerateVideo.ts index 1507d6e30..d78fdaa17 100644 --- a/apps/web/src/pages/generate/hooks/useGenerateVideo.ts +++ b/apps/web/src/pages/generate/hooks/useGenerateVideo.ts @@ -9,27 +9,11 @@ import { generateEditPlan, updateEditPlan, getEditPlan } from "@/api/template-ed import type { UseGenerateVideoProps } from "./generate-video/types" import { getGenerationPhase } from "./generate-video/phase" import { useGenerationPolling } from "./generate-video/useGenerationPolling" -import { buildVoiceConfig } from "./generate-video/voiceConfig" +import { buildEditPlanPayload, validateGenerateInputs } from "./generate-video/buildPayload" import { extractBackendError, translateError } from "./generate-video/errorUtils" export function useGenerateVideo(props: UseGenerateVideoProps) { - const { - titleSettings, - selectedTemplate, - selectedMaterials, - materialMode, - smartSelectedIds, - voiceMode, - selectedVoice, - selectedClonedVoice, - coverSettings, - videoRatio, - style, - duration, - autoSubtitles, - bgm, - generateCount, - } = props + const { selectedTemplate } = props /* ── 生成状态 ── */ const [generating, setGenerating] = useState(false) @@ -58,16 +42,9 @@ export function useGenerateVideo(props: UseGenerateVideoProps) { /* ── 生成视频 ── */ const generate = useCallback(async () => { - if (!titleSettings.title.trim()) { - message.warning("请先选择或输入标题") - return - } - if (materialMode === "manual" && selectedMaterials.length === 0) { - message.warning("请至少选择一个素材") - return - } - if (voiceMode === "clone" && !selectedClonedVoice) { - message.warning("请先选择一个克隆音色") + const errorMsg = validateGenerateInputs(props) + if (errorMsg) { + message.warning(errorMsg) return } @@ -78,41 +55,13 @@ export function useGenerateVideo(props: UseGenerateVideoProps) { clearTimer() try { - const voiceConfig = buildVoiceConfig({ - voiceMode, - selectedVoice, - selectedClonedVoice, - }) + const payload = buildEditPlanPayload(props) // 获取或创建草稿 await getEditPlan(selectedTemplate) // 更新草稿内容 + 切换到 editing 状态 - await updateEditPlan(selectedTemplate, { - name: titleSettings.title.trim(), - config: { - asset_ids: materialMode === "auto" ? smartSelectedIds : selectedMaterials, - title_config: { - ai_auto_select: titleSettings.aiAutoSelect, - content: titleSettings.title, - position: titleSettings.position, - font_preset: titleSettings.font, - font_color: titleSettings.color, - font_size: titleSettings.size, - }, - cover_config: coverSettings, - ...voiceConfig, - ratio: videoRatio, - style, - duration, - auto_subtitles: autoSubtitles, - bgm, - generate_count: generateCount, - material_mode: materialMode, - }, - total_duration: duration, - status: "editing", - }) + await updateEditPlan(selectedTemplate, payload) await generateEditPlan(selectedTemplate) startPolling() @@ -125,25 +74,7 @@ export function useGenerateVideo(props: UseGenerateVideoProps) { setGenerateError(finalMsg) message.error(finalMsg) } - }, [ - titleSettings, - selectedMaterials, - selectedVoice, - voiceMode, - selectedClonedVoice, - videoRatio, - style, - duration, - autoSubtitles, - bgm, - selectedTemplate, - generateCount, - materialMode, - coverSettings, - smartSelectedIds, - clearTimer, - startPolling, - ]) + }, [props, selectedTemplate, clearTimer, startPolling]) /* 重新生成(失败后重试) */ const retry = useCallback(() => { diff --git a/apps/web/src/test/pages/generate/smoke.test.tsx b/apps/web/src/test/pages/generate/smoke.test.tsx index 784ba7b0d..cbbe9d70a 100755 --- a/apps/web/src/test/pages/generate/smoke.test.tsx +++ b/apps/web/src/test/pages/generate/smoke.test.tsx @@ -42,3 +42,4 @@ import "@/pages/generate/hooks/generate-video/types" import "@/pages/generate/hooks/generate-video/phase" import "@/pages/generate/hooks/generate-video/voiceConfig" import "@/pages/generate/hooks/generate-video/errorUtils" +import "@/pages/generate/hooks/generate-video/buildPayload"