import { useState, useEffect, useCallback } from "react"; import api from "@/shared/api/axios"; import { ApiResponse, unwrap } from "@/shared/api/types"; import { toast } from "sonner"; export type ExtractionStep = "config" | "processing" | "result"; export type InputTab = "file" | "url"; const VALID_FILE_TYPES = [".mp4", ".mov", ".avi", ".mp3", ".wav", ".m4a"]; interface UseScriptExtractionOptions { isOpen: boolean; } export const useScriptExtraction = ({ isOpen }: UseScriptExtractionOptions) => { const [isLoading, setIsLoading] = useState(false); const [script, setScript] = useState(""); const [error, setError] = useState(null); const [step, setStep] = useState("config"); const [dragActive, setDragActive] = useState(false); const [selectedFile, setSelectedFile] = useState(null); const [activeTab, setActiveTab] = useState("url"); const [inputUrl, setInputUrl] = useState(""); // Reset state when modal opens useEffect(() => { if (isOpen) { setStep("config"); setScript(""); setError(null); setIsLoading(false); setSelectedFile(null); setInputUrl(""); setActiveTab("url"); } }, [isOpen]); const handleDrag = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); if (e.type === "dragenter" || e.type === "dragover") { setDragActive(true); } else if (e.type === "dragleave") { setDragActive(false); } }, []); const handleFile = useCallback((file: File) => { const ext = file.name.toLowerCase().slice(file.name.lastIndexOf(".")); if (!VALID_FILE_TYPES.includes(ext)) { setError(`不支持的文件格式 ${ext},请上传视频或音频文件`); return; } setSelectedFile(file); setError(null); }, []); const handleDrop = useCallback( (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files?.[0]) { handleFile(e.dataTransfer.files[0]); } }, [handleFile] ); const handleFileChange = useCallback( (e: React.ChangeEvent) => { if (e.target.files?.[0]) { handleFile(e.target.files[0]); } }, [handleFile] ); const handleExtract = useCallback(async () => { if (activeTab === "file" && !selectedFile) { setError("请先上传文件"); return; } if (activeTab === "url" && !inputUrl.trim()) { setError("请先输入视频链接"); return; } setIsLoading(true); setStep("processing"); setError(null); try { const formData = new FormData(); if (activeTab === "file" && selectedFile) { formData.append("file", selectedFile); } else if (activeTab === "url") { formData.append("url", inputUrl.trim()); } formData.append("rewrite", "false"); const { data: res } = await api.post< ApiResponse<{ original_script: string }> >("/api/tools/extract-script", formData, { headers: { "Content-Type": "multipart/form-data" }, timeout: 180000, // 3 minutes timeout }); const payload = unwrap(res); setScript(payload.original_script); setStep("result"); } catch (err: unknown) { console.error(err); const axiosErr = err as { response?: { data?: { message?: string } }; message?: string; }; const msg = axiosErr.response?.data?.message || axiosErr.message || "请求失败"; setError(msg); setStep("config"); } finally { setIsLoading(false); } }, [activeTab, selectedFile, inputUrl]); const copyToClipboard = useCallback((text: string) => { if (navigator.clipboard && window.isSecureContext) { navigator.clipboard .writeText(text) .then(() => { toast.success("已复制到剪贴板"); }) .catch(() => { fallbackCopyTextToClipboard(text); }); } else { fallbackCopyTextToClipboard(text); } }, []); const fallbackCopyTextToClipboard = (text: string) => { const textArea = document.createElement("textarea"); textArea.value = text; textArea.style.top = "0"; textArea.style.left = "0"; textArea.style.position = "fixed"; textArea.style.opacity = "0"; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { const successful = document.execCommand("copy"); if (successful) { toast.success("已复制到剪贴板"); } else { toast.error("复制失败,请手动复制"); } } catch { toast.error("复制失败,请手动复制"); } document.body.removeChild(textArea); }; const resetToConfig = useCallback(() => { setStep("config"); }, []); const clearSelectedFile = useCallback(() => { setSelectedFile(null); }, []); const clearInputUrl = useCallback(() => { setInputUrl(""); }, []); return { // State isLoading, script, error, step, dragActive, selectedFile, activeTab, inputUrl, // Setters setActiveTab, setInputUrl, // Handlers handleDrag, handleDrop, handleFileChange, handleExtract, copyToClipboard, resetToConfig, clearSelectedFile, clearInputUrl, }; };