2025-08-03 15:07:32 +08:00

426 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useCallback, useMemo } from "react";
import {
ShotEntity,
RoleEntity,
SceneEntity,
} from "../domain/Entities";
import { ContentItem, 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<void>;
/** 选择分镜并获取详情 */
selectShot: (shotId: string) => Promise<void>;
/** 修改分镜对话内容 */
updateShotContent: (
newContent: Array<{ roleId: string; content: string }>
) => Promise<void>;
/** 修改分镜镜头 */
updateShotShot: (newShot: string[]) => Promise<void>;
/** 获取用户角色库 */
fetchUserRoleLibrary: () => Promise<void>;
/** 替换分镜角色 */
replaceShotRole: (oldRoleId: string, newRoleId: string) => Promise<void>;
/** 获取分镜视频剧本内容 */
fetchShotVideoScript: () => Promise<void>;
/** 获取分镜关联的角色信息 */
getShotRoles: () => Promise<RoleEntity[]>;
/** 获取分镜关联的场景信息 */
getShotScenes: () => Promise<SceneEntity[]>;
/** 重新生成分镜 */
regenerateShot: (
shotPrompt: string,
dialogueContent: ContentItem[],
roleReplaceParams: { oldId: string; newId: string }[],
sceneReplaceParams: { oldId: string; newId: string }[]
) => Promise<void>;
}
/**
* 分镜服务Hook
* 提供分镜相关的所有状态管理和操作方法
* 包括分镜列表管理、分镜选择、数据获取、内容修改等功能
*/
export const useShotService = (): UseShotService => {
// 响应式状态
const [shotList, setShotList] = useState<ShotEntity[]>([]);
const [selectedShot, setSelectedShot] = useState<ShotItem | null>(null);
const [shotSketchData, setShotSketchData] = useState<string | null>(null);
const [shotVideoData, setShotVideoData] = useState<string[] | null>(null);
const [userRoleLibrary, setUserRoleLibrary] = useState<RoleEntity[]>([]);
const [shotVideoScript, setShotVideoScript] = useState<ScriptSlice[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const [projectId, setProjectId] = useState<string>("");
// UseCase实例
const [shotEditUseCase, setShotEditUseCase] =
useState<ShotEditUseCase | null>(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<RoleEntity[]> 角色信息列表
*/
const getShotRoles = useCallback(async (): Promise<RoleEntity[]> => {
if (!shotEditUseCase) {
throw new Error("分镜编辑用例未初始化");
}
return await shotEditUseCase.getShotRoles();
}, [shotEditUseCase]);
/**
* 获取分镜关联的场景信息
* @description 获取当前分镜可以使用的场景列表
* @returns Promise<SceneEntity[]> 场景信息列表
*/
const getShotScenes = useCallback(async (): Promise<SceneEntity[]> => {
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,
};
};