forked from 77media/video-flow
382 lines
12 KiB
TypeScript
382 lines
12 KiB
TypeScript
import { NewCharacterItem, CharacterListByProjectItem, CharacterListByProjectWithHighlightResponse, CharacterUpdateAndRegenerateRequest, CharacterUpdateAndRegenerateResponse } from '@/api/allMovieType';
|
||
import { RoleEntity } from '../domain/Entities';
|
||
import {
|
||
applyRoleToShots,
|
||
getRoleList,
|
||
getRoleData,
|
||
regenerateRole,
|
||
getRoleShots,
|
||
replaceRole,
|
||
getAllCharacterList,
|
||
getCharacterListByProjectWithHighlight,
|
||
updateAndRegenerateCharacter,
|
||
getUserRoleLibrary,
|
||
generateCharacterDescription,
|
||
} from '@/api/video_flow';
|
||
|
||
export interface RoleResponse {
|
||
/** 角色描述 */
|
||
character_description: string;
|
||
/** 角色名称 */
|
||
character_name: string;
|
||
/** 高亮关键词 */
|
||
highlights: string[];
|
||
/** 角色图片地址 */
|
||
image_path: string;
|
||
}
|
||
|
||
/**
|
||
* 角色图编辑用例
|
||
* 负责角色图内容的初始化、修改和优化
|
||
*/
|
||
export class RoleEditUseCase {
|
||
/** 角色列表 */
|
||
roleList: RoleEntity[] = [];
|
||
/** 当前选中的角色 */
|
||
selectedRole: RoleEntity | null = null;
|
||
/** 角色库列表 */
|
||
roleLibraryList: RoleEntity[] = [];
|
||
constructor() {
|
||
}
|
||
/**
|
||
* 获取当前项目角色列表
|
||
* @param projectId 项目ID
|
||
* @returns Promise<RoleEntity[]> 角色列表
|
||
*/
|
||
async getRoleList(projectId: string): Promise<RoleEntity[]> {
|
||
try {
|
||
// 使用新的项目角色列表接口
|
||
const response = await getCharacterListByProjectWithHighlight({
|
||
project_id: projectId,
|
||
max_keywords: 6 // 默认提取6个关键词
|
||
});
|
||
|
||
if (response.successful) {
|
||
const roleList = this.parseProjectRoleList(response.data);
|
||
return roleList;
|
||
} else {
|
||
throw new Error(response.message || '获取项目角色列表失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('获取项目角色列表失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 解析新角色列表接口返回的数据
|
||
* @param newCharacterData 新角色列表数据
|
||
* @returns RoleEntity[] 角色实体数组
|
||
*/
|
||
parseNewRoleList(newCharacterData: { data: NewCharacterItem[] }): RoleEntity[] {
|
||
const characters = newCharacterData.data || [];
|
||
|
||
return characters.map((char, index) => {
|
||
const roleEntity: RoleEntity = {
|
||
id: `role_${index + 1}`,
|
||
name: char.character_name || '',
|
||
generateText: char.character_description || '',
|
||
tags: [], // 默认为空标签数组
|
||
imageUrl: char.image_path || '', // 使用API返回的图片路径
|
||
loadingProgress: 100, // 默认加载完成
|
||
disableEdit: false, // 默认允许编辑
|
||
updatedAt: Date.now()
|
||
};
|
||
|
||
return roleEntity;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 解析项目角色列表接口返回的数据
|
||
* @description 将接口返回的项目角色列表数据转换为RoleEntity数组
|
||
* @param {RoleResponse[]} projectRoleData - 项目角色列表数据
|
||
* @returns {RoleEntity[]} 角色实体数组
|
||
* @throws {Error} 如果数据格式不正确则抛出异常
|
||
*/
|
||
/**
|
||
* 解析项目角色列表接口返回的数据
|
||
* @description 将接口返回的项目角色列表数据转换为RoleEntity数组
|
||
* @param {RoleResponse[]} projectRoleData - 项目角色列表数据
|
||
* @returns {RoleEntity[]} 角色实体数组
|
||
* @throws {Error} 如果数据格式不正确则抛出异常
|
||
*/
|
||
parseProjectRoleList(projectRoleData: RoleResponse[]): RoleEntity[] {
|
||
if (!Array.isArray(projectRoleData)) {
|
||
throw new Error('项目角色数据格式错误');
|
||
}
|
||
|
||
return projectRoleData.map((char, index) => {
|
||
/** 角色实体对象 */
|
||
const roleEntity: RoleEntity = {
|
||
id: `role_${index + 1}`,
|
||
name: char.character_name || '',
|
||
generateText: char.character_description || '',
|
||
tags: Array.isArray(char.highlights)
|
||
? char.highlights.map((highlight: string) => ({
|
||
id: `tag_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||
updatedAt: Date.now(),
|
||
/** 名称 */
|
||
name: highlight,
|
||
/** 内容 */
|
||
content: highlight,
|
||
loadingProgress: 100,
|
||
disableEdit: false
|
||
}))
|
||
: [],
|
||
imageUrl: char.image_path || '',
|
||
loadingProgress: 100,
|
||
disableEdit: false,
|
||
updatedAt: Date.now()
|
||
};
|
||
|
||
return roleEntity;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取角色库的角色列表
|
||
* @returns Promise<RoleEntity[]> 角色库列表
|
||
*/
|
||
async getRoleLibraryList(): Promise<RoleEntity[]> {
|
||
try {
|
||
// 使用新的角色列表接口获取角色库
|
||
const response = await getAllCharacterList();
|
||
if (response.successful) {
|
||
const roleList = this.parseNewRoleList(response.data);
|
||
return roleList;
|
||
} else {
|
||
throw new Error(response.message || '获取角色库失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('获取角色库失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 选中某个角色作为当前活跃角色
|
||
* @param roleId 角色ID
|
||
*/
|
||
async selectRole(roleId: string): Promise<void> {
|
||
try {
|
||
// 从已获取的角色列表中查找对应的角色实体
|
||
const roleEntity = this.roleList.find(role => role.id === roleId);
|
||
if (roleEntity) {
|
||
this.selectedRole = roleEntity;
|
||
// 获取角色数据
|
||
const response = await getRoleData({ roleId });
|
||
if (!response.successful) {
|
||
throw new Error(response.message || '获取角色数据失败');
|
||
}
|
||
} else {
|
||
throw new Error('未找到对应的角色实体,请先获取角色列表');
|
||
}
|
||
} catch (error) {
|
||
console.error('选择角色失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* AI生成角色
|
||
* @param projectId 项目ID
|
||
* @param characterName 角色名称
|
||
* @param characterDescription 角色描述
|
||
* @returns Promise<RoleEntity> 生成的角色实体
|
||
*/
|
||
async AIgenerateRole(
|
||
projectId: string,
|
||
characterName: string,
|
||
characterDescription: string
|
||
): Promise<RoleEntity> {
|
||
try {
|
||
// 使用新的角色更新和重新生成接口
|
||
const response = await updateAndRegenerateCharacter({
|
||
project_id: projectId,
|
||
character_name: characterName,
|
||
character_description: characterDescription,
|
||
max_keywords: 6 // 默认提取6个关键词
|
||
});
|
||
|
||
if (!response.successful) {
|
||
throw new Error(response.message || 'AI生成角色失败');
|
||
}
|
||
|
||
const characterData = response.data.character;
|
||
|
||
// 将API响应转换为RoleEntity
|
||
const roleEntity: RoleEntity = {
|
||
id: `role_${Date.now()}`, // 生成唯一ID
|
||
name: characterData.character_name,
|
||
generateText: characterData.character_description,
|
||
tags: (characterData.highlights || []).map(highlight => ({
|
||
id: `tag_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||
updatedAt: Date.now(),
|
||
name: highlight,
|
||
content: highlight,
|
||
loadingProgress: 100,
|
||
disableEdit: false
|
||
})), // 将高亮关键词转换为TagValueObject格式
|
||
imageUrl: characterData.image_path || '',
|
||
loadingProgress: 100,
|
||
disableEdit: false,
|
||
updatedAt: Date.now()
|
||
};
|
||
|
||
return roleEntity;
|
||
} catch (error) {
|
||
console.error('AI生成角色失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 应用此角色到指定分镜
|
||
* @param shotIds 分镜ID列表
|
||
* @param roleId 角色ID
|
||
* @returns 应用结果
|
||
*/
|
||
async applyRole(shotIds: string[], roleId: string) {
|
||
try {
|
||
const response = await applyRoleToShots({
|
||
roleId,
|
||
shotIds,
|
||
});
|
||
|
||
if (response.successful) {
|
||
return response.data;
|
||
} else {
|
||
throw new Error(response.message || '应用角色到分镜失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('应用角色到分镜失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取角色应用到的分镜列表
|
||
* @param roleId 角色ID
|
||
* @returns 分镜列表和应用状态
|
||
*/
|
||
async getRoleShotsList(roleId: string) {
|
||
try {
|
||
const response = await getRoleShots({ roleId });
|
||
if (response.successful) {
|
||
return response.data;
|
||
} else {
|
||
throw new Error(response.message || '获取角色分镜列表失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('获取角色分镜列表失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 替换角色
|
||
* @param currentRoleId 当前角色ID
|
||
* @param replaceRoleId 替换的角色ID
|
||
* @returns 替换结果
|
||
*/
|
||
async replaceRoleById(currentRoleId: string, replaceRoleId: string) {
|
||
try {
|
||
const response = await replaceRole({
|
||
currentRoleId,
|
||
replaceRoleId,
|
||
});
|
||
|
||
if (response.successful) {
|
||
return response.data;
|
||
} else {
|
||
throw new Error(response.message || '替换角色失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('替换角色失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @description: AI优化当前角色描述
|
||
* @param selectedRole 角色描述文本
|
||
* @returns Promise<{optimizedDescription: string, keywords: string[]}> 优化后的角色描述和关键词
|
||
*/
|
||
async optimizeRoleDescription( selectedRole: RoleEntity): Promise<{optimizedDescription: string, keywords: string[]}> {
|
||
try {
|
||
if (!this.selectedRole) {
|
||
throw new Error('请先选择角色');
|
||
}
|
||
|
||
// 调用新的AI优化角色描述API
|
||
const response = await generateCharacterDescription({
|
||
original_text: selectedRole.generateText,
|
||
});
|
||
|
||
if (!response.successful) {
|
||
throw new Error(response.message || 'AI优化角色描述失败');
|
||
}
|
||
|
||
const { optimized_description, keywords } = response.data;
|
||
|
||
// 更新角色列表中的对应角色描述
|
||
const roleIndex = this.roleList.findIndex(role => role.id === this.selectedRole?.id);
|
||
if (roleIndex !== -1) {
|
||
this.roleList[roleIndex].generateText = optimized_description;
|
||
}
|
||
|
||
return {
|
||
optimizedDescription: optimized_description,
|
||
keywords: keywords
|
||
};
|
||
} 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 [];
|
||
}
|
||
|
||
/**
|
||
* @description 根据图片地址获取角色实体数据
|
||
* @param imageUrl 图片地址
|
||
* @returns Promise<RoleEntity> 角色实体数据
|
||
*/
|
||
async getRoleByImage(imageUrl: string): Promise<RoleEntity> {
|
||
try {
|
||
// TODO: 调用后端API,根据图片地址获取角色数据
|
||
// 这里需要根据实际的后端API接口来实现
|
||
// const response = await getRoleByImage({ imageUrl });
|
||
|
||
// 临时实现:返回一个模拟的角色实体
|
||
// 实际使用时需要替换为真实的API调用
|
||
const mockRole: RoleEntity = {
|
||
id: `role_${Date.now()}`,
|
||
name: '从图片识别的角色',
|
||
generateText: '通过图片识别生成的角色描述',
|
||
tags: [], // 空标签数组
|
||
imageUrl: imageUrl, // 使用传入的图片地址
|
||
loadingProgress: 100, // 加载完成
|
||
disableEdit: false, // 允许编辑
|
||
updatedAt: Date.now()
|
||
};
|
||
|
||
return mockRole;
|
||
} catch (error) {
|
||
console.error('根据图片获取角色失败:', error);
|
||
throw new Error('根据图片获取角色失败');
|
||
}
|
||
}
|
||
|
||
}
|