diff --git a/api/video_flow.ts b/api/video_flow.ts index 5c6a28a..3e2e34d 100644 --- a/api/video_flow.ts +++ b/api/video_flow.ts @@ -699,6 +699,8 @@ export const createMovieProjectV1 = async (request: { mode: "auto" | "manual"; /** 分辨率:720p | 1080p | 4k */ resolution: "720p" | "1080p" | "4k"; + /** 语言 */ + language: string; }) => { return post Promise; /** 聚焦处理函数 */ - focusHandler: (field: 'synopsis' | 'categories' | 'protagonist' | 'incitingIncident' | 'problem' | 'conflict' | 'stakes' | 'characterArc') => Promise; + focusHandler: ( + field: + | "synopsis" + | "categories" + | "protagonist" + | "incitingIncident" + | "problem" + | "conflict" + | "stakes" + | "characterArc" + ) => Promise; /** 增强剧本 */ enhanceScript: () => Promise; /** 设置AI优化要求 */ @@ -72,6 +93,18 @@ export interface UseScriptService { /** 设置人物弧线完成 */ setCharacterArc: Dispatch>; + /** 设置项目ID */ + setProjectId: Dispatch>; + /** 设置计划ID */ + setPlanId: Dispatch>; + /** 创建项目 */ + createMovieProjectV1: ( + idea: string, + userId: string, + mode: "auto" | "manual", + resolution: string, + language: string + ) => Promise; } /** @@ -96,22 +129,103 @@ export const useScriptService = (): UseScriptService => { const [focusedField, setFocusedField] = useState(""); // UseCase实例 - const [scriptEditUseCase, setScriptEditUseCase] = useState(null); + const [scriptEditUseCase, setScriptEditUseCase] = useState( + new ScriptEditUseCase("") + ); /** * 根据用户想法生成剧本并自动创建项目 * @param idea 用户想法 */ - const generateScriptFromIdea = useCallback(async (idea: string): Promise => { - try { - setLoading(true); + 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 || ""); + }); - // 创建新的剧本编辑用例 - const newScriptEditUseCase = new ScriptEditUseCase(''); - setScriptEditUseCase(newScriptEditUseCase); + // 自动保存剧本到项目 + 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: "auto" | "manual", + resolution: string, + language: string + ): Promise => { + try { + setLoading(true); + + // 剧本生成完成后,自动创建项目 + const projectData = await scriptEditUseCase.createProject( + idea, + userId, + mode as "auto" | "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.successful) { + throw new Error(response.message || "获取项目剧本失败"); + } + + const { generated_script } = response.data; + + // 创建新的剧本编辑用例并初始化数据 + const newScriptEditUseCase = new ScriptEditUseCase(generated_script); + setScriptEditUseCase(newScriptEditUseCase); - // 调用AI生成剧本 - await newScriptEditUseCase.generateScript(idea, (content) => { // 获取解析后的故事详情 const storyDetails = newScriptEditUseCase.getStoryDetails(); setSynopsis(storyDetails.synopsis || ""); @@ -122,72 +236,15 @@ export const useScriptService = (): UseScriptService => { setConflict(storyDetails.conflict || ""); setStakes(storyDetails.stakes || ""); setCharacterArc(storyDetails.characterArc || ""); - }); - - // 剧本生成完成后,自动创建项目 - const projectData = await newScriptEditUseCase.createProject( - idea, - JSON.parse(localStorage.getItem('currentUser') || '{}').id, - "auto", - "720p" - ); - - setProjectId(projectData.project_id); - setPlanId(projectData.plan_id); - - // 自动保存剧本到项目 - await newScriptEditUseCase.saveScript(projectData.project_id); - - } catch (error) { - console.error('生成剧本失败:', error); - throw error; - } finally { - setLoading(false); - } - }, []); - - /** - * 根据项目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.successful) { - throw new Error(response.message || '获取项目剧本失败'); + } catch (error) { + console.error("初始化项目剧本失败:", error); + throw error; + } finally { + setLoading(false); } - - 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); - } - }, []); + }, + [] + ); /** * 修改剧本 @@ -220,7 +277,6 @@ export const useScriptService = (): UseScriptService => { } await scriptEditUseCase.applyScript(projectId, planId); - } catch (error) { console.error("应用剧本失败:", error); throw error; @@ -234,7 +290,6 @@ export const useScriptService = (): UseScriptService => { */ const abortVideoTaskHandler = useCallback(async (): Promise => { try { - if (!projectId || !planId) { throw new Error("项目ID或计划ID未设置"); } @@ -242,7 +297,7 @@ export const useScriptService = (): UseScriptService => { // 调用中断视频任务API const response = await abortVideoTask({ project_id: projectId, - plan_id: planId + plan_id: planId, }); if (!response.successful) { @@ -250,7 +305,6 @@ export const useScriptService = (): UseScriptService => { } console.log("视频任务中断成功"); - } catch (error) { console.error("中断视频任务失败:", error); throw error; @@ -258,85 +312,113 @@ export const useScriptService = (): UseScriptService => { }, [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 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 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 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 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 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 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 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 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 { - // 如果当前已经有聚焦的字段,先处理暂停/继续逻辑 + const focusHandler = useCallback( + async (field: ScriptEditKey): Promise => { + try { + // 如果当前已经有聚焦的字段,先处理暂停/继续逻辑 - // 设置新的聚焦字段 - setFocusedField(field); - - } catch (error) { - console.error("聚焦处理失败:", error); - throw error; - } - }, []); + // 设置新的聚焦字段 + setFocusedField(field); + } catch (error) { + console.error("聚焦处理失败:", error); + throw error; + } + }, + [] + ); /** * 增强剧本 @@ -372,7 +454,6 @@ export const useScriptService = (): UseScriptService => { if (projectId) { await scriptEditUseCase.saveScript(projectId); } - } catch (error) { console.error("增强剧本失败:", error); throw error; @@ -382,18 +463,35 @@ export const useScriptService = (): UseScriptService => { }, [scriptEditUseCase, synopsis, focusedField, aiOptimizing, projectId]); // 在ScriptService中添加一个方法来获取渲染数据 -const scriptBlocksMemo = useMemo((): ScriptBlock[] => { - return [ - parseScriptBlock('synopsis', 'Logline', synopsis || ''), - parseScriptBlock('categories', 'GENRE', categories.join(', ') || ''), - 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]); + const scriptBlocksMemo = useMemo((): ScriptBlock[] => { + return [ + parseScriptBlock("synopsis", "Logline", synopsis || ""), + parseScriptBlock("categories", "GENRE", categories.join(", ") || ""), + 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 { // 响应式状态 @@ -419,6 +517,9 @@ const scriptBlocksMemo = useMemo((): ScriptBlock[] => { focusHandler, enhanceScript, setAiOptimizing, + setProjectId, + setPlanId, + createMovieProjectV1, // 封装的set函数 setSynopsis: setSynopsisWrapper, setCategories: setCategoriesWrapper, diff --git a/app/service/test/Script.test.ts b/app/service/test/Script.test.ts index 0370008..efe0861 100644 --- a/app/service/test/Script.test.ts +++ b/app/service/test/Script.test.ts @@ -38,7 +38,8 @@ describe("ScriptService 业务逻辑测试", () => { script, "user123", "auto", - "720p" + "720p", + "en" ); expect(createRes.project_id).toBeDefined(); projectId = createRes.project_id; diff --git a/app/service/usecase/ScriptEditUseCase.ts b/app/service/usecase/ScriptEditUseCase.ts index 6501d17..735ad0f 100644 --- a/app/service/usecase/ScriptEditUseCase.ts +++ b/app/service/usecase/ScriptEditUseCase.ts @@ -126,7 +126,8 @@ export class ScriptEditUseCase { prompt: string, userId: string , mode: "auto" | "manual" = "auto", - resolution: "720p" | "1080p" | "4k" = "720p" + resolution: "720p" | "1080p" | "4k" = "720p", + language: string ) { try { // 调用创建项目API @@ -135,6 +136,7 @@ export class ScriptEditUseCase { user_id: userId, mode, resolution, + language, }); if (!response.successful) {