forked from 77media/video-flow
数据结构修改
This commit is contained in:
parent
46bda04605
commit
ce3497003a
@ -14,6 +14,7 @@ import {
|
|||||||
LensType,
|
LensType,
|
||||||
ScriptSlice,
|
ScriptSlice,
|
||||||
} from "@/app/service/domain/valueObject";
|
} from "@/app/service/domain/valueObject";
|
||||||
|
import { VideoSegmentEntityAdapter } from "@/app/service/adapter/oldErrAdapter";
|
||||||
|
|
||||||
// API 响应类型
|
// API 响应类型
|
||||||
interface BaseApiResponse<T> {
|
interface BaseApiResponse<T> {
|
||||||
@ -505,16 +506,18 @@ export const getShotData = async (request: {
|
|||||||
* @returns Promise<ApiResponse<重新生成的分镜>>
|
* @returns Promise<ApiResponse<重新生成的分镜>>
|
||||||
*/
|
*/
|
||||||
export const regenerateShot = async (request: {
|
export const regenerateShot = async (request: {
|
||||||
|
/** 项目ID */
|
||||||
|
project_id:string;
|
||||||
/** 分镜ID */
|
/** 分镜ID */
|
||||||
shotId?: string;
|
shot_id?: string;
|
||||||
/** 镜头描述 */
|
/** 镜头描述 */
|
||||||
shotPrompt?: LensType[];
|
shot_descriptions?: LensType[];
|
||||||
/** 角色ID替换参数,格式为{oldId:string,newId:string}[] */
|
/** 角色ID替换参数,格式为{oldId:string,newId:string}[] */
|
||||||
roleReplaceParams?: { oldId: string; newId: string }[];
|
roleReplaceParams?: { oldId: string; newId: string }[];
|
||||||
/** 场景ID替换参数,格式为{oldId:string,newId:string}[] */
|
/** 场景ID替换参数,格式为{oldId:string,newId:string}[] */
|
||||||
sceneReplaceParams?: { oldId: string; newId: string }[];
|
sceneReplaceParams?: { oldId: string; newId: string }[];
|
||||||
}): Promise<ApiResponse<VideoSegmentEntity>> => {
|
}): Promise<ApiResponse<VideoSegmentEntity>> => {
|
||||||
return post<ApiResponse<any>>("/movie/regenerate_shot", request);
|
return post<ApiResponse<any>>("/movie/regenerate_shot_video", request);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -525,9 +528,9 @@ export const regenerateShot = async (request: {
|
|||||||
*/
|
*/
|
||||||
export const getShotList = async (request: {
|
export const getShotList = async (request: {
|
||||||
/** 项目ID */
|
/** 项目ID */
|
||||||
projectId: string;
|
project_id: string;
|
||||||
}): Promise<ApiResponse<VideoSegmentEntity[]>> => {
|
}): Promise<ApiResponse<VideoSegmentEntityAdapter>> => {
|
||||||
return post<ApiResponse<any>>("/movie/get_shot_list", request);
|
return post<ApiResponse<any>>("/movie_cut/get_task_list", request);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -742,3 +745,72 @@ export const optimizeShotContent = async (request: {
|
|||||||
}): Promise<ApiResponse<LensType[]>> => {
|
}): Promise<ApiResponse<LensType[]>> => {
|
||||||
return post<ApiResponse<LensType[]>>("/api/v1/shot/optimize_content", request);
|
return post<ApiResponse<LensType[]>>("/api/v1/shot/optimize_content", request);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停电影项目计划
|
||||||
|
* @param request - 暂停项目请求参数
|
||||||
|
* @returns Promise<ApiResponse<暂停结果>>
|
||||||
|
*/
|
||||||
|
export const pauseMovieProjectPlan = async (request: {
|
||||||
|
/** 项目ID */
|
||||||
|
project_id: string;
|
||||||
|
}): Promise<ApiResponse<{
|
||||||
|
/** 项目ID */
|
||||||
|
project_id: string;
|
||||||
|
/** 暂停结果描述 */
|
||||||
|
message: string;
|
||||||
|
}>> => {
|
||||||
|
return post("/movie/pause_movie_project_plan", request);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 继续电影项目计划
|
||||||
|
* @param request - 继续项目请求参数
|
||||||
|
* @returns Promise<ApiResponse<继续结果>>
|
||||||
|
*/
|
||||||
|
export const resumeMovieProjectPlan = async (request: {
|
||||||
|
/** 项目ID */
|
||||||
|
project_id: string;
|
||||||
|
}): Promise<ApiResponse<{
|
||||||
|
/** 项目ID */
|
||||||
|
project_id: string;
|
||||||
|
/** 继续结果描述 */
|
||||||
|
message: string;
|
||||||
|
}>> => {
|
||||||
|
return post("/movie/resume_movie_project_plan", request);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI优化角色描述
|
||||||
|
* @param request - AI优化角色描述请求参数
|
||||||
|
* @returns Promise<ApiResponse<优化后的角色描述>>
|
||||||
|
*/
|
||||||
|
export const optimizeRoleDescription = async (request: {
|
||||||
|
/** 角色ID */
|
||||||
|
roleId: string;
|
||||||
|
/** 用户优化建议 */
|
||||||
|
userSuggestion: string;
|
||||||
|
/** 角色描述文本 */
|
||||||
|
roleDescription: string;
|
||||||
|
}): Promise<ApiResponse<{
|
||||||
|
/** 优化后的角色描述 */
|
||||||
|
optimizedDescription: string;
|
||||||
|
}>> => {
|
||||||
|
return post("/movie/optimize_role_description", request);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新分镜提示词数据
|
||||||
|
* @param request - 更新分镜提示词请求参数
|
||||||
|
* @returns Promise<ApiResponse<any>> 更新结果
|
||||||
|
*/
|
||||||
|
export const updateShotPrompt = async (request: {
|
||||||
|
/** 项目ID */
|
||||||
|
project_id: string;
|
||||||
|
/** 分镜ID */
|
||||||
|
shot_id: string;
|
||||||
|
/** 镜头描述 */
|
||||||
|
shot_descriptions: LensType[];
|
||||||
|
}): Promise<ApiResponse<any>> => {
|
||||||
|
return post("/movie/update_shot_prompt", request);
|
||||||
|
};
|
||||||
|
|||||||
@ -1,27 +1,7 @@
|
|||||||
import { useState, useCallback, useMemo } from 'react';
|
import { useState, useCallback, useMemo } from 'react';
|
||||||
import { RoleEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities';
|
import { RoleEntity, AITextEntity } from '../domain/Entities';
|
||||||
import { TagValueObject } from '../domain/valueObject';
|
|
||||||
import { RoleItem, TagItem, TextItem } from '../domain/Item';
|
|
||||||
import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
|
import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
|
||||||
import { TagEditUseCase } from '../usecase/TagEditUseCase';
|
import { getRoleData, getRoleList, getUserRoleLibrary, replaceRole } from '@/api/video_flow';
|
||||||
import { TextEditUseCase } from '../usecase/TextEditUseCase';
|
|
||||||
import { getRoleShots, getRoleData, getRoleList, getUserRoleLibrary, replaceRole } from '@/api/video_flow';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分镜选择项接口
|
|
||||||
*/
|
|
||||||
interface ShotSelectionItem {
|
|
||||||
/** 分镜ID */
|
|
||||||
id: string;
|
|
||||||
/** 分镜名称 */
|
|
||||||
name: string;
|
|
||||||
/** 是否已选中 */
|
|
||||||
selected: boolean;
|
|
||||||
/** 是否已应用角色 */
|
|
||||||
applied: boolean;
|
|
||||||
/** 分镜数据 */
|
|
||||||
shot: VideoSegmentEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色服务Hook返回值接口
|
* 角色服务Hook返回值接口
|
||||||
@ -29,51 +9,30 @@ interface ShotSelectionItem {
|
|||||||
interface UseRoleService {
|
interface UseRoleService {
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
/** 角色列表 */
|
/** 角色列表 */
|
||||||
roleList: RoleItem[];
|
roleList: RoleEntity[];
|
||||||
/** 当前选中的角色 */
|
/** 当前选中的角色 */
|
||||||
selectedRole: RoleItem | null;
|
selectedRole: RoleEntity | null;
|
||||||
/** 当前角色的AI文本 */
|
/** 当前角色的AI文本 */
|
||||||
currentRoleText: TextItem | null;
|
currentRoleText: string | null;
|
||||||
/** 当前角色的标签列表 */
|
|
||||||
currentRoleTags: TagItem[];
|
|
||||||
/** 角色图片URL */
|
/** 角色图片URL */
|
||||||
roleImageUrl: string;
|
roleImageUrl: string;
|
||||||
/** 分镜选择列表 */
|
|
||||||
shotSelectionList: ShotSelectionItem[];
|
|
||||||
/** 是否全选分镜 */
|
|
||||||
isAllShotsSelected: boolean;
|
|
||||||
/** 已选中的分镜数量 */
|
|
||||||
selectedShotsCount: number;
|
|
||||||
/** 用户角色库 */
|
/** 用户角色库 */
|
||||||
userRoleLibrary: RoleItem[];
|
userRoleLibrary: RoleEntity[];
|
||||||
// 操作方法
|
// 操作方法
|
||||||
/** 获取角色列表 */
|
/** 获取角色列表 */
|
||||||
fetchRoleList: (projectId: string) => Promise<void>;
|
fetchRoleList: (projectId: string) => Promise<void>;
|
||||||
/** 选择角色 */
|
/** 选择角色 */
|
||||||
selectRole: (roleId: string) => void;
|
selectRole: (roleId: string) => Promise<void>;
|
||||||
/** 初始化当前选中角色的AI文本和标签数据 */
|
|
||||||
initializeRoleData: () => Promise<void>;
|
|
||||||
/** 优化AI文本 */
|
/** 优化AI文本 */
|
||||||
optimizeRoleText: () => Promise<void>;
|
optimizeRoleText: (userSuggestion: string) => Promise<void>;
|
||||||
/** 修改AI文本 */
|
/** 修改AI文本 */
|
||||||
updateRoleText: (newContent: string) => Promise<void>;
|
updateRoleText: (newContent: string) => Promise<void>;
|
||||||
/** 修改标签内容 */
|
|
||||||
updateTagContent: (tagId: string, newContent: string | number) => Promise<void>;
|
|
||||||
/** 重新生成角色 */
|
/** 重新生成角色 */
|
||||||
regenerateRole: () => Promise<void>;
|
regenerateRole: () => Promise<void>;
|
||||||
/** 获取角色出现的分镜列表 */
|
|
||||||
fetchRoleShots: () => Promise<void>;
|
|
||||||
/** 切换全选与全不选 */
|
|
||||||
toggleSelectAllShots: () => void;
|
|
||||||
/** 选择/取消选择单个分镜 */
|
|
||||||
toggleShotSelection: (shotId: string) => void;
|
|
||||||
/** 应用角色到选中的分镜 */
|
|
||||||
applyRoleToSelectedShots: () => Promise<void>;
|
|
||||||
/** 获取用户角色库 */
|
/** 获取用户角色库 */
|
||||||
fetchUserRoleLibrary: () => Promise<void>;
|
fetchUserRoleLibrary: () => Promise<void>;
|
||||||
/** 替换角色 */
|
/** 替换角色 */
|
||||||
replaceRoleWithLibrary: (replaceRoleId: string) => Promise<void>;
|
replaceRoleWithLibrary: (replaceRoleId: string) => Promise<void>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,17 +41,13 @@ interface UseRoleService {
|
|||||||
*/
|
*/
|
||||||
export const useRoleServiceHook = (): UseRoleService => {
|
export const useRoleServiceHook = (): UseRoleService => {
|
||||||
// 响应式状态
|
// 响应式状态
|
||||||
const [roleList, setRoleList] = useState<RoleItem[]>([]);
|
const [roleList, setRoleList] = useState<RoleEntity[]>([]);
|
||||||
const [selectedRole, setSelectedRole] = useState<RoleItem | null>(null);
|
const [selectedRole, setSelectedRole] = useState<RoleEntity | null>(null);
|
||||||
const [currentRoleText, setCurrentRoleText] = useState<TextItem | null>(null);
|
const [currentRoleText, setCurrentRoleText] = useState<string | null>(null);
|
||||||
const [currentRoleTags, setCurrentRoleTags] = useState<TagItem[]>([]);
|
const [userRoleLibrary, setUserRoleLibrary] = useState<RoleEntity[]>([]);
|
||||||
const [shotSelectionList, setShotSelectionList] = useState<ShotSelectionItem[]>([]);
|
|
||||||
const [userRoleLibrary, setUserRoleLibrary] = useState<RoleItem[]>([]);
|
|
||||||
|
|
||||||
// UseCase实例 - 在角色选择时初始化
|
// UseCase实例 - 在角色选择时初始化
|
||||||
const [roleEditUseCase, setRoleEditUseCase] = useState<RoleEditUseCase | null>(null);
|
const [roleEditUseCase, setRoleEditUseCase] = useState<RoleEditUseCase | null>(null);
|
||||||
const [textEditUseCase, setTextEditUseCase] = useState<TextEditUseCase | null>(null);
|
|
||||||
const [tagEditUseCases, setTagEditUseCases] = useState<Map<string, TagEditUseCase>>(new Map());
|
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
/**
|
/**
|
||||||
@ -100,27 +55,9 @@ export const useRoleServiceHook = (): UseRoleService => {
|
|||||||
* @description 获取当前选中角色的图片URL
|
* @description 获取当前选中角色的图片URL
|
||||||
*/
|
*/
|
||||||
const roleImageUrl = useMemo(() => {
|
const roleImageUrl = useMemo(() => {
|
||||||
return selectedRole?.entity.imageUrl || '';
|
return selectedRole?.imageUrl || '';
|
||||||
}, [selectedRole]);
|
}, [selectedRole]);
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否全选分镜
|
|
||||||
* @description 判断是否所有分镜都被选中
|
|
||||||
*/
|
|
||||||
const isAllShotsSelected = useMemo(() => {
|
|
||||||
return shotSelectionList.length > 0 && shotSelectionList.every(shot => shot.selected);
|
|
||||||
}, [shotSelectionList]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 已选中的分镜数量
|
|
||||||
* @description 获取当前选中的分镜数量
|
|
||||||
*/
|
|
||||||
const selectedShotsCount = useMemo(() => {
|
|
||||||
return shotSelectionList.filter(shot => shot.selected).length;
|
|
||||||
}, [shotSelectionList]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色列表
|
* 获取角色列表
|
||||||
* @description 根据项目ID获取所有角色列表
|
* @description 根据项目ID获取所有角色列表
|
||||||
@ -130,13 +67,9 @@ export const useRoleServiceHook = (): UseRoleService => {
|
|||||||
*/
|
*/
|
||||||
const fetchRoleList = useCallback(async (projectId: string) => {
|
const fetchRoleList = useCallback(async (projectId: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await getRoleList({
|
const response = await getRoleList({ projectId });
|
||||||
projectId: projectId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.successful) {
|
if (response.successful) {
|
||||||
const roleItems = response.data.map(role => new RoleItem(role));
|
setRoleList(response.data);
|
||||||
setRoleList(roleItems);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`获取角色列表失败: ${response.message}`);
|
throw new Error(`获取角色列表失败: ${response.message}`);
|
||||||
}
|
}
|
||||||
@ -146,62 +79,19 @@ export const useRoleServiceHook = (): UseRoleService => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/**
|
|
||||||
* 选择角色
|
|
||||||
* @description 根据角色ID选择角色,并初始化相关的UseCase实例
|
|
||||||
* @param roleId 角色ID
|
|
||||||
*/
|
|
||||||
const selectRole = useCallback(async (roleId: string) => {
|
|
||||||
const role = roleList.find(r => r.entity.id === roleId);
|
|
||||||
if (role) {
|
|
||||||
setSelectedRole(role);
|
|
||||||
|
|
||||||
// 初始化角色编辑UseCase实例
|
|
||||||
setRoleEditUseCase(new RoleEditUseCase(role));
|
|
||||||
|
|
||||||
// 清空文本和标签相关状态
|
|
||||||
setTextEditUseCase(null);
|
|
||||||
setTagEditUseCases(new Map());
|
|
||||||
setCurrentRoleText(null);
|
|
||||||
setCurrentRoleTags([]);
|
|
||||||
await initializeRoleData();
|
|
||||||
}
|
|
||||||
}, [roleList]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化角色数据
|
* 初始化角色数据
|
||||||
* @description 初始化当前选中角色的AI文本和标签数据
|
* @description 初始化当前选中角色的AI文本数据
|
||||||
|
* @param roleId 角色ID
|
||||||
* @throws {Error} 当未选择角色或API调用失败时抛出错误
|
* @throws {Error} 当未选择角色或API调用失败时抛出错误
|
||||||
* @returns {Promise<void>} 初始化完成后的Promise
|
* @returns {Promise<void>} 初始化完成后的Promise
|
||||||
*/
|
*/
|
||||||
const initializeRoleData = useCallback(async () => {
|
const initializeRoleData = useCallback(async (roleId: string) => {
|
||||||
if (!selectedRole) {
|
|
||||||
throw new Error('请先选择角色');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await getRoleData({
|
const response = await getRoleData({ roleId });
|
||||||
roleId: selectedRole.entity.id
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.successful) {
|
if (response.successful) {
|
||||||
const { text, tags } = response.data;
|
const { text } = response.data;
|
||||||
const textItem = new TextItem(text);
|
setCurrentRoleText(text.content);
|
||||||
const tagItems = tags.map(tag => new TagItem(tag));
|
|
||||||
|
|
||||||
// 设置当前角色的AI文本和标签
|
|
||||||
setCurrentRoleText(textItem);
|
|
||||||
setCurrentRoleTags(tagItems);
|
|
||||||
|
|
||||||
// 初始化文本UseCase
|
|
||||||
setTextEditUseCase(new TextEditUseCase(textItem));
|
|
||||||
|
|
||||||
// 初始化标签UseCase
|
|
||||||
const newTagEditUseCases = new Map<string, TagEditUseCase>();
|
|
||||||
tagItems.forEach(tag => {
|
|
||||||
newTagEditUseCases.set(tag.entity.id, new TagEditUseCase(tag));
|
|
||||||
});
|
|
||||||
setTagEditUseCases(newTagEditUseCases);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`获取角色数据失败: ${response.message}`);
|
throw new Error(`获取角色数据失败: ${response.message}`);
|
||||||
}
|
}
|
||||||
@ -209,29 +99,69 @@ export const useRoleServiceHook = (): UseRoleService => {
|
|||||||
console.error('初始化角色数据失败:', error);
|
console.error('初始化角色数据失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}, [selectedRole]);
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择角色
|
||||||
|
* @description 根据角色ID选择角色,并初始化相关的UseCase实例
|
||||||
|
* @param roleId 角色ID
|
||||||
|
*/
|
||||||
|
const selectRole = useCallback(async (roleId: string) => {
|
||||||
|
const role = roleList.find(r => r.id === roleId);
|
||||||
|
if (role) {
|
||||||
|
setSelectedRole(role);
|
||||||
|
|
||||||
|
// 初始化角色编辑UseCase实例
|
||||||
|
const newRoleEditUseCase = new RoleEditUseCase();
|
||||||
|
newRoleEditUseCase.roleList = roleList;
|
||||||
|
await newRoleEditUseCase.selectRole(roleId);
|
||||||
|
setRoleEditUseCase(newRoleEditUseCase);
|
||||||
|
|
||||||
|
// 初始化角色数据
|
||||||
|
await initializeRoleData(roleId);
|
||||||
|
} else {
|
||||||
|
throw new Error('未找到对应的角色');
|
||||||
|
}
|
||||||
|
}, [initializeRoleData, roleList]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 优化AI文本
|
* 优化AI文本
|
||||||
* @description 对当前角色的AI文本进行优化,无文本时不可进行优化
|
* @description 对当前角色的AI文本进行优化
|
||||||
|
* @param userSuggestion 用户优化建议
|
||||||
* @throws {Error} 当没有可优化的文本内容或UseCase未初始化时抛出错误
|
* @throws {Error} 当没有可优化的文本内容或UseCase未初始化时抛出错误
|
||||||
* @returns {Promise<void>} 优化完成后的Promise
|
* @returns {Promise<void>} 优化完成后的Promise
|
||||||
*/
|
*/
|
||||||
const optimizeRoleText = useCallback(async () => {
|
const optimizeRoleText = useCallback(async (userSuggestion: string) => {
|
||||||
if (!textEditUseCase) {
|
if (!roleEditUseCase) {
|
||||||
throw new Error('文本编辑UseCase未初始化');
|
throw new Error('角色编辑UseCase未初始化');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentRoleText || !currentRoleText.entity.content) {
|
if (!currentRoleText) {
|
||||||
throw new Error('没有可优化的文本内容');
|
throw new Error('没有可优化的文本内容');
|
||||||
}
|
}
|
||||||
|
|
||||||
const optimizedContent = await textEditUseCase.getOptimizedContent();
|
try {
|
||||||
|
const optimizedDescription = await roleEditUseCase.optimizeRoleDescription(userSuggestion, currentRoleText);
|
||||||
|
setCurrentRoleText(optimizedDescription);
|
||||||
|
|
||||||
// 更新文本内容
|
// 更新角色列表中的对应角色描述
|
||||||
const updatedTextItem = await textEditUseCase.updateText(optimizedContent);
|
setRoleList(prev =>
|
||||||
setCurrentRoleText(updatedTextItem);
|
prev.map(role =>
|
||||||
}, [textEditUseCase, currentRoleText]);
|
role.id === selectedRole?.id
|
||||||
|
? { ...role, generateText: optimizedDescription }
|
||||||
|
: role
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 更新当前选中角色
|
||||||
|
if (selectedRole) {
|
||||||
|
setSelectedRole({ ...selectedRole, generateText: optimizedDescription });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('优化角色文本失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}, [roleEditUseCase, currentRoleText, selectedRole]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改AI文本
|
* 修改AI文本
|
||||||
@ -241,47 +171,30 @@ export const useRoleServiceHook = (): UseRoleService => {
|
|||||||
* @returns {Promise<void>} 修改完成后的Promise
|
* @returns {Promise<void>} 修改完成后的Promise
|
||||||
*/
|
*/
|
||||||
const updateRoleText = useCallback(async (newContent: string) => {
|
const updateRoleText = useCallback(async (newContent: string) => {
|
||||||
if (!textEditUseCase) {
|
if (!roleEditUseCase) {
|
||||||
throw new Error('文本编辑UseCase未初始化');
|
throw new Error('角色编辑UseCase未初始化');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentRoleText) {
|
setCurrentRoleText(newContent);
|
||||||
throw new Error('没有可编辑的文本');
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedTextItem = await textEditUseCase.updateText(newContent);
|
// 更新角色列表中的对应角色描述
|
||||||
setCurrentRoleText(updatedTextItem);
|
setRoleList(prev =>
|
||||||
}, [textEditUseCase, currentRoleText]);
|
prev.map(role =>
|
||||||
|
role.id === selectedRole?.id
|
||||||
/**
|
? { ...role, generateText: newContent }
|
||||||
* 修改标签内容
|
: role
|
||||||
* @description 修改指定标签的内容
|
|
||||||
* @param tagId 标签ID
|
|
||||||
* @param newContent 新的标签内容
|
|
||||||
* @throws {Error} 当标签不存在或UseCase未初始化时抛出错误
|
|
||||||
* @returns {Promise<void>} 修改完成后的Promise
|
|
||||||
*/
|
|
||||||
const updateTagContent = useCallback(async (tagId: string, newContent: string | number) => {
|
|
||||||
const tagEditUseCase = tagEditUseCases.get(tagId);
|
|
||||||
if (!tagEditUseCase) {
|
|
||||||
throw new Error(`标签编辑UseCase未初始化,标签ID: ${tagId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedTagItem = await tagEditUseCase.updateTag(newContent);
|
|
||||||
|
|
||||||
// 更新标签列表
|
|
||||||
setCurrentRoleTags(prev =>
|
|
||||||
prev.map(tag =>
|
|
||||||
tag.entity.id === tagId
|
|
||||||
? updatedTagItem
|
|
||||||
: tag
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}, [tagEditUseCases]);
|
|
||||||
|
// 更新当前选中角色
|
||||||
|
if (selectedRole) {
|
||||||
|
setSelectedRole({ ...selectedRole, generateText: newContent });
|
||||||
|
}
|
||||||
|
}, [roleEditUseCase, selectedRole]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新生成角色
|
* 重新生成角色
|
||||||
* @description 使用AI文本和标签重新生成角色
|
* @description 使用AI文本重新生成角色
|
||||||
* @throws {Error} 当缺少重新生成角色所需的数据或UseCase未初始化时抛出错误
|
* @throws {Error} 当缺少重新生成角色所需的数据或UseCase未初始化时抛出错误
|
||||||
* @returns {Promise<void>} 重新生成完成后的Promise
|
* @returns {Promise<void>} 重新生成完成后的Promise
|
||||||
*/
|
*/
|
||||||
@ -290,121 +203,27 @@ export const useRoleServiceHook = (): UseRoleService => {
|
|||||||
throw new Error('角色编辑UseCase未初始化');
|
throw new Error('角色编辑UseCase未初始化');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedRole || !currentRoleText || currentRoleTags.length === 0) {
|
if (!selectedRole || !currentRoleText) {
|
||||||
throw new Error('缺少重新生成角色所需的数据');
|
throw new Error('缺少重新生成角色所需的数据');
|
||||||
}
|
}
|
||||||
|
|
||||||
const newRoleEntity = await roleEditUseCase.AIgenerateRole(currentRoleText, currentRoleTags);
|
|
||||||
|
|
||||||
// 更新角色
|
|
||||||
const newRoleItem = new RoleItem(newRoleEntity);
|
|
||||||
setSelectedRole(newRoleItem);
|
|
||||||
|
|
||||||
// 更新角色列表
|
|
||||||
setRoleList(prev =>
|
|
||||||
prev.map(role =>
|
|
||||||
role.entity.id === newRoleEntity.id ? newRoleItem : role
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}, [roleEditUseCase, selectedRole, currentRoleText, currentRoleTags]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取角色出现的分镜列表
|
|
||||||
* @description 获取当前角色应用到的分镜列表,包括已应用状态
|
|
||||||
* @throws {Error} 当未选择角色或API调用失败时抛出错误
|
|
||||||
* @returns {Promise<void>} 获取完成后的Promise
|
|
||||||
*/
|
|
||||||
const fetchRoleShots = useCallback(async () => {
|
|
||||||
if (!selectedRole) {
|
|
||||||
throw new Error('请先选择角色');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await getRoleShots({
|
const newRoleEntity = await roleEditUseCase.AIgenerateRole(currentRoleText);
|
||||||
roleId: selectedRole.entity.id
|
setSelectedRole(newRoleEntity);
|
||||||
});
|
|
||||||
|
|
||||||
if (response.successful) {
|
// 更新角色列表
|
||||||
const { shots, appliedShotIds } = response.data;
|
setRoleList(prev =>
|
||||||
|
prev.map(role =>
|
||||||
const shotSelectionItems: ShotSelectionItem[] = shots.map(shot => ({
|
role.id === newRoleEntity.id
|
||||||
id: shot.id,
|
? newRoleEntity
|
||||||
name: shot.name,
|
: role
|
||||||
selected: false,
|
)
|
||||||
applied: appliedShotIds.includes(shot.id), // 根据API返回的已应用列表判断
|
);
|
||||||
shot
|
|
||||||
}));
|
|
||||||
|
|
||||||
setShotSelectionList(shotSelectionItems);
|
|
||||||
} else {
|
|
||||||
throw new Error(`获取角色分镜列表失败: ${response.message}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取角色分镜列表失败:', error);
|
console.error('重新生成角色失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}, [selectedRole]);
|
}, [roleEditUseCase, selectedRole, currentRoleText]);
|
||||||
|
|
||||||
/**
|
|
||||||
* 切换全选与全不选
|
|
||||||
* @description 如果当前是全选状态则全不选,否则全选
|
|
||||||
*/
|
|
||||||
const toggleSelectAllShots = useCallback(() => {
|
|
||||||
setShotSelectionList(prev => {
|
|
||||||
const isAllSelected = prev.length > 0 && prev.every(shot => shot.selected);
|
|
||||||
return prev.map(shot => ({ ...shot, selected: !isAllSelected }));
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 选择/取消选择单个分镜
|
|
||||||
* @description 切换指定分镜的选择状态
|
|
||||||
* @param shotId 分镜ID
|
|
||||||
*/
|
|
||||||
const toggleShotSelection = useCallback((shotId: string) => {
|
|
||||||
setShotSelectionList(prev =>
|
|
||||||
prev.map(shot =>
|
|
||||||
shot.id === shotId
|
|
||||||
? { ...shot, selected: !shot.selected }
|
|
||||||
: shot
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 应用角色到选中的分镜
|
|
||||||
* @description 将当前角色应用到选中的分镜,并更新应用状态
|
|
||||||
* @throws {Error} 当未选择角色、未选择分镜或UseCase未初始化时抛出错误
|
|
||||||
* @returns {Promise<void>} 应用完成后的Promise
|
|
||||||
*/
|
|
||||||
const applyRoleToSelectedShots = useCallback(async () => {
|
|
||||||
if (!roleEditUseCase) {
|
|
||||||
throw new Error('角色编辑UseCase未初始化');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!selectedRole) {
|
|
||||||
throw new Error('请先选择角色');
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedShotIds = shotSelectionList
|
|
||||||
.filter(shot => shot.selected)
|
|
||||||
.map(shot => shot.id);
|
|
||||||
|
|
||||||
if (selectedShotIds.length === 0) {
|
|
||||||
throw new Error('请先选择要应用的分镜');
|
|
||||||
}
|
|
||||||
|
|
||||||
await roleEditUseCase.applyRole(selectedShotIds);
|
|
||||||
|
|
||||||
// 更新分镜列表,标记已应用
|
|
||||||
setShotSelectionList(prev =>
|
|
||||||
prev.map(shot =>
|
|
||||||
selectedShotIds.includes(shot.id)
|
|
||||||
? { ...shot, applied: true, selected: false }
|
|
||||||
: shot
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}, [roleEditUseCase, selectedRole, shotSelectionList]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户角色库
|
* 获取用户角色库
|
||||||
@ -415,10 +234,8 @@ export const useRoleServiceHook = (): UseRoleService => {
|
|||||||
const fetchUserRoleLibrary = useCallback(async () => {
|
const fetchUserRoleLibrary = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await getUserRoleLibrary();
|
const response = await getUserRoleLibrary();
|
||||||
|
|
||||||
if (response.successful) {
|
if (response.successful) {
|
||||||
const roleItems = response.data.map(role => new RoleItem(role));
|
setUserRoleLibrary(response.data);
|
||||||
setUserRoleLibrary(roleItems);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`获取用户角色库失败: ${response.message}`);
|
throw new Error(`获取用户角色库失败: ${response.message}`);
|
||||||
}
|
}
|
||||||
@ -445,72 +262,51 @@ export const useRoleServiceHook = (): UseRoleService => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await replaceRole({
|
await roleEditUseCase.replaceRoleById(selectedRole.id, replaceRoleId);
|
||||||
currentRoleId: selectedRole.entity.id,
|
|
||||||
replaceRoleId: replaceRoleId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.successful) {
|
// 重新获取角色数据
|
||||||
// 重新获取当前角色的详细数据
|
await initializeRoleData(selectedRole.id);
|
||||||
await initializeRoleData();
|
|
||||||
} else {
|
// 更新角色列表
|
||||||
throw new Error(`替换角色失败: ${response.message}`);
|
const libraryRole = userRoleLibrary.find(role => role.id === replaceRoleId);
|
||||||
|
if (libraryRole) {
|
||||||
|
const updatedRole = {
|
||||||
|
...selectedRole,
|
||||||
|
name: libraryRole.name,
|
||||||
|
generateText: libraryRole.generateText,
|
||||||
|
imageUrl: libraryRole.imageUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
setSelectedRole(updatedRole);
|
||||||
|
setRoleList(prev =>
|
||||||
|
prev.map(role =>
|
||||||
|
role.id === selectedRole.id
|
||||||
|
? updatedRole
|
||||||
|
: role
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('替换角色失败:', error);
|
console.error('替换角色失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}, [selectedRole, roleEditUseCase, initializeRoleData]);
|
}, [selectedRole, roleEditUseCase, userRoleLibrary, initializeRoleData]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
/** 角色列表 */
|
|
||||||
roleList,
|
roleList,
|
||||||
/** 当前选中的角色 */
|
|
||||||
selectedRole,
|
selectedRole,
|
||||||
/** 当前角色的AI文本 */
|
|
||||||
currentRoleText,
|
currentRoleText,
|
||||||
/** 当前角色的标签列表 */
|
|
||||||
currentRoleTags,
|
|
||||||
/** 角色图片URL */
|
|
||||||
roleImageUrl,
|
roleImageUrl,
|
||||||
/** 分镜选择列表 */
|
|
||||||
shotSelectionList,
|
|
||||||
/** 是否全选分镜 */
|
|
||||||
isAllShotsSelected,
|
|
||||||
/** 已选中的分镜数量 */
|
|
||||||
selectedShotsCount,
|
|
||||||
/** 用户角色库 */
|
|
||||||
userRoleLibrary,
|
userRoleLibrary,
|
||||||
// 操作方法
|
|
||||||
/** 获取角色列表 */
|
|
||||||
fetchRoleList,
|
|
||||||
/** 选择角色 */
|
|
||||||
selectRole,
|
|
||||||
/** 初始化当前选中角色的AI文本和标签数据 */
|
|
||||||
initializeRoleData,
|
|
||||||
/** 优化AI文本 */
|
|
||||||
optimizeRoleText,
|
|
||||||
/** 修改AI文本 */
|
|
||||||
updateRoleText,
|
|
||||||
/** 修改标签内容 */
|
|
||||||
updateTagContent,
|
|
||||||
/** 重新生成角色 */
|
|
||||||
regenerateRole,
|
|
||||||
/** 获取角色出现的分镜列表 */
|
|
||||||
fetchRoleShots,
|
|
||||||
/** 切换全选与全不选 */
|
|
||||||
toggleSelectAllShots,
|
|
||||||
/** 选择/取消选择单个分镜 */
|
|
||||||
toggleShotSelection,
|
|
||||||
/** 应用角色到选中的分镜 */
|
|
||||||
applyRoleToSelectedShots,
|
|
||||||
/** 获取用户角色库 */
|
|
||||||
fetchUserRoleLibrary,
|
|
||||||
/** 替换角色 */
|
|
||||||
replaceRoleWithLibrary,
|
|
||||||
|
|
||||||
|
// 操作方法
|
||||||
|
fetchRoleList,
|
||||||
|
selectRole,
|
||||||
|
optimizeRoleText,
|
||||||
|
updateRoleText,
|
||||||
|
regenerateRole,
|
||||||
|
fetchUserRoleLibrary,
|
||||||
|
replaceRoleWithLibrary,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
192
app/service/Interaction/RoleShotService.ts
Normal file
192
app/service/Interaction/RoleShotService.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import { useState, useCallback, useMemo } from 'react';
|
||||||
|
import { VideoSegmentEntity } from '../domain/Entities';
|
||||||
|
import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
|
||||||
|
import { getRoleShots, applyRoleToShots } from '@/api/video_flow';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展的视频片段实体,包含选择状态
|
||||||
|
*/
|
||||||
|
interface ExtendedVideoSegmentEntity extends VideoSegmentEntity {
|
||||||
|
/** 是否已选中 */
|
||||||
|
selected?: boolean;
|
||||||
|
/** 是否已应用角色 */
|
||||||
|
applied?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色视频片段服务Hook返回值接口
|
||||||
|
*/
|
||||||
|
interface UseRoleShotService {
|
||||||
|
// 响应式数据
|
||||||
|
/** 分镜选择列表 */
|
||||||
|
shotSelectionList: ExtendedVideoSegmentEntity[];
|
||||||
|
/** 是否全选分镜 */
|
||||||
|
isAllVideoSegmentSelected: boolean;
|
||||||
|
/** 已选中的分镜数量 */
|
||||||
|
selectedVideoSegmentCount: number;
|
||||||
|
/** 当前选中的角色ID */
|
||||||
|
selectedRoleId: string | null;
|
||||||
|
// 操作方法
|
||||||
|
/** 获取角色出现的分镜列表 */
|
||||||
|
fetchRoleShots: (roleId: string) => Promise<void>;
|
||||||
|
/** 切换全选与全不选 */
|
||||||
|
toggleSelectAllShots: () => void;
|
||||||
|
/** 选择/取消选择单个分镜 */
|
||||||
|
toggleShotSelection: (shotId: string) => void;
|
||||||
|
/** 应用角色到选中的分镜 */
|
||||||
|
applyRoleToSelectedShots: (roleId: string) => Promise<void>;
|
||||||
|
/** 清空选择列表 */
|
||||||
|
clearShotSelection: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色视频片段服务Hook
|
||||||
|
* 提供角色与视频片段交互的所有响应式功能和业务逻辑
|
||||||
|
*/
|
||||||
|
export const useRoleShotServiceHook = (): UseRoleShotService => {
|
||||||
|
// 响应式状态
|
||||||
|
const [shotSelectionList, setShotSelectionList] = useState<ExtendedVideoSegmentEntity[]>([]);
|
||||||
|
const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// UseCase实例
|
||||||
|
const [roleEditUseCase, setRoleEditUseCase] = useState<RoleEditUseCase | null>(null);
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
/**
|
||||||
|
* 是否全选分镜
|
||||||
|
* @description 判断是否所有分镜都被选中
|
||||||
|
*/
|
||||||
|
const isAllVideoSegmentSelected = useMemo(() => {
|
||||||
|
return shotSelectionList.length > 0 && shotSelectionList.every(shot => shot.selected);
|
||||||
|
}, [shotSelectionList]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已选中的分镜数量
|
||||||
|
* @description 获取当前选中的分镜数量
|
||||||
|
*/
|
||||||
|
const selectedVideoSegmentCount = useMemo(() => {
|
||||||
|
return shotSelectionList.filter(shot => shot.selected).length;
|
||||||
|
}, [shotSelectionList]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取角色出现的分镜列表
|
||||||
|
* @description 获取当前角色应用到的分镜列表,包括已应用状态
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @throws {Error} 当API调用失败时抛出错误
|
||||||
|
* @returns {Promise<void>} 获取完成后的Promise
|
||||||
|
*/
|
||||||
|
const fetchRoleShots = useCallback(async (roleId: string) => {
|
||||||
|
try {
|
||||||
|
const response = await getRoleShots({ roleId });
|
||||||
|
if (response.successful) {
|
||||||
|
const { shots, appliedShotIds } = response.data;
|
||||||
|
|
||||||
|
const extendedShots: ExtendedVideoSegmentEntity[] = shots.map(shot => ({
|
||||||
|
...shot,
|
||||||
|
selected: false,
|
||||||
|
applied: appliedShotIds.includes(shot.id)
|
||||||
|
}));
|
||||||
|
|
||||||
|
setShotSelectionList(extendedShots);
|
||||||
|
setSelectedRoleId(roleId);
|
||||||
|
|
||||||
|
// 初始化角色编辑UseCase实例
|
||||||
|
const newRoleEditUseCase = new RoleEditUseCase();
|
||||||
|
setRoleEditUseCase(newRoleEditUseCase);
|
||||||
|
} else {
|
||||||
|
throw new Error(`获取角色分镜列表失败: ${response.message}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取角色分镜列表失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换全选与全不选
|
||||||
|
* @description 如果当前是全选状态则全不选,否则全选
|
||||||
|
*/
|
||||||
|
const toggleSelectAllShots = useCallback(() => {
|
||||||
|
setShotSelectionList(prev => {
|
||||||
|
const isAllSelected = prev.length > 0 && prev.every(shot => shot.selected);
|
||||||
|
return prev.map(shot => ({ ...shot, selected: !isAllSelected }));
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择/取消选择单个分镜
|
||||||
|
* @description 切换指定分镜的选择状态
|
||||||
|
* @param shotId 分镜ID
|
||||||
|
*/
|
||||||
|
const toggleShotSelection = useCallback((shotId: string) => {
|
||||||
|
setShotSelectionList(prev =>
|
||||||
|
prev.map(shot =>
|
||||||
|
shot.id === shotId
|
||||||
|
? { ...shot, selected: !shot.selected }
|
||||||
|
: shot
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用角色到选中的分镜
|
||||||
|
* @description 将当前角色应用到选中的分镜,并更新应用状态
|
||||||
|
* @param roleId 角色ID
|
||||||
|
* @throws {Error} 当未选择分镜或UseCase未初始化时抛出错误
|
||||||
|
* @returns {Promise<void>} 应用完成后的Promise
|
||||||
|
*/
|
||||||
|
const applyRoleToSelectedShots = useCallback(async (roleId: string) => {
|
||||||
|
if (!roleEditUseCase) {
|
||||||
|
throw new Error('角色编辑UseCase未初始化');
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedShotIds = shotSelectionList
|
||||||
|
.filter(shot => shot.selected)
|
||||||
|
.map(shot => shot.id);
|
||||||
|
|
||||||
|
if (selectedShotIds.length === 0) {
|
||||||
|
throw new Error('请先选择要应用的分镜');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await roleEditUseCase.applyRole(selectedShotIds, roleId);
|
||||||
|
|
||||||
|
// 更新应用状态
|
||||||
|
setShotSelectionList(prev =>
|
||||||
|
prev.map(shot =>
|
||||||
|
selectedShotIds.includes(shot.id)
|
||||||
|
? { ...shot, applied: true, selected: false }
|
||||||
|
: shot
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('应用角色到分镜失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}, [roleEditUseCase, shotSelectionList]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空选择列表
|
||||||
|
* @description 清空所有分镜的选择状态
|
||||||
|
*/
|
||||||
|
const clearShotSelection = useCallback(() => {
|
||||||
|
setShotSelectionList(prev =>
|
||||||
|
prev.map(shot => ({ ...shot, selected: false }))
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 响应式数据
|
||||||
|
shotSelectionList,
|
||||||
|
isAllVideoSegmentSelected,
|
||||||
|
selectedVideoSegmentCount,
|
||||||
|
selectedRoleId,
|
||||||
|
|
||||||
|
// 操作方法
|
||||||
|
fetchRoleShots,
|
||||||
|
toggleSelectAllShots,
|
||||||
|
toggleShotSelection,
|
||||||
|
applyRoleToSelectedShots,
|
||||||
|
clearShotSelection,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,9 +1,8 @@
|
|||||||
import { useState, useCallback, useMemo } from 'react';
|
import { useState, useCallback, useMemo } from 'react';
|
||||||
import { SceneEntity, TagValueObject, AITextEntity, VideoSegmentEntity } from '../domain/Entities';
|
import { SceneEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities';
|
||||||
import { SceneItem, TagItem, TextItem } from '../domain/Item';
|
import { SceneItem, TagItem, TextItem } from '../domain/Item';
|
||||||
import { SceneEditUseCase } from '../usecase/SceneEditUseCase';
|
import { SceneEditUseCase } from '../usecase/SceneEditUseCase';
|
||||||
import { TagEditUseCase } from '../usecase/TagEditUseCase';
|
import { TagEditUseCase } from '../usecase/TagEditUseCase';
|
||||||
import { TextEditUseCase } from '../usecase/TextEditUseCase';
|
|
||||||
import { getSceneShots, getSceneData, getSceneList } from '@/api/video_flow';
|
import { getSceneShots, getSceneData, getSceneList } from '@/api/video_flow';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -22,8 +22,6 @@ export interface UseShotService {
|
|||||||
getVideoSegmentList: (projectId: string) => Promise<void>;
|
getVideoSegmentList: (projectId: string) => Promise<void>;
|
||||||
/** 重新生成视频片段 */
|
/** 重新生成视频片段 */
|
||||||
regenerateVideoSegment: (
|
regenerateVideoSegment: (
|
||||||
shotPrompt: LensType[],
|
|
||||||
shotId?: string,
|
|
||||||
roleReplaceParams?: { oldId: string; newId: string }[],
|
roleReplaceParams?: { oldId: string; newId: string }[],
|
||||||
sceneReplaceParams?: { oldId: string; newId: string }[]
|
sceneReplaceParams?: { oldId: string; newId: string }[]
|
||||||
) => Promise<VideoSegmentEntity>;
|
) => Promise<VideoSegmentEntity>;
|
||||||
@ -55,7 +53,7 @@ export const useShotService = (): UseShotService => {
|
|||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [videoSegments, setVideoSegments] = useState<VideoSegmentEntity[]>([]);
|
const [videoSegments, setVideoSegments] = useState<VideoSegmentEntity[]>([]);
|
||||||
const [selectedSegment, setSelectedSegment] = useState<VideoSegmentEntity | null>(null);
|
const [selectedSegment, setSelectedSegment] = useState<VideoSegmentEntity | null>(null);
|
||||||
|
const [projectId, setProjectId] = useState<string>("");
|
||||||
// UseCase实例
|
// UseCase实例
|
||||||
const [vidoEditUseCase] = useState<VideoSegmentEditUseCase>(
|
const [vidoEditUseCase] = useState<VideoSegmentEditUseCase>(
|
||||||
new VideoSegmentEditUseCase()
|
new VideoSegmentEditUseCase()
|
||||||
@ -71,6 +69,9 @@ export const useShotService = (): UseShotService => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const segments = await vidoEditUseCase.getVideoSegmentList(projectId);
|
const segments = await vidoEditUseCase.getVideoSegmentList(projectId);
|
||||||
|
setProjectId(projectId);
|
||||||
|
console.log('segments', segments);
|
||||||
|
|
||||||
setVideoSegments(segments);
|
setVideoSegments(segments);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取视频片段列表失败:", error);
|
console.error("获取视频片段列表失败:", error);
|
||||||
@ -91,8 +92,6 @@ export const useShotService = (): UseShotService => {
|
|||||||
*/
|
*/
|
||||||
const regenerateVideoSegment = useCallback(
|
const regenerateVideoSegment = useCallback(
|
||||||
async (
|
async (
|
||||||
shotPrompt: LensType[],
|
|
||||||
shotId?: string,
|
|
||||||
roleReplaceParams?: { oldId: string; newId: string }[],
|
roleReplaceParams?: { oldId: string; newId: string }[],
|
||||||
sceneReplaceParams?: { oldId: string; newId: string }[]
|
sceneReplaceParams?: { oldId: string; newId: string }[]
|
||||||
): Promise<VideoSegmentEntity> => {
|
): Promise<VideoSegmentEntity> => {
|
||||||
@ -100,17 +99,18 @@ export const useShotService = (): UseShotService => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const regeneratedSegment = await vidoEditUseCase.regenerateVideoSegment(
|
const regeneratedSegment = await vidoEditUseCase.regenerateVideoSegment(
|
||||||
shotPrompt,
|
projectId,
|
||||||
shotId,
|
selectedSegment!.lens,
|
||||||
|
selectedSegment!.id,
|
||||||
roleReplaceParams,
|
roleReplaceParams,
|
||||||
sceneReplaceParams
|
sceneReplaceParams
|
||||||
);
|
);
|
||||||
|
|
||||||
// 如果重新生成的是现有片段,更新列表中的对应项
|
// 如果重新生成的是现有片段,更新列表中的对应项
|
||||||
if (shotId) {
|
if (selectedSegment) {
|
||||||
setVideoSegments(prev =>
|
setVideoSegments(prev =>
|
||||||
prev.map(segment =>
|
prev.map(segment =>
|
||||||
segment.id === shotId ? regeneratedSegment : segment
|
segment.id === selectedSegment.id ? regeneratedSegment : segment
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -126,7 +126,7 @@ export const useShotService = (): UseShotService => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[vidoEditUseCase]
|
[projectId, selectedSegment, vidoEditUseCase]
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
250
app/service/adapter/oldErrAdapter.ts
Normal file
250
app/service/adapter/oldErrAdapter.ts
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
|
||||||
|
/**============因协同任务开发流程没有明确管理,导致的必要的适配=================**/
|
||||||
|
|
||||||
|
import { VideoSegmentEntity } from "../domain/Entities";
|
||||||
|
import { LensType, ContentItem } from "../domain/valueObject";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 视频片段后端结构
|
||||||
|
*/
|
||||||
|
export class VideoSegmentEntityAdapter {
|
||||||
|
/** 原始文本 */
|
||||||
|
original_text: string = "";
|
||||||
|
/** 任务状态 */
|
||||||
|
task_status: string = "";
|
||||||
|
/** 任务结果 */
|
||||||
|
task_result: Array<{
|
||||||
|
/** 叙事目标 */
|
||||||
|
narrative_goal: string;
|
||||||
|
/** 镜头1描述 */
|
||||||
|
shot_1: string;
|
||||||
|
/** 镜头2描述 */
|
||||||
|
shot_2: string;
|
||||||
|
/** 镜头3描述 */
|
||||||
|
shot_3: string;
|
||||||
|
/** 镜头4描述 */
|
||||||
|
shot_4: string;
|
||||||
|
/** 镜头5描述 */
|
||||||
|
shot_5: string;
|
||||||
|
/** 镜头6描述 */
|
||||||
|
shot_6: string;
|
||||||
|
/** 镜头7描述 */
|
||||||
|
shot_7: string;
|
||||||
|
/** 镜头8描述 */
|
||||||
|
shot_8: string;
|
||||||
|
/** 镜头9描述 */
|
||||||
|
shot_9: string;
|
||||||
|
/** 镜头10描述 */
|
||||||
|
shot_10: string;
|
||||||
|
/** 视频列表 */
|
||||||
|
videos: Array<{
|
||||||
|
/** 视频地址 */
|
||||||
|
video_url: string;
|
||||||
|
}>;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 解析shotContent,分离镜头描述和对话内容
|
||||||
|
* @param shotContent 原始shot内容
|
||||||
|
* @returns {description: string, dialogues: ContentItem[]} 解析后的描述和对话内容
|
||||||
|
*/
|
||||||
|
static parseShotContent(shotContent: string): { description: string; dialogues: ContentItem[] } {
|
||||||
|
const lines = shotContent.split('\n').map(line => line.trim()).filter(line => line.length > 0);
|
||||||
|
const dialogues: ContentItem[] = [];
|
||||||
|
let descriptionLines: string[] = [];
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
// 检查是否是对话行,格式:人物名称[CH-XX]: 对话内容
|
||||||
|
const dialogueMatch = line.match(/^(.+?)\s*\[CH-\d+\]:\s*(.+)$/);
|
||||||
|
if (dialogueMatch) {
|
||||||
|
const roleName = dialogueMatch[1].trim();
|
||||||
|
const content = dialogueMatch[2].trim();
|
||||||
|
dialogues.push({
|
||||||
|
roleName,
|
||||||
|
content
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 如果不是对话行,则认为是描述内容
|
||||||
|
descriptionLines.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const description = descriptionLines.join('\n');
|
||||||
|
return { description, dialogues };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 将后端数据结构转换为VideoSegmentEntity数组
|
||||||
|
* @param data 后端数据结构
|
||||||
|
* @returns VideoSegmentEntity[] 视频片段实体数组
|
||||||
|
*/
|
||||||
|
static toVideoSegmentEntity(data: VideoSegmentEntityAdapter): VideoSegmentEntity[] {
|
||||||
|
const entities: VideoSegmentEntity[] = [];
|
||||||
|
|
||||||
|
if (data.task_result && data.task_result.length > 0) {
|
||||||
|
// 遍历task_result中的每一项
|
||||||
|
data.task_result.forEach((result, index) => {
|
||||||
|
// 从task_result中提取镜头信息
|
||||||
|
const lens: LensType[] = [];
|
||||||
|
|
||||||
|
// 处理镜头1到镜头10
|
||||||
|
for (let i = 1; i <= 10; i++) {
|
||||||
|
const shotKey = `shot_${i}` as keyof typeof result;
|
||||||
|
const shotContent = (result as any)[shotKey] as string;
|
||||||
|
if (shotContent && shotContent.trim()) {
|
||||||
|
// 解析shotContent,分离镜头描述和对话内容
|
||||||
|
const { description, dialogues } = this.parseShotContent(shotContent);
|
||||||
|
|
||||||
|
// 创建镜头项,包含描述和对话内容
|
||||||
|
const lensItem = new LensType(`shot_${i}`, description, dialogues);
|
||||||
|
lens.push(lensItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有任何镜头但有narrative_goal,将其作为镜头1
|
||||||
|
if (lens.length === 0 && result.narrative_goal) {
|
||||||
|
const narrativeLens = new LensType("镜头1", result.narrative_goal, []);
|
||||||
|
lens.push(narrativeLens);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取视频URL列表
|
||||||
|
const videoUrls: string[] = [];
|
||||||
|
if (result.videos && result.videos.length > 0) {
|
||||||
|
videoUrls.push(...result.videos.map(video => video.video_url));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据task_status确定状态
|
||||||
|
let status: 0 | 1 | 2 = 1; // 默认为已完成状态
|
||||||
|
if (data.task_status === "INIT" || data.task_status === "IN_PROGRESS") {
|
||||||
|
status = 0; // 视频加载中(INIT和IN_PROGRESS都当成进行中)
|
||||||
|
} else if (data.task_status === "COMPLETED") {
|
||||||
|
status = 1; // 任务已完成
|
||||||
|
} else if (data.task_status === "FAILED") {
|
||||||
|
status = 2; // 任务失败
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建VideoSegmentEntity
|
||||||
|
const entity: VideoSegmentEntity = {
|
||||||
|
id: `video_mock_${index}`, // 生成临时ID,包含索引
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
loadingProgress: status === 1 ? 100 : status === 0 ? 50 : 0, // 已完成100%,进行中50%,失败0%
|
||||||
|
disableEdit: false,
|
||||||
|
name: `视频片段_${index}`, // 生成临时名称,包含索引
|
||||||
|
sketchUrl: "", // 后端数据中没有sketchUrl,设为空字符串
|
||||||
|
videoUrl: videoUrls,
|
||||||
|
status: status,
|
||||||
|
lens: lens
|
||||||
|
};
|
||||||
|
|
||||||
|
entities.push(entity);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 将VideoSegmentEntity数组转换为后端数据结构
|
||||||
|
* @param entities 视频片段实体数组
|
||||||
|
* @returns VideoSegmentEntityAdapter 后端数据结构
|
||||||
|
*/
|
||||||
|
static fromVideoSegmentEntity(entities: VideoSegmentEntity[]): VideoSegmentEntityAdapter {
|
||||||
|
const taskResults: Array<{
|
||||||
|
narrative_goal: string;
|
||||||
|
shot_1: string;
|
||||||
|
shot_2: string;
|
||||||
|
shot_3: string;
|
||||||
|
shot_4: string;
|
||||||
|
shot_5: string;
|
||||||
|
shot_6: string;
|
||||||
|
shot_7: string;
|
||||||
|
shot_8: string;
|
||||||
|
shot_9: string;
|
||||||
|
shot_10: string;
|
||||||
|
videos: Array<{ video_url: string }>;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
// 遍历每个实体,转换为task_result项
|
||||||
|
entities.forEach(entity => {
|
||||||
|
// 从lens中提取镜头描述(支持镜头1到镜头10)
|
||||||
|
const shots: { [key: string]: string } = {};
|
||||||
|
for (let i = 1; i <= 10; i++) {
|
||||||
|
const lensItem = entity.lens.find(lens => lens.name === `shot_${i}`);
|
||||||
|
if (lensItem) {
|
||||||
|
// 重新组合镜头描述和对话内容
|
||||||
|
let fullContent = lensItem.script;
|
||||||
|
|
||||||
|
// 如果有对话内容,添加到镜头描述后面
|
||||||
|
if (lensItem.content && lensItem.content.length > 0) {
|
||||||
|
const dialogueLines = lensItem.content.map(dialogue =>
|
||||||
|
`${dialogue.roleName} [CH-01]: ${dialogue.content}`
|
||||||
|
);
|
||||||
|
fullContent += '\n' + dialogueLines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
shots[`shot_${i}`] = fullContent;
|
||||||
|
} else {
|
||||||
|
shots[`shot_${i}`] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有更多镜头,可以合并到narrative_goal中
|
||||||
|
let narrative_goal = "";
|
||||||
|
const additionalLenses = entity.lens
|
||||||
|
.filter(lens => !lens.name.match(/^shot_[1-9]$|^shot_10$/))
|
||||||
|
.map(lens => `${lens.name}: ${lens.script}`)
|
||||||
|
.join("; ");
|
||||||
|
|
||||||
|
if (additionalLenses) {
|
||||||
|
narrative_goal = additionalLenses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建videos数组
|
||||||
|
const videos = entity.videoUrl.map(url => ({
|
||||||
|
video_url: url
|
||||||
|
}));
|
||||||
|
|
||||||
|
taskResults.push({
|
||||||
|
narrative_goal: narrative_goal,
|
||||||
|
shot_1: shots.shot_1 || "",
|
||||||
|
shot_2: shots.shot_2 || "",
|
||||||
|
shot_3: shots.shot_3 || "",
|
||||||
|
shot_4: shots.shot_4 || "",
|
||||||
|
shot_5: shots.shot_5 || "",
|
||||||
|
shot_6: shots.shot_6 || "",
|
||||||
|
shot_7: shots.shot_7 || "",
|
||||||
|
shot_8: shots.shot_8 || "",
|
||||||
|
shot_9: shots.shot_9 || "",
|
||||||
|
shot_10: shots.shot_10 || "",
|
||||||
|
videos: videos
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 根据第一个实体的status确定task_status(如果数组为空,默认为COMPLETED)
|
||||||
|
let task_status: string = "COMPLETED";
|
||||||
|
if (entities.length > 0) {
|
||||||
|
const firstEntity = entities[0];
|
||||||
|
if (firstEntity.status === 0) {
|
||||||
|
task_status = "IN_PROGRESS"; // 视频加载中映射为IN_PROGRESS
|
||||||
|
} else if (firstEntity.status === 1) {
|
||||||
|
task_status = "COMPLETED"; // 任务已完成
|
||||||
|
} else if (firstEntity.status === 2) {
|
||||||
|
task_status = "FAILED"; // 任务失败
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建VideoSegmentEntityAdapter
|
||||||
|
const adapter = new VideoSegmentEntityAdapter();
|
||||||
|
adapter.original_text = ""; // 实体中没有original_text,设为空字符串
|
||||||
|
adapter.task_status = task_status;
|
||||||
|
adapter.task_result = taskResults;
|
||||||
|
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**视频片段基础数据 */
|
||||||
|
export interface TaskSketch {
|
||||||
|
url: string;
|
||||||
|
video_id: string;
|
||||||
|
}
|
||||||
@ -66,7 +66,7 @@ export interface VideoSegmentEntity extends BaseEntity {
|
|||||||
sketchUrl: string;
|
sketchUrl: string;
|
||||||
/**视频片段视频Url */
|
/**视频片段视频Url */
|
||||||
videoUrl: string[];
|
videoUrl: string[];
|
||||||
/**视频片段状态 0:草稿加载中 1:视频加载中 2:完成 */
|
/**视频片段状态 0:视频加载中 1:任务已完成 2:任务失败 */
|
||||||
status: 0 | 1 | 2;
|
status: 0 | 1 | 2;
|
||||||
/**镜头项 */
|
/**镜头项 */
|
||||||
lens: LensType[];
|
lens: LensType[];
|
||||||
|
|||||||
@ -25,3 +25,6 @@ export function parseScriptBlock(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { RoleEntity } from '../domain/Entities';
|
import { RoleEntity } from '../domain/Entities';
|
||||||
import { TagValueObject } from '../domain/valueObject';
|
|
||||||
import {
|
import {
|
||||||
applyRoleToShots,
|
applyRoleToShots,
|
||||||
getRoleList,
|
getRoleList,
|
||||||
@ -7,18 +6,20 @@ import {
|
|||||||
getRoleData,
|
getRoleData,
|
||||||
regenerateRole,
|
regenerateRole,
|
||||||
getRoleShots,
|
getRoleShots,
|
||||||
replaceRole
|
replaceRole,
|
||||||
|
optimizeRoleDescription
|
||||||
} from '@/api/video_flow';
|
} from '@/api/video_flow';
|
||||||
import { TagEditUseCase } from './TagEditUseCase';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色图编辑用例
|
* 角色图编辑用例
|
||||||
* 负责角色图内容的初始化、修改和优化
|
* 负责角色图内容的初始化、修改和优化
|
||||||
*/
|
*/
|
||||||
export class RoleEditUseCase {
|
export class RoleEditUseCase {
|
||||||
|
/** 角色列表 */
|
||||||
roleList: RoleEntity[] = [];
|
roleList: RoleEntity[] = [];
|
||||||
|
/** 当前选中的角色 */
|
||||||
selectedRole: RoleEntity | null = null;
|
selectedRole: RoleEntity | null = null;
|
||||||
selectedRoleTags:TagEditUseCase = new TagEditUseCase([]);
|
/** 角色库列表 */
|
||||||
roleLibraryList: RoleEntity[] = [];
|
roleLibraryList: RoleEntity[] = [];
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
@ -59,15 +60,6 @@ export class RoleEditUseCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改某个标签内容
|
|
||||||
* @param tagName 标签名称
|
|
||||||
* @param newContent 新内容
|
|
||||||
*/
|
|
||||||
async updateTag(tagName: string, newContent: string | number): Promise<void> {
|
|
||||||
await this.selectedRoleTags.updateTag(tagName, newContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选中某个角色作为当前活跃角色
|
* 选中某个角色作为当前活跃角色
|
||||||
* @param roleId 角色ID
|
* @param roleId 角色ID
|
||||||
@ -78,12 +70,10 @@ export class RoleEditUseCase {
|
|||||||
const roleEntity = this.roleList.find(role => role.id === roleId);
|
const roleEntity = this.roleList.find(role => role.id === roleId);
|
||||||
if (roleEntity) {
|
if (roleEntity) {
|
||||||
this.selectedRole = roleEntity;
|
this.selectedRole = roleEntity;
|
||||||
// 获取角色数据以获取标签信息
|
// 获取角色数据
|
||||||
const response = await getRoleData({ roleId });
|
const response = await getRoleData({ roleId });
|
||||||
if (response.successful) {
|
if (!response.successful) {
|
||||||
this.selectedRoleTags = new TagEditUseCase(response.data.tags);
|
throw new Error(response.message || '获取角色数据失败');
|
||||||
} else {
|
|
||||||
throw new Error(response.message || '获取角色标签数据失败');
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('未找到对应的角色实体,请先获取角色列表');
|
throw new Error('未找到对应的角色实体,请先获取角色列表');
|
||||||
@ -92,21 +82,19 @@ export class RoleEditUseCase {
|
|||||||
console.error('选择角色失败:', error);
|
console.error('选择角色失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新生成角色
|
* 重新生成角色
|
||||||
* @param prompt 角色提示词
|
* @param prompt 角色提示词
|
||||||
* @param tags 标签列表
|
|
||||||
* @returns Promise<RoleEntity> 重新生成的角色
|
* @returns Promise<RoleEntity> 重新生成的角色
|
||||||
*/
|
*/
|
||||||
async AIgenerateRole(prompt: string, tags: TagValueObject[]): Promise<RoleEntity> {
|
async AIgenerateRole(prompt: string): Promise<RoleEntity> {
|
||||||
try {
|
try {
|
||||||
// 直接使用当前角色的ID,不做任何处理
|
// 直接使用当前角色的ID,不做任何处理
|
||||||
const response = await regenerateRole({
|
const response = await regenerateRole({
|
||||||
prompt,
|
prompt,
|
||||||
tagTypes: tags, // 直接传递完整的标签列表给后端,让后端处理
|
tagTypes: [], // 标签现在只是文本的一部分,传递空数组
|
||||||
roleId: this.selectedRole?.id
|
roleId: this.selectedRole?.id
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -188,4 +176,52 @@ export class RoleEditUseCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: AI优化当前角色描述
|
||||||
|
* @param userSuggestion 用户优化建议
|
||||||
|
* @param roleDescription 角色描述文本
|
||||||
|
* @returns Promise<string> 优化后的角色描述
|
||||||
|
*/
|
||||||
|
async optimizeRoleDescription(userSuggestion: string, roleDescription: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
if (!this.selectedRole) {
|
||||||
|
throw new Error('请先选择角色');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用AI优化角色描述API
|
||||||
|
const response = await optimizeRoleDescription({
|
||||||
|
roleId: this.selectedRole.id,
|
||||||
|
userSuggestion,
|
||||||
|
roleDescription,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.successful) {
|
||||||
|
throw new Error(response.message || 'AI优化角色描述失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
const optimizedDescription = response.data.optimizedDescription;
|
||||||
|
|
||||||
|
// 更新角色列表中的对应角色描述
|
||||||
|
const roleIndex = this.roleList.findIndex(role => role.id === this.selectedRole?.id);
|
||||||
|
if (roleIndex !== -1) {
|
||||||
|
this.roleList[roleIndex].generateText = optimizedDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
return optimizedDescription;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('AI优化角色描述失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 从AI文本描述中解析标签信息(预留函数,未来实现)
|
||||||
|
* @param aiText AI文本描述
|
||||||
|
* @returns Promise<string[]> 解析出的标签列表
|
||||||
|
*/
|
||||||
|
async parseTagsFromAiText(aiText: string): Promise<string[]> {
|
||||||
|
// TODO: 未来实现从AI文本中解析标签的逻辑
|
||||||
|
// 例如:解析文本中的关键词、特征描述等作为标签
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import {
|
|||||||
createMovieProjectV1,
|
createMovieProjectV1,
|
||||||
saveScript,
|
saveScript,
|
||||||
enhanceScriptStream,
|
enhanceScriptStream,
|
||||||
|
pauseMovieProjectPlan,
|
||||||
|
resumeMovieProjectPlan,
|
||||||
} from "@/api/video_flow";
|
} from "@/api/video_flow";
|
||||||
|
|
||||||
export type ScriptEditKey = 'synopsis' | 'categories' | 'protagonist' | 'incitingIncident' | 'problem' | 'conflict' | 'stakes' | 'characterArc';
|
export type ScriptEditKey = 'synopsis' | 'categories' | 'protagonist' | 'incitingIncident' | 'problem' | 'conflict' | 'stakes' | 'characterArc';
|
||||||
@ -262,4 +264,59 @@ export class ScriptEditUseCase {
|
|||||||
// 更新剧本
|
// 更新剧本
|
||||||
this.scriptValueObject = new ScriptValueObject(newScriptText);
|
this.scriptValueObject = new ScriptValueObject(newScriptText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 暂停电影项目计划
|
||||||
|
* @param projectId 项目ID
|
||||||
|
* @returns Promise<void>
|
||||||
|
*/
|
||||||
|
async pauseProject(projectId: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
// 调用暂停项目API
|
||||||
|
const response = await pauseMovieProjectPlan({
|
||||||
|
project_id: projectId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.successful) {
|
||||||
|
throw new Error(response.message || "暂停项目失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("项目暂停成功:", response.data.message);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("暂停项目失败:", error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 继续电影项目计划
|
||||||
|
* @param projectId 项目ID
|
||||||
|
* @returns Promise<void>
|
||||||
|
*/
|
||||||
|
async resumeProject(projectId: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
// 调用继续项目API
|
||||||
|
const response = await resumeMovieProjectPlan({
|
||||||
|
project_id: projectId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.successful) {
|
||||||
|
throw new Error(response.message || "继续项目失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("项目继续成功:", response.data.message);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("继续项目失败:", error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
|
import { VideoSegmentEntityAdapter } from "../adapter/oldErrAdapter";
|
||||||
import { VideoSegmentEntity } from "../domain/Entities";
|
import { VideoSegmentEntity } from "../domain/Entities";
|
||||||
import { LensType } from "../domain/valueObject";
|
import { LensType } from "../domain/valueObject";
|
||||||
import {
|
import {
|
||||||
getShotList,
|
getShotList,
|
||||||
regenerateShot,
|
regenerateShot,
|
||||||
optimizeShotContent,
|
optimizeShotContent,
|
||||||
|
updateShotPrompt,
|
||||||
} from "@/api/video_flow";
|
} from "@/api/video_flow";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,13 +24,13 @@ export class VideoSegmentEditUseCase {
|
|||||||
try {
|
try {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
const response = await getShotList({ projectId });
|
const response = await getShotList({ project_id: projectId });
|
||||||
|
|
||||||
if (!response.successful) {
|
if (!response.successful) {
|
||||||
throw new Error(response.message || "获取视频片段列表失败");
|
throw new Error(response.message || "获取视频片段列表失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.data || [];
|
return VideoSegmentEntityAdapter.toVideoSegmentEntity(response.data) || [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取视频片段列表失败:", error);
|
console.error("获取视频片段列表失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
@ -37,26 +39,68 @@ export class VideoSegmentEditUseCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 保存分镜提示词数据
|
||||||
|
* @param project_id 项目ID
|
||||||
|
* @param shot_id 分镜ID
|
||||||
|
* @param shot_descriptions 镜头描述数据
|
||||||
|
* @returns Promise<any> 保存结果
|
||||||
|
*/
|
||||||
|
async saveShotPrompt(
|
||||||
|
project_id: string,
|
||||||
|
shot_id: string,
|
||||||
|
shot_descriptions: LensType[]
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
const response = await updateShotPrompt({
|
||||||
|
project_id,
|
||||||
|
shot_id,
|
||||||
|
shot_descriptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.successful) {
|
||||||
|
throw new Error(response.message || "保存分镜提示词数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("保存分镜提示词数据失败:", error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 通过视频镜头描述数据重新生成视频
|
* @description 通过视频镜头描述数据重新生成视频
|
||||||
* @param shotPrompt 镜头描述数据
|
* @param project_id 项目ID
|
||||||
* @param shotId 视频片段ID(可选,如果重新生成现有片段)
|
* @param shot_descriptions 镜头描述数据
|
||||||
|
* @param shot_id 视频片段ID(可选,如果重新生成现有片段)
|
||||||
* @param roleReplaceParams 角色替换参数(可选)
|
* @param roleReplaceParams 角色替换参数(可选)
|
||||||
* @param sceneReplaceParams 场景替换参数(可选)
|
* @param sceneReplaceParams 场景替换参数(可选)
|
||||||
* @returns Promise<VideoSegmentEntity> 重新生成的视频片段
|
* @returns Promise<VideoSegmentEntity> 重新生成的视频片段
|
||||||
*/
|
*/
|
||||||
async regenerateVideoSegment(
|
async regenerateVideoSegment(
|
||||||
shotPrompt: LensType[],
|
project_id: string,
|
||||||
shotId?: string,
|
shot_descriptions: LensType[],
|
||||||
|
shot_id?: string,
|
||||||
roleReplaceParams?: { oldId: string; newId: string }[],
|
roleReplaceParams?: { oldId: string; newId: string }[],
|
||||||
sceneReplaceParams?: { oldId: string; newId: string }[]
|
sceneReplaceParams?: { oldId: string; newId: string }[]
|
||||||
): Promise<VideoSegmentEntity> {
|
): Promise<VideoSegmentEntity> {
|
||||||
try {
|
try {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
|
// 如果有shot_id,先保存分镜数据
|
||||||
|
if (shot_id) {
|
||||||
|
await this.saveShotPrompt(project_id, shot_id, shot_descriptions);
|
||||||
|
}
|
||||||
|
|
||||||
const response = await regenerateShot({
|
const response = await regenerateShot({
|
||||||
shotId,
|
project_id,
|
||||||
shotPrompt,
|
shot_id,
|
||||||
|
shot_descriptions,
|
||||||
roleReplaceParams,
|
roleReplaceParams,
|
||||||
sceneReplaceParams,
|
sceneReplaceParams,
|
||||||
});
|
});
|
||||||
@ -69,6 +113,8 @@ export class VideoSegmentEditUseCase {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("重新生成视频片段失败:", error);
|
console.error("重新生成视频片段失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +153,6 @@ export class VideoSegmentEditUseCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 获取加载状态
|
* @description 获取加载状态
|
||||||
* @returns boolean 是否正在加载
|
* @returns boolean 是否正在加载
|
||||||
|
|||||||
@ -110,7 +110,7 @@ export function useWorkflowData() {
|
|||||||
}, [scriptBlocksMemo]);
|
}, [scriptBlocksMemo]);
|
||||||
// 监听继续 请求更新数据
|
// 监听继续 请求更新数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
}, [isPauseWorkFlow]);
|
}, [isPauseWorkFlow]);
|
||||||
|
|
||||||
// 自动开始播放一轮
|
// 自动开始播放一轮
|
||||||
@ -295,6 +295,7 @@ export function useWorkflowData() {
|
|||||||
url: video.urls && video.urls.length > 0 ? video.urls.find((url: string) => url) : null,
|
url: video.urls && video.urls.length > 0 ? video.urls.find((url: string) => url) : null,
|
||||||
script: video.description,
|
script: video.description,
|
||||||
audio: null,
|
audio: null,
|
||||||
|
video_id: video.video_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setTaskVideos(videoList);
|
setTaskVideos(videoList);
|
||||||
@ -507,6 +508,7 @@ export function useWorkflowData() {
|
|||||||
url: video.urls && video.urls.length > 0 ? video.urls.find((url: string) => url) : null,
|
url: video.urls && video.urls.length > 0 ? video.urls.find((url: string) => url) : null,
|
||||||
script: video.description,
|
script: video.description,
|
||||||
audio: null,
|
audio: null,
|
||||||
|
video_id: video.video_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setTaskVideos(videoList);
|
setTaskVideos(videoList);
|
||||||
@ -534,7 +536,7 @@ export function useWorkflowData() {
|
|||||||
finalStep = '5.5';
|
finalStep = '5.5';
|
||||||
loadingText = LOADING_TEXT_MAP.postProduction('generating fine-grained video clips...');
|
loadingText = LOADING_TEXT_MAP.postProduction('generating fine-grained video clips...');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.final_video && data.final_video.video) {
|
if (data.final_video && data.final_video.video) {
|
||||||
setFinal({
|
setFinal({
|
||||||
url: data.final_video.video
|
url: data.final_video.video
|
||||||
@ -617,4 +619,4 @@ export function useWorkflowData() {
|
|||||||
setAnyAttribute,
|
setAnyAttribute,
|
||||||
applyScript
|
applyScript
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user