forked from 77media/video-flow
292 lines
8.0 KiB
TypeScript
292 lines
8.0 KiB
TypeScript
import { useState, useCallback, useMemo } from "react";
|
||
import { ScriptSlice } from "../domain/valueObject";
|
||
import { ScriptEditUseCase } from "../usecase/ScriptEditUseCase";
|
||
import { getProjectScript, saveScript as saveScriptAPI, createProject as createProjectAPI } from "../../../api/video_flow";
|
||
import { throttle } from "@/utils/tools";
|
||
|
||
/**
|
||
* 剧本服务Hook接口
|
||
* 定义剧本服务Hook的所有状态和操作方法
|
||
*/
|
||
export interface UseScriptService {
|
||
// 响应式状态
|
||
/** 当前剧本文本 */
|
||
scriptText: string;
|
||
/** 剧本片段列表 */
|
||
scriptSlices: ScriptSlice[];
|
||
/** 用户提示词(可编辑) */
|
||
userPrompt: string;
|
||
/** 加载状态 */
|
||
loading: boolean;
|
||
/** 项目ID */
|
||
projectId: string;
|
||
|
||
// 操作方法
|
||
/** 获取根据用户想法调用接口AI生成剧本(用户提示词) */
|
||
fetchScriptData: (prompt: string) => Promise<void>;
|
||
/** 根据项目ID获取已存在的剧本数据 */
|
||
fetchProjectScript: (projectId: string) => Promise<void>;
|
||
/** 更新用户提示词 */
|
||
updateUserPrompt: (prompt: string) => void;
|
||
/** 重置剧本内容到初始状态 */
|
||
resetScript: () => void;
|
||
/** 应用剧本 */
|
||
applyScript: () => Promise<void>;
|
||
/** 中断剧本生成 */
|
||
abortGenerateScript: () => void;
|
||
/** 保存剧本 */
|
||
saveScript: () => Promise<void>;
|
||
/** 创建项目 */
|
||
createProject: () => Promise<void>;
|
||
}
|
||
|
||
/**
|
||
* 剧本服务Hook
|
||
* 提供剧本相关的所有状态管理和操作方法
|
||
* 包括剧本数据获取、片段管理等功能
|
||
*/
|
||
export const useScriptService = (): UseScriptService => {
|
||
// 响应式状态
|
||
const [scriptText, setScriptText] = useState<string>("");
|
||
const [scriptSlices, setScriptSlices] = useState<ScriptSlice[]>([]);
|
||
const [userPrompt, setUserPrompt] = useState<string>("");
|
||
const [initialScriptText, setInitialScriptText] = useState<string>("");
|
||
const [loading, setLoading] = useState<boolean>(false);
|
||
const [projectId, setProjectId] = useState<string>("");
|
||
// UseCase实例
|
||
const [scriptEditUseCase, setScriptEditUseCase] = useState<ScriptEditUseCase | null>(null);
|
||
|
||
/**
|
||
* 初始化,ai生成剧本(用户提示词)
|
||
* @param prompt 用户提示词
|
||
*/
|
||
const fetchScriptData = useCallback(async (prompt: string): Promise<void> => {
|
||
try {
|
||
setLoading(true);
|
||
// 清空当前状态
|
||
setScriptText("");
|
||
setScriptSlices([]);
|
||
|
||
// 更新用户提示词状态
|
||
setUserPrompt(prompt);
|
||
|
||
// 创建新的剧本编辑用例
|
||
const newScriptEditUseCase = new ScriptEditUseCase('');
|
||
setScriptEditUseCase(newScriptEditUseCase);
|
||
|
||
// 调用AI生成剧本
|
||
await newScriptEditUseCase.generateScript(prompt,throttle((newContent)=>{
|
||
// 获取生成的剧本文本
|
||
const generatedScriptText = newScriptEditUseCase.toString();
|
||
setScriptText(generatedScriptText);
|
||
console.log(scriptText);
|
||
|
||
// 获取剧本片段列表
|
||
const slices = newScriptEditUseCase.getScriptSlices();
|
||
setScriptSlices(slices);
|
||
|
||
// 保存初始剧本文本(只在第一次获取时保存)
|
||
if (!initialScriptText) {
|
||
setInitialScriptText(generatedScriptText);
|
||
}
|
||
}));
|
||
|
||
} catch (error) {
|
||
console.error('获取剧本数据失败:', error);
|
||
throw error;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, [initialScriptText]);
|
||
|
||
/**
|
||
* 根据项目ID获取已存在的剧本数据
|
||
* @param projectId 项目ID
|
||
*/
|
||
const fetchProjectScript = useCallback(async (projectId: string): Promise<void> => {
|
||
try {
|
||
setLoading(true);
|
||
// 清空当前状态
|
||
setScriptText("");
|
||
setScriptSlices([]);
|
||
|
||
// 设置项目ID
|
||
setProjectId(projectId);
|
||
|
||
// 调用API获取项目剧本数据
|
||
const response = await getProjectScript({ projectId });
|
||
|
||
if (!response.successful) {
|
||
throw new Error(response.message || '获取项目剧本失败');
|
||
}
|
||
|
||
const { prompt, scriptText } = response.data;
|
||
|
||
// 更新用户提示词状态
|
||
setUserPrompt(prompt);
|
||
|
||
// 保存初始剧本文本(只在第一次获取时保存)
|
||
if (!initialScriptText) {
|
||
setInitialScriptText(scriptText);
|
||
}
|
||
|
||
// 创建新的剧本编辑用例并初始化数据
|
||
const newScriptEditUseCase = new ScriptEditUseCase(scriptText);
|
||
setScriptEditUseCase(newScriptEditUseCase);
|
||
|
||
// 设置剧本文本
|
||
setScriptText(scriptText);
|
||
|
||
// 从UseCase获取解析后的剧本片段
|
||
const scriptSlices = newScriptEditUseCase.getScriptSlices();
|
||
setScriptSlices(scriptSlices);
|
||
|
||
} catch (error) {
|
||
console.error('获取项目剧本数据失败:', error);
|
||
throw error;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, [initialScriptText]);
|
||
|
||
/**
|
||
* 更新用户提示词
|
||
* @param prompt 新的用户提示词
|
||
*/
|
||
const updateUserPrompt = useCallback((prompt: string): void => {
|
||
setUserPrompt(prompt);
|
||
}, []);
|
||
|
||
/**
|
||
* 重置剧本内容到初始状态
|
||
*/
|
||
const resetScript = useCallback((): void => {
|
||
if (initialScriptText && scriptEditUseCase) {
|
||
// 重置剧本文本到初始状态
|
||
setScriptText(initialScriptText);
|
||
// 更新现有剧本编辑用例的数据
|
||
scriptEditUseCase.updateScript(initialScriptText);
|
||
// 从UseCase获取解析后的剧本片段
|
||
const scriptSlices = scriptEditUseCase.getScriptSlices();
|
||
setScriptSlices(scriptSlices);
|
||
}
|
||
}, [initialScriptText, scriptEditUseCase]);
|
||
|
||
/**
|
||
* 应用剧本
|
||
*/
|
||
const applyScript = useCallback(async (): Promise<void> => {
|
||
try {
|
||
setLoading(true);
|
||
|
||
if (!scriptEditUseCase) {
|
||
throw new Error("剧本编辑用例未初始化");
|
||
}
|
||
|
||
await scriptEditUseCase.applyScript(projectId);
|
||
|
||
} catch (error) {
|
||
console.error("应用剧本失败:", error);
|
||
throw error;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, [scriptEditUseCase, projectId]);
|
||
|
||
/**
|
||
* 中断剧本生成
|
||
*/
|
||
const abortGenerateScript = useCallback((): void => {
|
||
if (scriptEditUseCase) {
|
||
scriptEditUseCase.abortGenerateScript();
|
||
setLoading(false);
|
||
}
|
||
}, [scriptEditUseCase]);
|
||
|
||
/**
|
||
* 保存剧本
|
||
*/
|
||
const saveScript = useCallback(async (): Promise<void> => {
|
||
try {
|
||
setLoading(true);
|
||
|
||
if (!projectId) {
|
||
throw new Error("项目ID未设置");
|
||
}
|
||
|
||
if (!scriptEditUseCase) {
|
||
throw new Error("剧本编辑用例未初始化");
|
||
}
|
||
|
||
// 调用保存剧本接口
|
||
const scriptText = scriptEditUseCase.toString();
|
||
const response = await saveScriptAPI({ projectId, scriptText });
|
||
|
||
if (!response.successful) {
|
||
throw new Error(response.message || '保存剧本失败');
|
||
}
|
||
|
||
console.log("剧本保存成功");
|
||
|
||
} catch (error) {
|
||
console.error("保存剧本失败:", error);
|
||
throw error;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, [projectId, scriptEditUseCase]);
|
||
|
||
/**
|
||
* 创建项目
|
||
* @throws {Error} - 创建项目失败时抛出异常
|
||
*/
|
||
const createProject = useCallback(async (): Promise<void> => {
|
||
try {
|
||
setLoading(true);
|
||
|
||
// 直接使用当前state中的userPrompt和scriptText
|
||
const currentUserPrompt = userPrompt;
|
||
const currentScriptContent = scriptText;
|
||
|
||
const response = await createProjectAPI({
|
||
userPrompt: currentUserPrompt,
|
||
scriptContent: currentScriptContent
|
||
});
|
||
|
||
if (!response.successful) {
|
||
throw new Error(response.message || '创建项目失败');
|
||
}
|
||
|
||
const { projectId: newProjectId } = response.data;
|
||
setProjectId(newProjectId);
|
||
|
||
console.log("项目创建成功");
|
||
|
||
} catch (error) {
|
||
console.error("创建项目失败:", error);
|
||
throw error;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, [userPrompt, scriptText]);
|
||
|
||
return {
|
||
// 响应式状态
|
||
scriptText,
|
||
scriptSlices,
|
||
userPrompt,
|
||
loading,
|
||
projectId,
|
||
|
||
// 操作方法
|
||
fetchScriptData,
|
||
fetchProjectScript,
|
||
updateUserPrompt,
|
||
resetScript,
|
||
applyScript,
|
||
abortGenerateScript,
|
||
saveScript,
|
||
createProject,
|
||
};
|
||
};
|