diff --git a/apps/web/src/pages/generate/GeneratePage.tsx b/apps/web/src/pages/generate/GeneratePage.tsx index ae0d0a2e3..1e35f0b31 100644 --- a/apps/web/src/pages/generate/GeneratePage.tsx +++ b/apps/web/src/pages/generate/GeneratePage.tsx @@ -12,6 +12,7 @@ import React, { useMemo, useState, useEffect, useRef } from "react" import { Modal, message } from "antd" import { useNavigate } from "react-router-dom" import type { VoiceClone } from "@/api/voice-clone" +import { useQuery } from "@tanstack/react-query" import { useCloneProgress } from "@/hooks/useCloneProgress" import CloneModal from "@/components/voice/CloneModal" import GenerateHeader from "./components/GenerateHeader" @@ -29,6 +30,7 @@ import { useStepNavigation } from "./hooks/useStepNavigation" import { useGenerateVideo } from "./hooks/useGenerateVideo" import { usePreviewAssets } from "./hooks/usePreviewAssets" import { useTitleStyleUpdaters } from "./hooks/useStep4Title/useTitleStyleUpdaters" +import { getAssetsByKind } from "@/api/assets" import { previewTts } from "@/api/tts" import "./generate.css" @@ -88,19 +90,35 @@ const GeneratePage: React.FC = () => { }) /* ── 配音预览音频(TTS 试听)── */ + const { data: voiceMaterials = [] } = useQuery({ + queryKey: ["assets", "voice"], + queryFn: () => getAssetsByKind("voice", { limit: 50 }), + }) + const [previewVoiceAudioUrl, setPreviewVoiceAudioUrl] = useState(null) const ttsAbortRef = useRef(null) useEffect(() => { + // 如果 selectedVoice 是已上传的配音素材,直接用 file_url + const voiceAsset = voiceMaterials.find((m) => m.id === selectedVoice) + if (voiceAsset?.file_url) { + setPreviewVoiceAudioUrl(voiceAsset.file_url) + return + } + + // 没有选中的 voice 或标题,跳过 const voiceId = selectedClonedVoice || selectedVoice if (!voiceId || !titleSettings.title) { setPreviewVoiceAudioUrl(null) return } + + // 预设音色 / 克隆音色 → 调 TTS 合成 ttsAbortRef.current?.abort() const controller = new AbortController() ttsAbortRef.current = controller let cancelled = false + previewTts({ text: titleSettings.title, voice_id: voiceId, @@ -116,12 +134,13 @@ const GeneratePage: React.FC = () => { setPreviewVoiceAudioUrl(null) } }) + return () => { cancelled = true controller.abort() } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedVoice, selectedClonedVoice, titleSettings.title]) + }, [selectedVoice, selectedClonedVoice, titleSettings.title, voiceMaterials]) /* ── 克隆声音 ── */ const { clones: clonedVoices, addClone, hasProcessing } = useCloneProgress()