387 lines
14 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 {
VideoSegmentEntity,
RoleEntity,
SceneEntity,
} from "../domain/Entities";
import { ContentItem, ScriptSlice, ScriptValueObject, LensType } from "../domain/valueObject";
import { VideoSegmentItem } from "../domain/Item";
import { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase";
import {
getShotList,
// getShotDetail,
updateShotContent,
getUserRoleLibrary,
replaceShotRole,
getShotVideoScript,
} from "@/api/video_flow";
/**
* 视频片段服务Hook接口
* 定义视频片段服务Hook的所有状态和操作方法
*/
export interface UseVideoSegmentService {
// 响应式状态
/** 视频片段列表 */
videoSegmentList: VideoSegmentEntity[];
/** 当前选中的视频片段 */
selectedVideoSegment: VideoSegmentItem | null;
/** 当前视频片段的草图数据URL */
videoSegmentSketchData: string | null;
/** 当前视频片段的视频数据URL */
videoSegmentVideoData: string[] | null;
/** 用户角色库 */
userRoleLibrary: RoleEntity[];
/** 当前视频片段的视频剧本片段 */
videoSegmentVideoScript: ScriptSlice[];
/** 加载状态 */
loading: boolean;
/** 错误信息 */
error: string | null;
// 操作方法
/** 获取视频片段列表 */
fetchVideoSegmentList: (projectId: string) => Promise<void>;
/** 选择视频片段并获取详情 */
selectVideoSegment: (videoSegmentId: string) => Promise<void>;
/** 修改视频片段对话内容 */
updateVideoSegmentContent: (
newContent: Array<{ roleId: string; content: string }>
) => Promise<void>;
/** 获取用户角色库 */
fetchUserRoleLibrary: () => Promise<void>;
/** 替换视频片段角色 */
replaceVideoSegmentRole: (oldRoleId: string, newRoleId: string) => Promise<void>;
/** 获取视频片段视频剧本内容 */
fetchVideoSegmentVideoScript: () => Promise<void>;
/** 获取视频片段关联的角色信息 */
getVideoSegmentRoles: () => Promise<RoleEntity[]>;
/** 获取视频片段关联的场景信息 */
getVideoSegmentScenes: () => Promise<SceneEntity[]>;
/** 重新生成视频片段 */
regenerateVideoSegment: (
shotPrompt: LensType[],
dialogueContent: ContentItem[],
roleReplaceParams: { oldId: string; newId: string }[],
sceneReplaceParams: { oldId: string; newId: string }[]
) => Promise<void>;
}
/**
* 视频片段服务Hook
* 提供视频片段相关的所有状态管理和操作方法
* 包括视频片段列表管理、视频片段选择、数据获取、内容修改等功能
*/
export const useVideoSegmentService = (): UseVideoSegmentService => {
// 响应式状态
const [videoSegmentList, setVideoSegmentList] = useState<VideoSegmentEntity[]>([]);
const [selectedVideoSegment, setSelectedVideoSegment] = useState<VideoSegmentItem | null>(null);
const [videoSegmentSketchData, setVideoSegmentSketchData] = useState<string | null>(null);
const [videoSegmentVideoData, setVideoSegmentVideoData] = useState<string[] | null>(null);
const [userRoleLibrary, setUserRoleLibrary] = useState<RoleEntity[]>([]);
const [videoSegmentVideoScript, setVideoSegmentVideoScript] = useState<ScriptSlice[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const [projectId, setProjectId] = useState<string>("");
// UseCase实例
const [videoSegmentEditUseCase, setVideoSegmentEditUseCase] =
useState<VideoSegmentEditUseCase | null>(null);
/**
* 获取视频片段列表
* @description 根据项目ID获取所有视频片段列表
* @param projectId 项目ID
*/
const fetchVideoSegmentList = useCallback(async (projectId: string) => {
setProjectId(projectId);
try {
setLoading(true);
setError(null);
const response = await getShotList({ projectId });
if (response.successful) {
setVideoSegmentList(response.data);
} else {
setError(`获取视频片段列表失败: ${response.message}`);
}
} catch (err) {
setError(
`获取视频片段列表失败: ${err instanceof Error ? err.message : "未知错误"}`
);
} finally {
setLoading(false);
}
}, []);
/**
* 选择视频片段并获取详情
* @description 根据视频片段ID获取视频片段详情并初始化相关的UseCase和数据
* @param videoSegmentId 视频片段ID
*/
const selectVideoSegment = useCallback(async (videoSegmentId: string) => {
try {
setLoading(true);
setError(null);
// 获取视频片段详情
await fetchVideoSegmentList(projectId);
const videoSegmentEntity = videoSegmentList.find(
(videoSegment: VideoSegmentEntity) => videoSegment.id === videoSegmentId
);
if (!videoSegmentEntity) {
setError(`视频片段不存在: ${videoSegmentId}`);
return;
}
const videoSegmentItem = new VideoSegmentItem(videoSegmentEntity);
setSelectedVideoSegment(videoSegmentItem);
// 初始化UseCase
const newVideoSegmentEditUseCase = new VideoSegmentEditUseCase(videoSegmentItem);
setVideoSegmentEditUseCase(newVideoSegmentEditUseCase);
// 从视频片段实体中获取草图数据和视频数据
setVideoSegmentSketchData(videoSegmentEntity.sketchUrl || null);
setVideoSegmentVideoData(videoSegmentEntity.videoUrl || null);
} catch (err) {
setError(
`选择视频片段失败: ${err instanceof Error ? err.message : "未知错误"}`
);
} finally {
setLoading(false);
}
}, []);
/**
* 修改视频片段对话内容
* @description 更新视频片段的对话内容ContentItem数量和ID顺序不能变只能修改content字段
* @param newContent 新的对话内容数组
*/
const updateVideoSegmentContentHandler = useCallback(
async (newContent: Array<{ roleId: string; content: string }>) => {
if (!videoSegmentEditUseCase) {
setError("视频片段编辑用例未初始化");
return;
}
try {
setLoading(true);
setError(null);
const updatedVideoSegment = await videoSegmentEditUseCase.updateVideoSegmentContent(newContent);
setSelectedVideoSegment(new VideoSegmentItem(updatedVideoSegment));
} catch (err) {
setError(
`修改视频片段对话内容失败: ${
err instanceof Error ? err.message : "未知错误"
}`
);
} finally {
setLoading(false);
}
},
[videoSegmentEditUseCase]
);
/**
* 获取用户角色库
* @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 replaceVideoSegmentRoleHandler = useCallback(
async (oldRoleId: string, newRoleId: string) => {
if (!selectedVideoSegment) {
setError("未选择视频片段");
return;
}
try {
setLoading(true);
setError(null);
const response = await replaceShotRole({
shotId: selectedVideoSegment.entity.id,
oldRoleId,
newRoleId,
});
if (response.successful) {
// 重新获取视频片段详情
await selectVideoSegment(selectedVideoSegment.entity.id);
} else {
setError(`替换视频片段角色失败: ${response.message}`);
}
} catch (err) {
setError(
`替换视频片段角色失败: ${err instanceof Error ? err.message : "未知错误"}`
);
} finally {
setLoading(false);
}
},
[selectedVideoSegment, selectVideoSegment]
);
/**
* 获取视频片段视频剧本内容
* @description 获取视频片段视频的剧本内容通过接口返回多个ScriptSliceEntity片段
*/
const fetchVideoSegmentVideoScript = useCallback(async () => {
if (!selectedVideoSegment) {
setError("未选择视频片段");
return;
}
try {
setLoading(true);
setError(null);
const response = await getShotVideoScript({
shotId: selectedVideoSegment.entity.id,
});
if (response.successful) {
const script = new ScriptValueObject(response.data);
setVideoSegmentVideoScript([...script.scriptSlices]);
} else {
setError(`获取视频片段视频剧本失败: ${response.message}`);
}
} catch (err) {
setError(
`获取视频片段视频剧本失败: ${
err instanceof Error ? err.message : "未知错误"
}`
);
} finally {
setLoading(false);
}
}, [selectedVideoSegment]);
/**
* 获取视频片段关联的角色信息
* @description 获取当前视频片段可以使用的角色列表
* @returns Promise<RoleEntity[]> 角色信息列表
*/
const getVideoSegmentRoles = useCallback(async (): Promise<RoleEntity[]> => {
if (!videoSegmentEditUseCase) {
throw new Error("视频片段编辑用例未初始化");
}
return await videoSegmentEditUseCase.getVideoSegmentRoles();
}, [videoSegmentEditUseCase]);
/**
* 获取视频片段关联的场景信息
* @description 获取当前视频片段可以使用的场景列表
* @returns Promise<SceneEntity[]> 场景信息列表
*/
const getVideoSegmentScenes = useCallback(async (): Promise<SceneEntity[]> => {
if (!videoSegmentEditUseCase) {
throw new Error("视频片段编辑用例未初始化");
}
return await videoSegmentEditUseCase.getVideoSegmentScenes();
}, [videoSegmentEditUseCase]);
/**
* 重新生成视频片段
* @description 使用镜头、对话内容、角色ID替换参数、场景ID替换参数重新生成视频片段
* @param shotPrompt 镜头描述
* @param dialogueContent 对话内容
* @param roleReplaceParams 角色ID替换参数格式为{oldId:string,newId:string}[]
* @param sceneReplaceParams 场景ID替换参数格式为{oldId:string,newId:string}[]
*/
const regenerateVideoSegment = useCallback(
async (
shotPrompt: LensType[],
dialogueContent: ContentItem[],
roleReplaceParams: { oldId: string; newId: string }[],
sceneReplaceParams: { oldId: string; newId: string }[]
) => {
if (!videoSegmentEditUseCase) {
setError("视频片段编辑用例未初始化");
return;
}
try {
setLoading(true);
setError(null);
const updatedVideoSegment = await videoSegmentEditUseCase.regenerateVideoSegment(
shotPrompt,
dialogueContent,
roleReplaceParams,
sceneReplaceParams
);
setSelectedVideoSegment(new VideoSegmentItem(updatedVideoSegment));
} catch (err) {
setError(
`重新生成视频片段失败: ${err instanceof Error ? err.message : "未知错误"}`
);
} finally {
setLoading(false);
}
},
[videoSegmentEditUseCase]
);
return {
// 响应式状态 - 用于UI组件订阅和渲染
/** 视频片段列表 - 当前项目的所有视频片段数据 */
videoSegmentList,
/** 当前选中的视频片段 - 包含视频片段实体和编辑状态 */
selectedVideoSegment,
/** 当前视频片段的草图数据URL - 从视频片段实体中获取的草图图片链接 */
videoSegmentSketchData,
/** 当前视频片段的视频数据URL - 从视频片段实体中获取的视频链接 */
videoSegmentVideoData,
/** 用户角色库 - 当前用户可用的所有角色数据 */
userRoleLibrary,
/** 当前视频片段的视频剧本片段 - 通过接口获取的剧本内容 */
videoSegmentVideoScript,
/** 加载状态 - 标识当前是否有异步操作正在进行 */
loading,
/** 错误信息 - 记录最近一次操作的错误信息 */
error,
// 操作方法 - 提供给UI组件调用的业务逻辑方法
/** 获取视频片段列表 - 根据项目ID获取所有视频片段数据 */
fetchVideoSegmentList,
/** 选择视频片段并获取详情 - 选择指定视频片段并初始化相关数据 */
selectVideoSegment,
/** 修改视频片段对话内容 - 更新视频片段的对话内容保持ContentItem结构不变 */
updateVideoSegmentContent: updateVideoSegmentContentHandler,
/** 获取用户角色库 - 获取当前用户的所有角色数据 */
fetchUserRoleLibrary,
/** 替换视频片段角色 - 将视频片段中的角色替换为角色库中的另一个角色 */
replaceVideoSegmentRole: replaceVideoSegmentRoleHandler,
/** 获取视频片段视频剧本内容 - 通过接口获取视频剧本片段 */
fetchVideoSegmentVideoScript,
/** 获取视频片段关联的角色信息 - 获取当前视频片段可用的角色列表 */
getVideoSegmentRoles,
/** 获取视频片段关联的场景信息 - 获取当前视频片段可用的场景列表 */
getVideoSegmentScenes,
/** 重新生成视频片段 - 使用新参数重新生成视频片段内容 */
regenerateVideoSegment,
};
};