forked from 77media/video-flow
重构视频流和交互服务,使用 TagValueObject 替代 TagEntity。更新相关方法和接口以保持一致性。移除未使用的 TextEditUseCase。增强角色和场景编辑功能。
This commit is contained in:
parent
067295a2bc
commit
46bda04605
@ -7,8 +7,8 @@ import {
|
||||
RoleEntity,
|
||||
SceneEntity,
|
||||
VideoSegmentEntity,
|
||||
TagEntity,
|
||||
} from "@/app/service/domain/Entities";
|
||||
import { TagValueObject } from "@/app/service/domain/valueObject";
|
||||
import {
|
||||
ContentItem,
|
||||
LensType,
|
||||
@ -252,8 +252,8 @@ export const getVideoJson = async (data: {
|
||||
export const regenerateRole = async (request: {
|
||||
/** 角色提示词 */
|
||||
prompt: string;
|
||||
/** 标签类型列表 */
|
||||
tagTypes: (number | string)[];
|
||||
/** 标签列表 */
|
||||
tagTypes: TagValueObject[];
|
||||
/** 角色ID(可选,如果重新生成现有角色) */
|
||||
roleId?: string;
|
||||
}): Promise<ApiResponse<RoleEntity>> => {
|
||||
@ -318,7 +318,7 @@ export const getRoleData = async (request: {
|
||||
/** AI文本数据 */
|
||||
text: AITextEntity;
|
||||
/** 标签列表 */
|
||||
tags: TagEntity[];
|
||||
tags: TagValueObject[];
|
||||
}>
|
||||
> => {
|
||||
return post<ApiResponse<any>>("/movie/get_role_data", request);
|
||||
@ -358,7 +358,7 @@ export const updateTag = async (request: {
|
||||
tagId: string;
|
||||
/** 新的标签内容 */
|
||||
content: string | number;
|
||||
}): Promise<ApiResponse<TagEntity>> => {
|
||||
}): Promise<ApiResponse<TagValueObject>> => {
|
||||
return post<ApiResponse<any>>("/movie/update_tag", request);
|
||||
};
|
||||
|
||||
@ -384,8 +384,8 @@ export const updateText = async (request: {
|
||||
export const regenerateScene = async (request: {
|
||||
/** 场景提示词 */
|
||||
prompt: string;
|
||||
/** 标签类型列表 */
|
||||
tagTypes: (number | string)[];
|
||||
/** 标签列表 */
|
||||
tagTypes: TagValueObject[];
|
||||
/** 场景ID(可选,如果重新生成现有场景) */
|
||||
sceneId?: string;
|
||||
}): Promise<ApiResponse<SceneEntity>> => {
|
||||
@ -419,7 +419,7 @@ export const getSceneData = async (request: {
|
||||
/** AI文本数据 */
|
||||
text: AITextEntity;
|
||||
/** 标签列表 */
|
||||
tags: TagEntity[];
|
||||
tags: TagValueObject[];
|
||||
}>
|
||||
> => {
|
||||
return post<ApiResponse<any>>("/movie/get_scene_data", request);
|
||||
@ -493,7 +493,7 @@ export const getShotData = async (request: {
|
||||
/** AI文本数据 */
|
||||
text: AITextEntity;
|
||||
/** 标签列表 */
|
||||
tags: TagEntity[];
|
||||
tags: TagValueObject[];
|
||||
}>
|
||||
> => {
|
||||
return post<ApiResponse<any>>("/movie/get_shot_data", request);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useState, useCallback, useMemo } from 'react';
|
||||
import { RoleEntity, TagEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities';
|
||||
import { RoleEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities';
|
||||
import { TagValueObject } from '../domain/valueObject';
|
||||
import { RoleItem, TagItem, TextItem } from '../domain/Item';
|
||||
import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
|
||||
import { TagEditUseCase } from '../usecase/TagEditUseCase';
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useState, useCallback, useMemo } from 'react';
|
||||
import { SceneEntity, TagEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities';
|
||||
import { SceneEntity, TagValueObject, AITextEntity, VideoSegmentEntity } from '../domain/Entities';
|
||||
import { SceneItem, TagItem, TextItem } from '../domain/Item';
|
||||
import { SceneEditUseCase } from '../usecase/SceneEditUseCase';
|
||||
import { TagEditUseCase } from '../usecase/TagEditUseCase';
|
||||
|
||||
@ -9,8 +9,6 @@ import { ScriptEditUseCase, ScriptEditKey } from "../usecase/ScriptEditUseCase";
|
||||
import {
|
||||
getProjectScript,
|
||||
abortVideoTask,
|
||||
pausePlanFlow,
|
||||
resumePlanFlow,
|
||||
} from "../../../api/video_flow";
|
||||
import { parseScriptBlock } from "../domain/service";
|
||||
import { ScriptBlock } from "@/components/script-renderer/types";
|
||||
|
||||
@ -2,6 +2,7 @@ import { useState, useCallback } from "react";
|
||||
import { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase";
|
||||
import { VideoSegmentEntity } from "../domain/Entities";
|
||||
import { LensType } from "../domain/valueObject";
|
||||
import { getUploadToken, uploadToQiniu } from "@/api/common";
|
||||
|
||||
/**
|
||||
* 视频片段服务Hook接口
|
||||
@ -40,6 +41,8 @@ export interface UseShotService {
|
||||
addNewLens: () => void;
|
||||
/** 删除指定镜头 */
|
||||
deleteLens: (lensName: string) => void;
|
||||
/** 获取视频当前帧并上传到七牛云 */
|
||||
filterRole: (video: HTMLVideoElement) => Promise<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -165,9 +168,9 @@ export const useShotService = (): UseShotService => {
|
||||
* 中断当前操作
|
||||
*/
|
||||
const abortOperation = useCallback((): void => {
|
||||
vidoEditUseCase.abortOperation();
|
||||
// vidoEditUseCase.abortOperation();
|
||||
setLoading(false);
|
||||
}, [vidoEditUseCase]);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 设置选中的视频片段
|
||||
@ -244,6 +247,54 @@ export const useShotService = (): UseShotService => {
|
||||
});
|
||||
}, [selectedSegment]);
|
||||
|
||||
/**
|
||||
* 获取视频当前帧的画面,上传到七牛云,并返回七牛云的图片地址,然后调用接口识别出里面的人物信息,返回人物信息
|
||||
* @param video HTML视频元素
|
||||
* @returns Promise<string> 七牛云的图片地址
|
||||
*/
|
||||
const filterRole = useCallback(async (video: HTMLVideoElement): Promise<string> => {
|
||||
try {
|
||||
// 创建canvas元素来截取视频帧
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
if (!ctx) {
|
||||
throw new Error('无法获取canvas上下文');
|
||||
}
|
||||
|
||||
// 设置canvas尺寸为视频尺寸
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
|
||||
// 将当前视频帧绘制到canvas上
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
// 将canvas转换为blob
|
||||
const blob = await new Promise<Blob>((resolve, reject) => {
|
||||
canvas.toBlob((blob) => {
|
||||
if (blob) {
|
||||
resolve(blob);
|
||||
} else {
|
||||
reject(new Error('无法将canvas转换为blob'));
|
||||
}
|
||||
}, 'image/png');
|
||||
});
|
||||
|
||||
// 创建File对象
|
||||
const file = new File([blob], `frame_${Date.now()}.png`, { type: 'image/png' });
|
||||
|
||||
// 获取上传token
|
||||
const { token } = await getUploadToken();
|
||||
|
||||
// 上传到七牛云
|
||||
const imageUrl = await uploadToQiniu(file, token);
|
||||
|
||||
return imageUrl;
|
||||
} catch (error) {
|
||||
console.error('获取视频帧失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}, []);
|
||||
return {
|
||||
// 响应式状态
|
||||
loading,
|
||||
@ -257,5 +308,6 @@ export const useShotService = (): UseShotService => {
|
||||
setSelectedSegment: setSelectedSegmentHandler,
|
||||
addNewLens,
|
||||
deleteLens,
|
||||
filterRole,
|
||||
};
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* 所有实体都应该实现这些基础接口
|
||||
*/
|
||||
|
||||
import { ContentItem, LensType } from "./valueObject";
|
||||
import { ContentItem, LensType, TagValueObject } from "./valueObject";
|
||||
|
||||
/**
|
||||
* 基础实体接口
|
||||
@ -34,24 +34,12 @@ export interface AITextEntity extends BaseEntity {
|
||||
export interface RoleEntity extends BaseEntity {
|
||||
/** 角色名称 */
|
||||
name: string;
|
||||
/** 角色提示词Id */
|
||||
generateTextId: string;
|
||||
/** 角色提示词 */
|
||||
generateText: string;
|
||||
/**角色标签 */
|
||||
tagIds: string[];
|
||||
tagIds: TagValueObject[];
|
||||
/** 角色图片URL */
|
||||
imageUrl: string;
|
||||
/** 角色是否已存储 */
|
||||
isStored: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标签实体接口
|
||||
*/
|
||||
export interface TagEntity extends BaseEntity {
|
||||
/** 标签名称 */
|
||||
name: string;
|
||||
/** 内容标签类型 */
|
||||
content: number | string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,9 +51,9 @@ export interface SceneEntity extends BaseEntity {
|
||||
/** 场景图片URL */
|
||||
imageUrl: string;
|
||||
/** 场景标签 */
|
||||
tagIds: string[];
|
||||
/** 场景提示词Id */
|
||||
generateTextId: string;
|
||||
tagIds: TagValueObject[];
|
||||
/** 场景提示词 */
|
||||
generateText: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -2,7 +2,7 @@ import {
|
||||
BaseEntity,
|
||||
AITextEntity,
|
||||
RoleEntity,
|
||||
TagEntity,
|
||||
TagValueObject,
|
||||
SceneEntity,
|
||||
VideoSegmentEntity
|
||||
} from './Entities';
|
||||
@ -87,11 +87,11 @@ export class RoleItem extends EditItem<RoleEntity> {
|
||||
/**
|
||||
* 标签可编辑项
|
||||
*/
|
||||
export class TagItem extends EditItem<TagEntity> {
|
||||
export class TagItem extends EditItem<TagValueObject> {
|
||||
type: ItemType.TEXT = ItemType.TEXT;
|
||||
|
||||
constructor(
|
||||
entity: TagEntity,
|
||||
entity: TagValueObject,
|
||||
metadata: Record<string, any> = {}
|
||||
) {
|
||||
super(entity, metadata);
|
||||
|
||||
@ -79,6 +79,17 @@ export class LensType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标签实体接口
|
||||
*/
|
||||
export interface TagValueObject {
|
||||
/** 标签名称 */
|
||||
name: string;
|
||||
/** 内容标签类型 */
|
||||
content: number | string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 这是一个符合DDD概念的值对象,代表故事的核心细节。
|
||||
* 它封装了故事的四个关键元素:梗概、分类、主角和框架内容。
|
||||
|
||||
@ -3,7 +3,7 @@ import { SceneEditUseCase } from '../usecase/SceneEditUseCase';
|
||||
import { TextEditUseCase } from '../usecase/TextEditUseCase';
|
||||
import { TagEditUseCase } from '../usecase/TagEditUseCase';
|
||||
import { SceneItem, TextItem, TagItem } from '../domain/Item';
|
||||
import { SceneEntity, AITextEntity, TagEntity, VideoSegmentEntity, ShotStatus } from '../domain/Entities';
|
||||
import { SceneEntity, AITextEntity, TagValueObject, VideoSegmentEntity, ShotStatus } from '../domain/Entities';
|
||||
|
||||
// Mock API模块
|
||||
jest.mock('@/api/video_flow', () => ({
|
||||
@ -53,7 +53,7 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
disableEdit: false,
|
||||
};
|
||||
|
||||
const mockTagEntity1: TagEntity = {
|
||||
const mockTagValueObject1: TagValueObject = {
|
||||
id: 'tag1',
|
||||
name: '场景标签1',
|
||||
content: '场景标签内容1',
|
||||
@ -62,7 +62,7 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
disableEdit: false,
|
||||
};
|
||||
|
||||
const mockTagEntity2: TagEntity = {
|
||||
const mockTagValueObject2: TagValueObject = {
|
||||
id: 'tag2',
|
||||
name: '场景标签2',
|
||||
content: '场景标签内容2',
|
||||
@ -167,7 +167,7 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
successful: true,
|
||||
data: {
|
||||
text: mockTextEntity,
|
||||
tags: [mockTagEntity1, mockTagEntity2],
|
||||
tags: [mockTagValueObject1, mockTagValueObject2],
|
||||
},
|
||||
});
|
||||
|
||||
@ -176,7 +176,7 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
expect(getSceneData).toHaveBeenCalledWith({ sceneId: 'scene1' });
|
||||
expect(result.successful).toBe(true);
|
||||
expect(result.data.text).toEqual(mockTextEntity);
|
||||
expect(result.data.tags).toEqual([mockTagEntity1, mockTagEntity2]);
|
||||
expect(result.data.tags).toEqual([mockTagValueObject1, mockTagValueObject2]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -202,10 +202,10 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
});
|
||||
|
||||
it('应该成功修改标签内容', async () => {
|
||||
const updatedTagEntity = { ...mockTagEntity1, content: '更新后的场景标签' };
|
||||
const updatedTagValueObject = { ...mockTagValueObject1, content: '更新后的场景标签' };
|
||||
(updateTag as jest.Mock).mockResolvedValue({
|
||||
successful: true,
|
||||
data: updatedTagEntity,
|
||||
data: updatedTagValueObject,
|
||||
});
|
||||
|
||||
const result = await updateTag({
|
||||
@ -319,14 +319,14 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
successful: true,
|
||||
data: {
|
||||
text: mockTextEntity,
|
||||
tags: [mockTagEntity1, mockTagEntity2],
|
||||
tags: [mockTagValueObject1, mockTagValueObject2],
|
||||
},
|
||||
});
|
||||
|
||||
const sceneDataResult = await getSceneData({ sceneId: 'scene1' });
|
||||
expect(sceneDataResult.successful).toBe(true);
|
||||
expect(sceneDataResult.data.text).toEqual(mockTextEntity);
|
||||
expect(sceneDataResult.data.tags).toEqual([mockTagEntity1, mockTagEntity2]);
|
||||
expect(sceneDataResult.data.tags).toEqual([mockTagValueObject1, mockTagValueObject2]);
|
||||
|
||||
// 模拟用户操作:修改场景提示词
|
||||
const updatedTextEntity = { ...mockTextEntity, content: '修改后的场景提示词' };
|
||||
@ -356,10 +356,10 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
expect(optimizedContentResult).toBe(optimizedContent);
|
||||
|
||||
// 模拟用户操作:修改标签
|
||||
const updatedTagEntity = { ...mockTagEntity1, content: '修改后的标签内容' };
|
||||
const updatedTagValueObject = { ...mockTagValueObject1, content: '修改后的标签内容' };
|
||||
(updateTag as jest.Mock).mockResolvedValue({
|
||||
successful: true,
|
||||
data: updatedTagEntity,
|
||||
data: updatedTagValueObject,
|
||||
});
|
||||
|
||||
const updateTagResult = await updateTag({
|
||||
@ -435,7 +435,7 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
successful: true,
|
||||
data: {
|
||||
text: mockTextEntity,
|
||||
tags: [mockTagEntity1],
|
||||
tags: [mockTagValueObject1],
|
||||
},
|
||||
});
|
||||
|
||||
@ -612,7 +612,7 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
});
|
||||
|
||||
it('TagEditUseCase应该正确初始化', () => {
|
||||
const tagItem = new TagItem(mockTagEntity1);
|
||||
const tagItem = new TagItem(mockTagValueObject1);
|
||||
const useCase = new TagEditUseCase(tagItem);
|
||||
|
||||
expect(TagEditUseCase).toHaveBeenCalledWith(tagItem);
|
||||
@ -637,11 +637,11 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
expect(textItem.disableEdit).toBe(false);
|
||||
});
|
||||
|
||||
it('TagItem应该正确包装TagEntity', () => {
|
||||
const tagItem = new TagItem(mockTagEntity1);
|
||||
it('TagItem应该正确包装TagValueObject', () => {
|
||||
const tagItem = new TagItem(mockTagValueObject1);
|
||||
|
||||
expect(TagItem).toHaveBeenCalledWith(mockTagEntity1);
|
||||
expect(tagItem.entity).toEqual(mockTagEntity1);
|
||||
expect(TagItem).toHaveBeenCalledWith(mockTagValueObject1);
|
||||
expect(tagItem.entity).toEqual(mockTagValueObject1);
|
||||
expect(tagItem.disableEdit).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -695,7 +695,7 @@ describe('SceneService 业务逻辑测试', () => {
|
||||
});
|
||||
|
||||
it('应该验证标签实体的完整性', () => {
|
||||
const tagItem = new TagItem(mockTagEntity1);
|
||||
const tagItem = new TagItem(mockTagValueObject1);
|
||||
|
||||
expect(tagItem.entity.id).toBe('tag1');
|
||||
expect(tagItem.entity.name).toBe('场景标签1');
|
||||
|
||||
@ -1,90 +1,191 @@
|
||||
import { RoleEntity, AITextEntity, TagEntity } from '../domain/Entities';
|
||||
import { RoleItem, TagItem, TextItem } from '../domain/Item';
|
||||
import { regenerateRole, applyRoleToShots, getRoleData } from '@/api/video_flow';
|
||||
import { RoleEntity } from '../domain/Entities';
|
||||
import { TagValueObject } from '../domain/valueObject';
|
||||
import {
|
||||
applyRoleToShots,
|
||||
getRoleList,
|
||||
getUserRoleLibrary,
|
||||
getRoleData,
|
||||
regenerateRole,
|
||||
getRoleShots,
|
||||
replaceRole
|
||||
} from '@/api/video_flow';
|
||||
import { TagEditUseCase } from './TagEditUseCase';
|
||||
|
||||
/**
|
||||
* 角色图编辑用例
|
||||
* 负责角色图内容的初始化、修改和优化
|
||||
*/
|
||||
export class RoleEditUseCase {
|
||||
constructor(private roleItem: RoleItem) {
|
||||
roleList: RoleEntity[] = [];
|
||||
selectedRole: RoleEntity | null = null;
|
||||
selectedRoleTags:TagEditUseCase = new TagEditUseCase([]);
|
||||
roleLibraryList: RoleEntity[] = [];
|
||||
constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 重新生成角色
|
||||
* @param {TextItem} prompt
|
||||
* @param {TagItem[]} tags
|
||||
* @return {*}
|
||||
* 获取当前项目角色列表
|
||||
* @param projectId 项目ID
|
||||
* @returns Promise<RoleEntity[]> 角色列表
|
||||
*/
|
||||
async AIgenerateRole(prompt: TextItem, tags: TagItem[]): Promise<RoleEntity> {
|
||||
const promptText = prompt.entity.content;
|
||||
const tagList = tags.map((tag) => tag.entity.content);
|
||||
|
||||
// 调用重新生成角色接口
|
||||
const response = await regenerateRole({
|
||||
roleId: this.roleItem.entity.id||'',
|
||||
prompt: promptText,
|
||||
tagTypes: tagList,
|
||||
});
|
||||
|
||||
if (response.successful) {
|
||||
const roleEntity = response.data;
|
||||
this.roleItem.setEntity(roleEntity);
|
||||
return roleEntity;
|
||||
} else {
|
||||
throw new Error(`重新生成角色失败: ${response.message}`);
|
||||
async getRoleList(projectId: string): Promise<RoleEntity[]> {
|
||||
try {
|
||||
const response = await getRoleList({ projectId });
|
||||
if (response.successful) {
|
||||
return response.data;
|
||||
} else {
|
||||
throw new Error(response.message || '获取角色列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取角色列表失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色库的角色列表
|
||||
* @returns Promise<RoleEntity[]> 角色库列表
|
||||
*/
|
||||
async getRoleLibraryList(): Promise<RoleEntity[]> {
|
||||
try {
|
||||
const response = await getUserRoleLibrary();
|
||||
if (response.successful) {
|
||||
return response.data;
|
||||
} else {
|
||||
throw new Error(response.message || '获取角色库失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取角色库失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改某个标签内容
|
||||
* @param tagName 标签名称
|
||||
* @param newContent 新内容
|
||||
*/
|
||||
async updateTag(tagName: string, newContent: string | number): Promise<void> {
|
||||
await this.selectedRoleTags.updateTag(tagName, newContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中某个角色作为当前活跃角色
|
||||
* @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) {
|
||||
this.selectedRoleTags = new TagEditUseCase(response.data.tags);
|
||||
} else {
|
||||
throw new Error(response.message || '获取角色标签数据失败');
|
||||
}
|
||||
} else {
|
||||
throw new Error('未找到对应的角色实体,请先获取角色列表');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('选择角色失败:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新生成角色
|
||||
* @param prompt 角色提示词
|
||||
* @param tags 标签列表
|
||||
* @returns Promise<RoleEntity> 重新生成的角色
|
||||
*/
|
||||
async AIgenerateRole(prompt: string, tags: TagValueObject[]): Promise<RoleEntity> {
|
||||
try {
|
||||
// 直接使用当前角色的ID,不做任何处理
|
||||
const response = await regenerateRole({
|
||||
prompt,
|
||||
tagTypes: tags, // 直接传递完整的标签列表给后端,让后端处理
|
||||
roleId: this.selectedRole?.id
|
||||
});
|
||||
|
||||
if (response.successful) {
|
||||
return response.data;
|
||||
} else {
|
||||
throw new Error(response.message || '重新生成角色失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('重新生成角色失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用此角色到指定分镜
|
||||
* @param shotIds 分镜ID列表
|
||||
* @param roleId 角色ID
|
||||
* @returns 应用结果
|
||||
*/
|
||||
async applyRole(shotIds: string[]) {
|
||||
const roleId = this.roleItem.entity.id;
|
||||
return await applyRoleToShots({
|
||||
roleId,
|
||||
shotIds,
|
||||
});
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新获取当前角色的数据
|
||||
* @description 从服务器重新获取当前角色的AI文本和标签数据,并更新当前实体
|
||||
* @returns Promise<{ text: AITextEntity; tags: TagEntity[] }> 角色相关的AI文本和标签数据
|
||||
* @throws {Error} 当API调用失败时抛出错误
|
||||
* 获取角色应用到的分镜列表
|
||||
* @param roleId 角色ID
|
||||
* @returns 分镜列表和应用状态
|
||||
*/
|
||||
async refreshRoleData(): Promise<{ text: AITextEntity; tags: TagEntity[] }> {
|
||||
const roleId = this.roleItem.entity.id;
|
||||
|
||||
if (!roleId) {
|
||||
throw new Error('角色ID不存在,无法获取角色数据');
|
||||
}
|
||||
|
||||
const response = await getRoleData({
|
||||
roleId: roleId
|
||||
});
|
||||
|
||||
if (response.successful) {
|
||||
// 更新当前角色的实体数据
|
||||
const { text, tags } = response.data;
|
||||
|
||||
// 更新角色实体中的相关字段
|
||||
const updatedRoleEntity = {
|
||||
...this.roleItem.entity,
|
||||
generateTextId: text.id, // 更新AI文本ID
|
||||
tagIds: tags.map(tag => tag.id), // 更新标签ID列表
|
||||
updatedAt: Date.now(), // 更新时间戳
|
||||
};
|
||||
|
||||
// 更新当前UseCase中的实体
|
||||
this.roleItem.setEntity(updatedRoleEntity);
|
||||
|
||||
return response.data;
|
||||
} else {
|
||||
throw new Error(`获取角色数据失败: ${response.message}`);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { SceneEntity, AITextEntity, TagEntity } from '../domain/Entities';
|
||||
import { SceneEntity, AITextEntity } from '../domain/Entities';
|
||||
import { SceneItem, TagItem, TextItem } from '../domain/Item';
|
||||
import { regenerateScene, applySceneToShots, getSceneData } from '@/api/video_flow';
|
||||
|
||||
@ -53,10 +53,10 @@ export class SceneEditUseCase {
|
||||
/**
|
||||
* 重新获取当前场景的数据
|
||||
* @description 从服务器重新获取当前场景的AI文本和标签数据,并更新当前实体
|
||||
* @returns Promise<{ text: AITextEntity; tags: TagEntity[] }> 场景相关的AI文本和标签数据
|
||||
* @returns Promise<{ text: AITextEntity; tags: TagValueObject[] }> 场景相关的AI文本和标签数据
|
||||
* @throws {Error} 当API调用失败时抛出错误
|
||||
*/
|
||||
async refreshSceneData(): Promise<{ text: AITextEntity; tags: TagEntity[] }> {
|
||||
async refreshSceneData(): Promise<{ text: AITextEntity; tags: TagValueObject[] }> {
|
||||
const sceneId = this.sceneItem.entity.id;
|
||||
|
||||
if (!sceneId) {
|
||||
|
||||
@ -12,7 +12,6 @@ import {
|
||||
*/
|
||||
export class VideoSegmentEditUseCase {
|
||||
private loading: boolean = false;
|
||||
private abortController: AbortController | null = null;
|
||||
|
||||
/**
|
||||
* @description 获取视频片段列表
|
||||
@ -55,9 +54,6 @@ export class VideoSegmentEditUseCase {
|
||||
try {
|
||||
this.loading = true;
|
||||
|
||||
// 创建新的中断控制器
|
||||
this.abortController = new AbortController();
|
||||
|
||||
const response = await regenerateShot({
|
||||
shotId,
|
||||
shotPrompt,
|
||||
@ -71,15 +67,8 @@ export class VideoSegmentEditUseCase {
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (this.abortController?.signal.aborted) {
|
||||
console.log("视频片段重新生成被中断");
|
||||
throw new Error("操作被中断");
|
||||
}
|
||||
console.error("重新生成视频片段失败:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.abortController = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,15 +106,7 @@ export class VideoSegmentEditUseCase {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @description 中断当前操作
|
||||
*/
|
||||
abortOperation(): void {
|
||||
if (this.abortController) {
|
||||
this.abortController.abort();
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description 获取加载状态
|
||||
|
||||
@ -1,40 +1,81 @@
|
||||
import { TagItem } from '../domain/Item';
|
||||
import { updateTag } from '@/api/video_flow';
|
||||
|
||||
import { TagValueObject } from '../domain/valueObject';
|
||||
|
||||
/**
|
||||
* 标签编辑用例
|
||||
* 负责标签内容的初始化、修改和优化
|
||||
*/
|
||||
export class TagEditUseCase {
|
||||
constructor(private readonly tagItem: TagItem) {
|
||||
this.tagItem = tagItem;
|
||||
constructor(public tagList: TagValueObject[]) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改标签内容
|
||||
* @param tagName 标签名称
|
||||
* @param newContent 新内容
|
||||
* @returns 更新后的标签项
|
||||
*/
|
||||
async updateTag(newContent: string|number): Promise<TagItem> {
|
||||
if (!this.tagItem) {
|
||||
throw new Error('标签项未初始化');
|
||||
}
|
||||
|
||||
if (this.tagItem.entity.disableEdit) {
|
||||
throw new Error('标签项已禁用编辑');
|
||||
}
|
||||
// 请求更新接口
|
||||
const response = await updateTag({
|
||||
tagId: this.tagItem.entity.id,
|
||||
content: newContent
|
||||
});
|
||||
|
||||
if (response.successful) {
|
||||
this.tagItem.setEntity(response.data);
|
||||
return this.tagItem;
|
||||
} else {
|
||||
throw new Error(`修改标签失败: ${response.message}`);
|
||||
async updateTag(tagName: string, newContent: string | number): Promise<void> {
|
||||
const tag = this.tagList.find(tag => tag.name === tagName);
|
||||
if (tag) {
|
||||
tag.content = newContent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增标签
|
||||
* @param tagName 标签名称
|
||||
* @param content 标签内容
|
||||
*/
|
||||
async addTag(tagName: string, content: string | number = ""): Promise<void> {
|
||||
// 检查标签是否已存在
|
||||
const existingTag = this.tagList.find(tag => tag.name === tagName);
|
||||
if (existingTag) {
|
||||
throw new Error(`标签 "${tagName}" 已存在`);
|
||||
}
|
||||
|
||||
// 创建新标签
|
||||
const newTag: TagValueObject = {
|
||||
name: tagName,
|
||||
content: content
|
||||
};
|
||||
|
||||
this.tagList.push(newTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定标签
|
||||
* @param tagName 要删除的标签名称
|
||||
*/
|
||||
async deleteTag(tagName: string): Promise<void> {
|
||||
const tagIndex = this.tagList.findIndex(tag => tag.name === tagName);
|
||||
if (tagIndex === -1) {
|
||||
throw new Error(`标签 "${tagName}" 不存在`);
|
||||
}
|
||||
|
||||
this.tagList.splice(tagIndex, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有标签
|
||||
*/
|
||||
async clearAllTags(): Promise<void> {
|
||||
this.tagList.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有标签
|
||||
* @returns 标签列表
|
||||
*/
|
||||
getTagList(): TagValueObject[] {
|
||||
return [...this.tagList];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签名称获取标签
|
||||
* @param tagName 标签名称
|
||||
* @returns 标签对象或undefined
|
||||
*/
|
||||
getTagByName(tagName: string): TagValueObject | undefined {
|
||||
return this.tagList.find(tag => tag.name === tagName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
import { TextItem } from '../domain/Item';
|
||||
import { updateText } from '@/api/video_flow';
|
||||
|
||||
/**
|
||||
* 文本编辑用例
|
||||
* 负责单个文本内容的初始化、修改和优化
|
||||
*/
|
||||
export class TextEditUseCase {
|
||||
constructor(private readonly textItem: TextItem) {
|
||||
this.textItem = textItem;
|
||||
}
|
||||
/**
|
||||
* 修改文本内容
|
||||
* @param newContent 新内容
|
||||
* @returns 更新后的文本项
|
||||
*/
|
||||
async updateText(newContent: string): Promise<TextItem> {
|
||||
if (!this.textItem) {
|
||||
throw new Error('文本项未初始化,请先调用 initializeText');
|
||||
}
|
||||
|
||||
if (this.textItem.entity.disableEdit) {
|
||||
throw new Error('文本项已禁用编辑');
|
||||
}
|
||||
|
||||
const response = await updateText({
|
||||
textId: this.textItem.entity.id,
|
||||
content: newContent
|
||||
});
|
||||
|
||||
if (response.successful) {
|
||||
this.textItem.setEntity(response.data);
|
||||
return this.textItem;
|
||||
} else {
|
||||
throw new Error(`修改文案失败: ${response.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取优化后的文案内容
|
||||
* @param optimizationOptions 优化选项
|
||||
* @returns 优化后的内容
|
||||
*/
|
||||
async getOptimizedContent(
|
||||
): Promise<string> {
|
||||
if (!this.textItem) {
|
||||
throw new Error('没有内容可优化');
|
||||
}
|
||||
return this.textItem.entity.content;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user