import { useState, useCallback, useMemo } from "react"; import { ShotEntity, RoleEntity, SceneEntity, ContentItem, } from "../domain/Entities"; import { ScriptSlice, ScriptValueObject } from "../domain/valueObject"; import { ShotItem } from "../domain/Item"; import { ShotEditUseCase } from "../usecase/ShotEditUsecase"; import { getShotList, // getShotDetail, updateShotContent, updateShotShot, getUserRoleLibrary, replaceShotRole, getShotVideoScript, } from "@/api/video_flow"; /** * 分镜服务Hook接口 * 定义分镜服务Hook的所有状态和操作方法 */ export interface UseShotService { // 响应式状态 /** 分镜列表 */ shotList: ShotEntity[]; /** 当前选中的分镜 */ selectedShot: ShotItem | null; /** 当前分镜的草图数据URL */ shotSketchData: string | null; /** 当前分镜的视频数据URL */ shotVideoData: string[] | null; /** 用户角色库 */ userRoleLibrary: RoleEntity[]; /** 当前分镜的视频剧本片段 */ shotVideoScript: ScriptSlice[]; /** 加载状态 */ loading: boolean; /** 错误信息 */ error: string | null; // 操作方法 /** 获取分镜列表 */ fetchShotList: (projectId: string) => Promise; /** 选择分镜并获取详情 */ selectShot: (shotId: string) => Promise; /** 修改分镜对话内容 */ updateShotContent: ( newContent: Array<{ roleId: string; content: string }> ) => Promise; /** 修改分镜镜头 */ updateShotShot: (newShot: string[]) => Promise; /** 获取用户角色库 */ fetchUserRoleLibrary: () => Promise; /** 替换分镜角色 */ replaceShotRole: (oldRoleId: string, newRoleId: string) => Promise; /** 获取分镜视频剧本内容 */ fetchShotVideoScript: () => Promise; /** 获取分镜关联的角色信息 */ getShotRoles: () => Promise; /** 获取分镜关联的场景信息 */ getShotScenes: () => Promise; /** 重新生成分镜 */ regenerateShot: ( shotPrompt: string, dialogueContent: ContentItem[], roleReplaceParams: { oldId: string; newId: string }[], sceneReplaceParams: { oldId: string; newId: string }[] ) => Promise; } /** * 分镜服务Hook * 提供分镜相关的所有状态管理和操作方法 * 包括分镜列表管理、分镜选择、数据获取、内容修改等功能 */ export const useShotService = (): UseShotService => { // 响应式状态 const [shotList, setShotList] = useState([]); const [selectedShot, setSelectedShot] = useState(null); const [shotSketchData, setShotSketchData] = useState(null); const [shotVideoData, setShotVideoData] = useState(null); const [userRoleLibrary, setUserRoleLibrary] = useState([]); const [shotVideoScript, setShotVideoScript] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [projectId, setProjectId] = useState(""); // UseCase实例 const [shotEditUseCase, setShotEditUseCase] = useState(null); /** * 获取分镜列表 * @description 根据项目ID获取所有分镜列表 * @param projectId 项目ID */ const fetchShotList = useCallback(async (projectId: string) => { setProjectId(projectId); try { setLoading(true); setError(null); const response = await getShotList({ projectId }); if (response.successful) { setShotList(response.data); } else { setError(`获取分镜列表失败: ${response.message}`); } } catch (err) { setError( `获取分镜列表失败: ${err instanceof Error ? err.message : "未知错误"}` ); } finally { setLoading(false); } }, []); /** * 选择分镜并获取详情 * @description 根据分镜ID获取分镜详情,并初始化相关的UseCase和数据 * @param shotId 分镜ID */ const selectShot = useCallback(async (shotId: string) => { try { setLoading(true); setError(null); // 获取分镜详情 await fetchShotList(projectId); const shotEntity = shotList.find( (shot: ShotEntity) => shot.id === shotId ); if (!shotEntity) { setError(`分镜不存在: ${shotId}`); return; } const shotItem = new ShotItem(shotEntity); setSelectedShot(shotItem); // 初始化UseCase const newShotEditUseCase = new ShotEditUseCase(shotItem); setShotEditUseCase(newShotEditUseCase); // 从分镜实体中获取草图数据和视频数据 setShotSketchData(shotEntity.sketchUrl || null); setShotVideoData(shotEntity.videoUrl || null); } catch (err) { setError( `选择分镜失败: ${err instanceof Error ? err.message : "未知错误"}` ); } finally { setLoading(false); } }, []); /** * 修改分镜对话内容 * @description 更新分镜的对话内容,ContentItem数量和ID顺序不能变,只能修改content字段 * @param newContent 新的对话内容数组 */ const updateShotContentHandler = useCallback( async (newContent: Array<{ roleId: string; content: string }>) => { if (!shotEditUseCase) { setError("分镜编辑用例未初始化"); return; } try { setLoading(true); setError(null); const updatedShot = await shotEditUseCase.updateShotContent(newContent); setSelectedShot(new ShotItem(updatedShot)); } catch (err) { setError( `修改分镜对话内容失败: ${ err instanceof Error ? err.message : "未知错误" }` ); } finally { setLoading(false); } }, [shotEditUseCase] ); /** * 修改分镜镜头 * @description 更新分镜的镜头数据 * @param newShot 新的镜头数据 */ const updateShotShotHandler = useCallback( async (newShot: string[]) => { if (!selectedShot) { setError("未选择分镜"); return; } try { setLoading(true); setError(null); const response = await updateShotShot({ shotId: selectedShot.entity.id, shot: newShot, }); if (response.successful) { const updatedShot = response.data; setSelectedShot(new ShotItem(updatedShot)); } else { setError(`修改分镜镜头失败: ${response.message}`); } } catch (err) { setError( `修改分镜镜头失败: ${err instanceof Error ? err.message : "未知错误"}` ); } finally { setLoading(false); } }, [selectedShot] ); /** * 获取用户角色库 * @description 获取当前用户的所有角色库数据 */ const fetchUserRoleLibrary = useCallback(async () => { try { setLoading(true); setError(null); const response = await getUserRoleLibrary(); if (response.successful) { setUserRoleLibrary(response.data); } else { setError(`获取用户角色库失败: ${response.message}`); } } catch (err) { setError( `获取用户角色库失败: ${err instanceof Error ? err.message : "未知错误"}` ); } finally { setLoading(false); } }, []); /** * 替换分镜角色 * @description 将分镜中的某个角色替换为角色库中的另一个角色 * @param oldRoleId 旧角色ID * @param newRoleId 新角色ID */ const replaceShotRoleHandler = useCallback( async (oldRoleId: string, newRoleId: string) => { if (!selectedShot) { setError("未选择分镜"); return; } try { setLoading(true); setError(null); const response = await replaceShotRole({ shotId: selectedShot.entity.id, oldRoleId, newRoleId, }); if (response.successful) { // 重新获取分镜详情 await selectShot(selectedShot.entity.id); } else { setError(`替换分镜角色失败: ${response.message}`); } } catch (err) { setError( `替换分镜角色失败: ${err instanceof Error ? err.message : "未知错误"}` ); } finally { setLoading(false); } }, [selectedShot, selectShot] ); /** * 获取分镜视频剧本内容 * @description 获取分镜视频的剧本内容,通过接口返回多个ScriptSliceEntity片段 */ const fetchShotVideoScript = useCallback(async () => { if (!selectedShot) { setError("未选择分镜"); return; } try { setLoading(true); setError(null); const response = await getShotVideoScript({ shotId: selectedShot.entity.id, }); if (response.successful) { const script = new ScriptValueObject(response.data); setShotVideoScript(script.scriptSlices); } else { setError(`获取分镜视频剧本失败: ${response.message}`); } } catch (err) { setError( `获取分镜视频剧本失败: ${ err instanceof Error ? err.message : "未知错误" }` ); } finally { setLoading(false); } }, [selectedShot]); /** * 获取分镜关联的角色信息 * @description 获取当前分镜可以使用的角色列表 * @returns Promise 角色信息列表 */ const getShotRoles = useCallback(async (): Promise => { if (!shotEditUseCase) { throw new Error("分镜编辑用例未初始化"); } return await shotEditUseCase.getShotRoles(); }, [shotEditUseCase]); /** * 获取分镜关联的场景信息 * @description 获取当前分镜可以使用的场景列表 * @returns Promise 场景信息列表 */ const getShotScenes = useCallback(async (): Promise => { if (!shotEditUseCase) { throw new Error("分镜编辑用例未初始化"); } return await shotEditUseCase.getShotScenes(); }, [shotEditUseCase]); /** * 重新生成分镜 * @description 使用镜头、对话内容、角色ID替换参数、场景ID替换参数重新生成分镜 * @param shotPrompt 镜头描述 * @param dialogueContent 对话内容 * @param roleReplaceParams 角色ID替换参数,格式为{oldId:string,newId:string}[] * @param sceneReplaceParams 场景ID替换参数,格式为{oldId:string,newId:string}[] */ const regenerateShot = useCallback( async ( shotPrompt: string, dialogueContent: ContentItem[], roleReplaceParams: { oldId: string; newId: string }[], sceneReplaceParams: { oldId: string; newId: string }[] ) => { if (!shotEditUseCase) { setError("分镜编辑用例未初始化"); return; } try { setLoading(true); setError(null); const updatedShot = await shotEditUseCase.regenerateShot( shotPrompt, dialogueContent, roleReplaceParams, sceneReplaceParams ); setSelectedShot(new ShotItem(updatedShot)); } catch (err) { setError( `重新生成分镜失败: ${err instanceof Error ? err.message : "未知错误"}` ); } finally { setLoading(false); } }, [shotEditUseCase] ); return { // 响应式状态 - 用于UI组件订阅和渲染 /** 分镜列表 - 当前项目的所有分镜数据 */ shotList, /** 当前选中的分镜 - 包含分镜实体和编辑状态 */ selectedShot, /** 当前分镜的草图数据URL - 从分镜实体中获取的草图图片链接 */ shotSketchData, /** 当前分镜的视频数据URL - 从分镜实体中获取的视频链接 */ shotVideoData, /** 用户角色库 - 当前用户可用的所有角色数据 */ userRoleLibrary, /** 当前分镜的视频剧本片段 - 通过接口获取的剧本内容 */ shotVideoScript, /** 加载状态 - 标识当前是否有异步操作正在进行 */ loading, /** 错误信息 - 记录最近一次操作的错误信息 */ error, // 操作方法 - 提供给UI组件调用的业务逻辑方法 /** 获取分镜列表 - 根据项目ID获取所有分镜数据 */ fetchShotList, /** 选择分镜并获取详情 - 选择指定分镜并初始化相关数据 */ selectShot, /** 修改分镜对话内容 - 更新分镜的对话内容,保持ContentItem结构不变 */ updateShotContent: updateShotContentHandler, /** 修改分镜镜头 - 更新分镜的镜头数据 */ updateShotShot: updateShotShotHandler, /** 获取用户角色库 - 获取当前用户的所有角色数据 */ fetchUserRoleLibrary, /** 替换分镜角色 - 将分镜中的角色替换为角色库中的另一个角色 */ replaceShotRole: replaceShotRoleHandler, /** 获取分镜视频剧本内容 - 通过接口获取视频剧本片段 */ fetchShotVideoScript, /** 获取分镜关联的角色信息 - 获取当前分镜可用的角色列表 */ getShotRoles, /** 获取分镜关联的场景信息 - 获取当前分镜可用的场景列表 */ getShotScenes, /** 重新生成分镜 - 使用新参数重新生成分镜内容 */ regenerateShot, }; };