102 lines
2.4 KiB
TypeScript
Executable File
102 lines
2.4 KiB
TypeScript
Executable File
import { useRef, useCallback, useEffect } from "react"
|
|
import { createVoiceClone, toVoiceClone } from "@/api/voice-clone"
|
|
import { uploadAsset } from "@/api/assets"
|
|
import type { VoiceClone } from "@/api/voice-clone"
|
|
|
|
interface UseCloneSubmitOptions {
|
|
voiceName: string
|
|
voiceDescription: string
|
|
selectedFile: File | null
|
|
recordedBlob: Blob | null
|
|
setPhase: (phase: "input" | "uploading" | "cloning" | "done") => void
|
|
setErrorMessage: (msg: string) => void
|
|
validateForm: () => string | null
|
|
onSuccess?: (clone: VoiceClone) => void
|
|
onClose: () => void
|
|
}
|
|
|
|
/**
|
|
* 克隆提交流程 Hook
|
|
* 封装上传 + 克隆 + 完成的三阶段流程
|
|
*/
|
|
export function useCloneSubmit({
|
|
voiceName,
|
|
voiceDescription,
|
|
selectedFile,
|
|
recordedBlob,
|
|
setPhase,
|
|
setErrorMessage,
|
|
validateForm,
|
|
onSuccess,
|
|
onClose,
|
|
}: UseCloneSubmitOptions) {
|
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
|
|
/** 组件卸载时清理定时器 */
|
|
useEffect(() => {
|
|
return () => {
|
|
if (timerRef.current) clearTimeout(timerRef.current)
|
|
}
|
|
}, [])
|
|
|
|
const handleSubmit = useCallback(async () => {
|
|
const formError = validateForm()
|
|
if (formError) {
|
|
setErrorMessage(formError)
|
|
return
|
|
}
|
|
|
|
setErrorMessage("")
|
|
|
|
try {
|
|
// 阶段 1:上传音频
|
|
setPhase("uploading")
|
|
|
|
let fileToUpload: File
|
|
if (selectedFile) {
|
|
fileToUpload = selectedFile
|
|
} else {
|
|
fileToUpload = new File([recordedBlob!], `recorded-${Date.now()}.webm`, {
|
|
type: "audio/webm",
|
|
})
|
|
}
|
|
|
|
const formData = new FormData()
|
|
formData.append("file", fileToUpload)
|
|
const uploadResult = await uploadAsset(formData)
|
|
|
|
// 阶段 2:克隆
|
|
setPhase("cloning")
|
|
const result = await createVoiceClone({
|
|
name: voiceName.trim(),
|
|
description: voiceDescription.trim() || undefined,
|
|
audio_url: uploadResult.url,
|
|
})
|
|
|
|
// 阶段 3:完成
|
|
setPhase("done")
|
|
|
|
// 2秒后自动关闭
|
|
timerRef.current = setTimeout(() => {
|
|
onSuccess?.(toVoiceClone(result))
|
|
onClose()
|
|
}, 2000)
|
|
} catch (err) {
|
|
setPhase("input")
|
|
setErrorMessage(err instanceof Error ? err.message : "克隆失败,请重试")
|
|
}
|
|
}, [
|
|
validateForm,
|
|
selectedFile,
|
|
recordedBlob,
|
|
voiceName,
|
|
voiceDescription,
|
|
setPhase,
|
|
setErrorMessage,
|
|
onSuccess,
|
|
onClose,
|
|
])
|
|
|
|
return { handleSubmit }
|
|
}
|