video-flow-b/app/service/Interaction/ScriptService.ts
2025-08-06 19:00:12 +08:00

285 lines
8.3 KiB
TypeScript

import { useState, useCallback, Dispatch, SetStateAction } from "react";
import { ScriptEditUseCase } from "../usecase/ScriptEditUseCase";
import { getProjectScript, abortVideoTask } from "../../../api/video_flow";
/**
* 剧本服务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;
// 操作方法
/** 根据用户想法生成剧本并自动创建项目 */
generateScriptFromIdea: (idea: string) => Promise<void>;
/** 根据项目ID初始化已有剧本 */
initializeFromProject: (projectId: string) => Promise<void>;
/** 修改剧本 */
updateScript: (scriptText: string) => void;
/** 应用剧本到视频生成流程 */
applyScript: () => Promise<void>;
/** 中断视频任务 */
abortVideoTask: () => Promise<void>;
// 修改字段的set函数
/** 设置故事梗概 */
setSynopsis: Dispatch<SetStateAction<string>>;
/** 设置故事分类 */
setCategories: Dispatch<SetStateAction<string[]>>;
/** 设置主角名称 */
setProtagonist: Dispatch<SetStateAction<string>>;
/** 设置激励事件 */
setIncitingIncident: Dispatch<SetStateAction<string>>;
/** 设置问题与新目标 */
setProblem: Dispatch<SetStateAction<string>>;
/** 设置冲突与障碍 */
setConflict: Dispatch<SetStateAction<string>>;
/** 设置赌注 */
setStakes: Dispatch<SetStateAction<string>>;
/** 设置人物弧线完成 */
setCharacterArc: Dispatch<SetStateAction<string>>;
}
/**
* 剧本服务Hook
* 提供剧本相关的所有状态管理和操作方法
* 包括剧本生成、项目创建、剧本保存等功能
*/
export const useScriptService = (): UseScriptService => {
// 响应式状态
const [loading, setLoading] = useState<boolean>(false);
const [synopsis, setSynopsis] = useState<string>("");
const [categories, setCategories] = useState<string[]>([]);
const [protagonist, setProtagonist] = useState<string>("");
const [incitingIncident, setIncitingIncident] = useState<string>("");
const [problem, setProblem] = useState<string>("");
const [conflict, setConflict] = useState<string>("");
const [stakes, setStakes] = useState<string>("");
const [characterArc, setCharacterArc] = useState<string>("");
const [projectId, setProjectId] = useState<string>("");
const [planId, setPlanId] = useState<string>("");
// UseCase实例
const [scriptEditUseCase, setScriptEditUseCase] = useState<ScriptEditUseCase | null>(null);
/**
* 根据用户想法生成剧本并自动创建项目
* @param idea 用户想法
*/
const generateScriptFromIdea = useCallback(async (idea: string): Promise<void> => {
try {
setLoading(true);
// 创建新的剧本编辑用例
const newScriptEditUseCase = new ScriptEditUseCase('');
setScriptEditUseCase(newScriptEditUseCase);
// 调用AI生成剧本
await newScriptEditUseCase.generateScript(idea, (content) => {
// 获取解析后的故事详情
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 || "");
});
// 剧本生成完成后,自动创建项目
const projectData = await newScriptEditUseCase.createProject(
idea,
"user123",
"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<void> => {
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);
// 获取解析后的故事详情
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((scriptText: string): void => {
if (scriptEditUseCase) {
scriptEditUseCase.updateScript(scriptText);
// 更新解析后的故事详情
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 || "");
}
}, [scriptEditUseCase]);
/**
* 应用剧本到视频生成流程
*/
const applyScript = useCallback(async (): Promise<void> => {
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<void> => {
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]);
return {
// 响应式状态
loading,
synopsis,
categories,
protagonist,
incitingIncident,
problem,
conflict,
stakes,
characterArc,
projectId,
planId,
// 操作方法
generateScriptFromIdea,
initializeFromProject,
updateScript,
applyScript,
abortVideoTask: abortVideoTaskHandler,
// 修改字段的set函数
setSynopsis,
setCategories,
setProtagonist,
setIncitingIncident,
setProblem,
setConflict,
setStakes,
setCharacterArc,
};
};