import { useState, useCallback, Dispatch, SetStateAction, useMemo, } from "react"; import { ScriptEditUseCase, ScriptEditKey } from "../usecase/ScriptEditUseCase"; import { getProjectScript, abortVideoTask, pausePlanFlow, resumePlanFlow, } from "../../../api/video_flow"; import { parseScriptBlock } from "../domain/service"; import { ScriptBlock } from "@/components/script-renderer/types"; /** * 剧本服务Hook接口 * 定义剧本服务Hook的所有状态和操作方法 */ export interface UseScriptService { // 响应式状态 /** 加载状态 */ loading: boolean; /** 故事梗概 */ synopsis: string; /** 故事分类 */ categories: string[]; /** 主角*/ protagonist: string; /** 激励事件 */ incitingIncident: string; /** 问题与新目标 */ problem: string; /** 冲突与障碍 */ conflict: string; /** 赌注 */ stakes: string; /** 人物弧线完成 */ characterArc: string; /** 项目ID */ projectId: string; /** 计划ID */ planId: string; /** AI优化要求 */ aiOptimizing: string; /** 渲染数据 */ scriptBlocksMemo: ScriptBlock[]; // 操作方法 /** 根据用户想法生成剧本并自动创建项目 */ generateScriptFromIdea: (idea: string) => Promise; /** 根据项目ID初始化已有剧本 */ initializeFromProject: (projectId: string) => Promise; /** 修改剧本 */ updateScript: (scriptText: string) => Promise; /** 应用剧本到视频生成流程 */ applyScript: () => Promise; /** 中断视频任务 */ abortVideoTask: () => Promise; /** 聚焦处理函数 */ focusHandler: ( field: | "synopsis" | "categories" | "protagonist" | "incitingIncident" | "problem" | "conflict" | "stakes" | "characterArc" ) => Promise; /** 增强剧本 */ enhanceScript: () => Promise; /** 设置AI优化要求 */ setAiOptimizing: Dispatch>; // 修改字段的set函数 /** 设置故事梗概 */ setSynopsis: Dispatch>; /** 设置故事分类 */ setCategories: Dispatch>; /** 设置主角名称 */ setProtagonist: Dispatch>; /** 设置激励事件 */ setIncitingIncident: Dispatch>; /** 设置问题与新目标 */ setProblem: Dispatch>; /** 设置冲突与障碍 */ setConflict: Dispatch>; /** 设置赌注 */ setStakes: Dispatch>; /** 设置人物弧线完成 */ setCharacterArc: Dispatch>; /** 设置项目ID */ setProjectId: Dispatch>; /** 设置计划ID */ setPlanId: Dispatch>; /** 创建项目 */ createMovieProjectV1: ( idea: string, userId: string, mode: "automatic" | "manual", resolution: string, language: string ) => Promise; } /** * 剧本服务Hook * 提供剧本相关的所有状态管理和操作方法 * 包括剧本生成、项目创建、剧本保存等功能 */ export const useScriptService = (): UseScriptService => { // 响应式状态 const [loading, setLoading] = useState(false); const [synopsis, setSynopsis] = useState(""); const [categories, setCategories] = useState([]); const [protagonist, setProtagonist] = useState(""); const [incitingIncident, setIncitingIncident] = useState(""); const [problem, setProblem] = useState(""); const [conflict, setConflict] = useState(""); const [stakes, setStakes] = useState(""); const [characterArc, setCharacterArc] = useState(""); const [projectId, setProjectId] = useState(""); const [planId, setPlanId] = useState(""); const [aiOptimizing, setAiOptimizing] = useState(""); const [focusedField, setFocusedField] = useState(""); // UseCase实例 const [scriptEditUseCase, setScriptEditUseCase] = useState( new ScriptEditUseCase("") ); /** * 根据用户想法生成剧本并自动创建项目 * @param idea 用户想法 */ const generateScriptFromIdea = useCallback( async (idea: string,project_id?:string): Promise => { try { setLoading(true); if(project_id){ setProjectId(project_id); } // 调用AI生成剧本 await scriptEditUseCase.generateScript(idea, (content) => { // 获取解析后的故事详情 const storyDetails = scriptEditUseCase.getStoryDetails(); setSynopsis(storyDetails.synopsis || ""); setCategories(storyDetails.categories || []); setProtagonist(storyDetails.protagonist || ""); setIncitingIncident(storyDetails.incitingIncident || ""); setProblem(storyDetails.problem || ""); setConflict(storyDetails.conflict || ""); setStakes(storyDetails.stakes || ""); setCharacterArc(storyDetails.characterArc || ""); }); // 自动保存剧本到项目 await scriptEditUseCase.saveScript((projectId||project_id) as string); } catch (error) { console.error("生成剧本失败:", error); throw error; } finally { setLoading(false); } }, [] ); const createMovieProjectV1 = useCallback( async ( idea: string, userId: string, mode: "automatic" | "manual", resolution: string, language: string ): Promise => { try { setLoading(true); // 剧本生成完成后,自动创建项目 const projectData = await scriptEditUseCase.createProject( idea, userId, mode as "automatic" | "manual", resolution as "720p" | "1080p" | "4k", language ); setProjectId(projectData.project_id); setPlanId(projectData.plan_id); } catch (error) { console.error("创建项目失败:", error); throw error; } finally { setLoading(false); } }, [scriptEditUseCase] ); /** * 根据项目ID初始化已有剧本 * @param projectId 项目ID */ const initializeFromProject = useCallback( async (projectId: string): Promise => { try { setLoading(true); // 设置项目ID setProjectId(projectId); // 调用API获取项目剧本数据 const response = await getProjectScript({ project_id: projectId }); if(response.data.generated_script===''){ await generateScriptFromIdea(response.data.script,projectId) return } if (!response.successful) { throw new Error(response.message || "获取项目剧本失败"); } const { generated_script } = response.data; // 创建新的剧本编辑用例并初始化数据 const newScriptEditUseCase = new ScriptEditUseCase(generated_script); setScriptEditUseCase(newScriptEditUseCase); // 获取解析后的故事详情 const storyDetails = newScriptEditUseCase.getStoryDetails(); setSynopsis(storyDetails.synopsis || ""); setCategories(storyDetails.categories || []); setProtagonist(storyDetails.protagonist || ""); setIncitingIncident(storyDetails.incitingIncident || ""); setProblem(storyDetails.problem || ""); setConflict(storyDetails.conflict || ""); setStakes(storyDetails.stakes || ""); setCharacterArc(storyDetails.characterArc || ""); } catch (error) { console.error("初始化项目剧本失败:", error); throw error; } finally { setLoading(false); } }, [] ); /** * 修改剧本 * @param scriptText 新的剧本文本 */ const updateScript = useCallback(async (): Promise => { if (scriptEditUseCase) { // 更新解析后的故事详情 const storyDetails = scriptEditUseCase.getStoryDetails(); // 如果有项目ID,则保存剧本 if (projectId) { await scriptEditUseCase.saveScript(projectId); } } }, [scriptEditUseCase, projectId]); /** * 应用剧本到视频生成流程 */ const applyScript = useCallback(async (): Promise => { try { setLoading(true); if (!scriptEditUseCase) { throw new Error("剧本编辑用例未初始化"); } if (!projectId || !planId) { throw new Error("项目ID或计划ID未设置"); } await scriptEditUseCase.applyScript(projectId, planId); } catch (error) { console.error("应用剧本失败:", error); throw error; } finally { setLoading(false); } }, [scriptEditUseCase, projectId, planId]); /** * 中断视频任务 */ const abortVideoTaskHandler = useCallback(async (): Promise => { try { if (!projectId || !planId) { throw new Error("项目ID或计划ID未设置"); } // 调用中断视频任务API const response = await abortVideoTask({ project_id: projectId, plan_id: planId, }); if (!response.successful) { throw new Error(response.message || "中断视频任务失败"); } console.log("视频任务中断成功"); } catch (error) { console.error("中断视频任务失败:", error); throw error; } }, [projectId, planId]); // 封装的setter函数,同时更新hook状态和scriptEditUseCase中的值对象 const setSynopsisWrapper = useCallback( (value: SetStateAction) => { const newValue = typeof value === "function" ? value(synopsis) : value; setSynopsis(newValue); if (scriptEditUseCase) { scriptEditUseCase.updateStoryField("synopsis", newValue); } }, [synopsis, scriptEditUseCase] ); const setCategoriesWrapper = useCallback( (value: SetStateAction) => { const newValue = typeof value === "function" ? value(categories) : value; setCategories(newValue); if (scriptEditUseCase) { scriptEditUseCase.updateStoryField("categories", newValue); } }, [categories, scriptEditUseCase] ); const setProtagonistWrapper = useCallback( (value: SetStateAction) => { const newValue = typeof value === "function" ? value(protagonist) : value; setProtagonist(newValue); if (scriptEditUseCase) { scriptEditUseCase.updateStoryField("protagonist", newValue); } }, [protagonist, scriptEditUseCase] ); const setIncitingIncidentWrapper = useCallback( (value: SetStateAction) => { const newValue = typeof value === "function" ? value(incitingIncident) : value; setIncitingIncident(newValue); if (scriptEditUseCase) { scriptEditUseCase.updateStoryField("incitingIncident", newValue); } }, [incitingIncident, scriptEditUseCase] ); const setProblemWrapper = useCallback( (value: SetStateAction) => { const newValue = typeof value === "function" ? value(problem) : value; setProblem(newValue); if (scriptEditUseCase) { scriptEditUseCase.updateStoryField("problem", newValue); } }, [problem, scriptEditUseCase] ); const setConflictWrapper = useCallback( (value: SetStateAction) => { const newValue = typeof value === "function" ? value(conflict) : value; setConflict(newValue); if (scriptEditUseCase) { scriptEditUseCase.updateStoryField("conflict", newValue); } }, [conflict, scriptEditUseCase] ); const setStakesWrapper = useCallback( (value: SetStateAction) => { const newValue = typeof value === "function" ? value(stakes) : value; setStakes(newValue); if (scriptEditUseCase) { scriptEditUseCase.updateStoryField("stakes", newValue); } }, [stakes, scriptEditUseCase] ); const setCharacterArcWrapper = useCallback( (value: SetStateAction) => { const newValue = typeof value === "function" ? value(characterArc) : value; setCharacterArc(newValue); if (scriptEditUseCase) { scriptEditUseCase.updateStoryField("characterArc", newValue); } }, [characterArc, scriptEditUseCase] ); /** * 聚焦处理函数 */ const focusHandler = useCallback( async (field: ScriptEditKey): Promise => { try { // 如果当前已经有聚焦的字段,先处理暂停/继续逻辑 // 设置新的聚焦字段 setFocusedField(field); } catch (error) { console.error("聚焦处理失败:", error); throw error; } }, [] ); /** * 增强剧本 */ const enhanceScript = useCallback(async (): Promise => { try { setLoading(true); if (!scriptEditUseCase) { throw new Error("剧本编辑用例未初始化"); } // 调用增强剧本方法 await scriptEditUseCase.enhanceScript( synopsis, focusedField as ScriptEditKey, aiOptimizing, (content: any) => { // 获取解析后的故事详情 const storyDetails = scriptEditUseCase.getStoryDetails(); setSynopsis(storyDetails.synopsis || ""); setCategories(storyDetails.categories || []); setProtagonist(storyDetails.protagonist || ""); setIncitingIncident(storyDetails.incitingIncident || ""); setProblem(storyDetails.problem || ""); setConflict(storyDetails.conflict || ""); setStakes(storyDetails.stakes || ""); setCharacterArc(storyDetails.characterArc || ""); } ); // 如果有项目ID,则保存增强后的剧本 if (projectId) { await scriptEditUseCase.saveScript(projectId); } } catch (error) { console.error("增强剧本失败:", error); throw error; } finally { setLoading(false); } }, [scriptEditUseCase, synopsis, focusedField, aiOptimizing, projectId]); // 在ScriptService中添加一个方法来获取渲染数据 const scriptBlocksMemo = useMemo((): ScriptBlock[] => { return [ parseScriptBlock("synopsis", "Logline", synopsis || ""), parseScriptBlock('categories', 'GENRE', categories.join(', ') || '', 'tag'), parseScriptBlock("protagonist", "Core Identity", protagonist || ""), parseScriptBlock( "incitingIncident", "The Inciting Incident", incitingIncident || "" ), parseScriptBlock("problem", "The Problem & New Goal", problem || ""), parseScriptBlock("conflict", "Conflict & Obstacles", conflict || ""), parseScriptBlock("stakes", "The Stakes", stakes || ""), parseScriptBlock( "characterArc", "Character Arc Accomplished", characterArc || "" ), ]; }, [ synopsis, categories, protagonist, incitingIncident, problem, conflict, stakes, characterArc, ]); return { // 响应式状态 loading, synopsis, categories, protagonist, incitingIncident, problem, conflict, stakes, characterArc, projectId, planId, aiOptimizing, scriptBlocksMemo, // 操作方法 generateScriptFromIdea, initializeFromProject, updateScript, applyScript, abortVideoTask: abortVideoTaskHandler, focusHandler, enhanceScript, setAiOptimizing, setProjectId, setPlanId, createMovieProjectV1, // 封装的set函数 setSynopsis: setSynopsisWrapper, setCategories: setCategoriesWrapper, setProtagonist: setProtagonistWrapper, setIncitingIncident: setIncitingIncidentWrapper, setProblem: setProblemWrapper, setConflict: setConflictWrapper, setStakes: setStakesWrapper, setCharacterArc: setCharacterArcWrapper, }; };