forked from 77media/video-flow
279 lines
7.4 KiB
TypeScript
279 lines
7.4 KiB
TypeScript
import { ImageStoryEntity } from "../domain/Entities";
|
||
import { AIGenerateImageStory } from "@/api/movie_start";
|
||
import { MovieStartDTO, CharacterAnalysis } from "@/api/DTO/movie_start_dto";
|
||
|
||
/**
|
||
* 图片故事用例
|
||
* 负责管理图片故事模式的业务逻辑,包括图片上传、AI分析和故事生成
|
||
*/
|
||
export class ImageStoryUseCase {
|
||
/** 当前图片故事数据 */
|
||
imageStory: Partial<ImageStoryEntity> = {
|
||
imageUrl: "",
|
||
imageStory: "",
|
||
storyType: "auto",
|
||
};
|
||
|
||
/** 故事梗概 */
|
||
storyLogline: string = "";
|
||
|
||
/** 角色头像及名称数据 */
|
||
charactersAnalysis: CharacterAnalysis[] = [];
|
||
|
||
/** 分类数据 */
|
||
potentialGenres: string[] = [];
|
||
|
||
/** 是否正在分析图片 */
|
||
isAnalyzing: boolean = false;
|
||
|
||
/** 是否正在上传 */
|
||
isUploading: boolean = false;
|
||
|
||
constructor() {}
|
||
|
||
/**
|
||
* 获取当前图片故事数据
|
||
* @returns {Partial<ImageStoryEntity>} 图片故事数据
|
||
*/
|
||
getImageStory(): Partial<ImageStoryEntity> {
|
||
return { ...this.imageStory };
|
||
}
|
||
|
||
/**
|
||
* 获取故事梗概
|
||
* @returns {string} 故事梗概
|
||
*/
|
||
getStoryLogline(): string {
|
||
return this.storyLogline;
|
||
}
|
||
|
||
/**
|
||
* 获取角色分析数据
|
||
* @returns {CharacterAnalysis[]} 角色分析数据
|
||
*/
|
||
getCharactersAnalysis(): CharacterAnalysis[] {
|
||
return [...this.charactersAnalysis];
|
||
}
|
||
|
||
/**
|
||
* 获取分类数据
|
||
* @returns {string[]} 分类数据
|
||
*/
|
||
getPotentialGenres(): string[] {
|
||
return [...this.potentialGenres];
|
||
}
|
||
|
||
/**
|
||
* 获取分析状态
|
||
* @returns {boolean} 是否正在分析
|
||
*/
|
||
getAnalyzingStatus(): boolean {
|
||
return this.isAnalyzing;
|
||
}
|
||
|
||
/**
|
||
* 获取上传状态
|
||
* @returns {boolean} 是否正在上传
|
||
*/
|
||
getUploadingStatus(): boolean {
|
||
return this.isUploading;
|
||
}
|
||
|
||
/**
|
||
* 设置图片故事数据
|
||
* @param {Partial<ImageStoryEntity>} data - 要设置的图片故事数据
|
||
*/
|
||
setImageStory(data: Partial<ImageStoryEntity>): void {
|
||
this.imageStory = { ...this.imageStory, ...data };
|
||
}
|
||
|
||
/**
|
||
* 重置图片故事数据
|
||
*/
|
||
resetImageStory(): void {
|
||
this.imageStory = {
|
||
imageUrl: "",
|
||
imageStory: "",
|
||
storyType: "auto",
|
||
};
|
||
this.storyLogline = "";
|
||
this.charactersAnalysis = [];
|
||
this.potentialGenres = [];
|
||
this.isAnalyzing = false;
|
||
this.isUploading = false;
|
||
}
|
||
|
||
/**
|
||
* 处理图片上传
|
||
* @param {string} imageUrl - 已上传的图片URL
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async handleImageUpload(imageUrl: string): Promise<void> {
|
||
try {
|
||
this.isUploading = false; // 图片已上传,设置上传状态为false
|
||
this.isAnalyzing = true;
|
||
|
||
// 设置上传后的图片URL
|
||
this.setImageStory({ imageUrl });
|
||
|
||
// 调用AI分析接口
|
||
await this.analyzeImageWithAI();
|
||
|
||
} catch (error) {
|
||
console.error("图片分析失败:", error);
|
||
// 分析失败时清空图片URL
|
||
this.setImageStory({ imageUrl: "" });
|
||
throw error;
|
||
} finally {
|
||
this.isAnalyzing = false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 使用AI分析图片
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async analyzeImageWithAI(): Promise<void> {
|
||
try {
|
||
// 调用AI分析接口
|
||
const response = await AIGenerateImageStory({
|
||
imageUrl: this.imageStory.imageUrl || "",
|
||
user_text: this.imageStory.imageStory || "",
|
||
});
|
||
|
||
if (response.successful && response.data) {
|
||
// 解析并存储新的数据结构
|
||
this.parseAndStoreAnalysisData(response.data);
|
||
|
||
// 组合成ImageStoryEntity
|
||
this.composeImageStoryEntity(response.data);
|
||
} else {
|
||
throw new Error("AI分析失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("AI分析失败:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 解析并存储分析数据到类属性中
|
||
* @param {MovieStartDTO} data - AI分析返回的数据
|
||
*/
|
||
parseAndStoreAnalysisData(data: MovieStartDTO): void {
|
||
// 存储故事梗概
|
||
this.storyLogline = data.story_logline || "";
|
||
|
||
// 存储角色头像及名称数据
|
||
this.charactersAnalysis = data.characters_analysis || [];
|
||
|
||
// 存储分类数据
|
||
this.potentialGenres = data.potential_genres || [];
|
||
}
|
||
|
||
/**
|
||
* 组合成ImageStoryEntity
|
||
* @param {MovieStartDTO} data - AI分析返回的数据
|
||
*/
|
||
composeImageStoryEntity(data: MovieStartDTO): void {
|
||
// 将角色数据转换为ImageStoryEntity需要的格式
|
||
const roleImage = data.characters_analysis?.map(character => ({
|
||
name: character.role_name,
|
||
avatar_url: "", // 这里需要根据实际情况设置头像URL
|
||
region: {
|
||
x: character.region.x,
|
||
y: character.region.y,
|
||
width: character.region.width,
|
||
height: character.region.height,
|
||
}
|
||
})) || [];
|
||
|
||
// 更新ImageStoryEntity
|
||
this.setImageStory({
|
||
imageAnalysis: data.story_logline || "",
|
||
storyType: data.potential_genres?.[0] || "auto", // 使用第一个分类作为故事类型
|
||
roleImage,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 更新故事类型
|
||
* @param {string} storyType - 新的故事类型
|
||
*/
|
||
updateStoryType(storyType: string): void {
|
||
this.setImageStory({ storyType });
|
||
}
|
||
|
||
/**
|
||
* 更新故事内容
|
||
* @param {string} storyContent - 新的故事内容
|
||
*/
|
||
updateStoryContent(storyContent: string): void {
|
||
this.setImageStory({ imageStory: storyContent });
|
||
}
|
||
|
||
/**
|
||
* 获取故事类型选项
|
||
* @returns {Array<{key: string, label: string}> 故事类型选项数组
|
||
*/
|
||
getStoryTypeOptions(): Array<{ key: string; label: string }> {
|
||
return [
|
||
{ key: "auto", label: "Auto" },
|
||
{ key: "adventure", label: "Adventure" },
|
||
{ key: "romance", label: "Romance" },
|
||
{ key: "mystery", label: "Mystery" },
|
||
{ key: "fantasy", label: "Fantasy" },
|
||
{ key: "comedy", label: "Comedy" },
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 处理角色数据,解析并存储到类属性中
|
||
* @param {CharacterAnalysis[]} characters - 角色分析数据
|
||
*/
|
||
processCharacterData(characters: CharacterAnalysis[]): void {
|
||
this.charactersAnalysis = characters.map(character => ({
|
||
...character,
|
||
region: {
|
||
x: character.region.x,
|
||
y: character.region.y,
|
||
width: character.region.width,
|
||
height: character.region.height,
|
||
}
|
||
}));
|
||
}
|
||
|
||
/**
|
||
* 获取指定角色的区域坐标
|
||
* @param {string} characterName - 角色名称
|
||
* @returns {CharacterAnalysis['region'] | null} 角色区域坐标,如果未找到则返回null
|
||
*/
|
||
getCharacterRegion(characterName: string): CharacterAnalysis['region'] | null {
|
||
const character = this.charactersAnalysis.find(char => char.role_name === characterName);
|
||
return character ? character.region : null;
|
||
}
|
||
|
||
/**
|
||
* 更新角色头像URL
|
||
* @param {string} characterName - 角色名称
|
||
* @param {string} avatarUrl - 头像URL
|
||
*/
|
||
updateCharacterAvatar(characterName: string, avatarUrl: string): void {
|
||
const character = this.charactersAnalysis.find(char => char.role_name === characterName);
|
||
if (character) {
|
||
// 更新角色头像URL(这里需要根据实际的数据结构来调整)
|
||
// 由于CharacterAnalysis接口中没有avatar_url字段,这里只是示例
|
||
console.log(`更新角色 ${characterName} 的头像URL: ${avatarUrl}`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取所有角色名称
|
||
* @returns {string[]} 角色名称数组
|
||
*/
|
||
getAllCharacterNames(): string[] {
|
||
return this.charactersAnalysis.map(char => char.role_name);
|
||
}
|
||
}
|
||
|