video-flow-b/app/service/Interaction/templateStoryService.ts
2025-08-23 19:02:26 +08:00

249 lines
7.2 KiB
TypeScript

import { message } from "antd";
import { StoryTemplateEntity } from "../domain/Entities";
import { useUploadFile } from "../domain/service";
/** 模板角色接口 */
interface TemplateRole {
/** 角色名 */
role_name: string;
/** 照片URL */
photo_url: string;
/** 声音URL */
voice_url: string;
}
import { TemplateStoryUseCase } from "../usecase/templateStoryUseCase";
import { getUploadToken, uploadToQiniu } from "@/api/common";
import { useState, useCallback, useMemo } from "react";
import { createMovieProjectV3 } from "@/api/movie_start";
import { CreateMovieProjectV3Request } from "@/api/DTO/movie_start_dto";
interface UseTemplateStoryService {
/** 模板列表 */
templateStoryList: StoryTemplateEntity[];
/** 当前选中要使用的模板 */
selectedTemplate: StoryTemplateEntity | null;
/** 当前选中的活跃角色索引 */
activeRoleIndex: number;
/** 计算属性:当前活跃角色信息 */
activeRole: TemplateRole | null;
/** 加载状态 */
isLoading: boolean;
/** 获取模板列表函数 */
/** 获取模板列表函数 */
getTemplateStoryList: () => Promise<void>;
/**
* action 生成电影函数
* @param {string} user_id - 用户ID
* @param {"auto" | "manual"} mode - 生成模式
* @param {"720p" | "1080p" | "4k"} resolution - 分辨率
* @param {string} language - 语言
* @returns {Promise<string>} - 生成的电影ID
*/
actionStory: (
user_id: string,
mode: "auto" | "manual",
resolution: "720p" | "1080p" | "4k" ,
language: string
) => Promise<string|undefined>;
/** 设置选中的模板 */
setSelectedTemplate: (template: StoryTemplateEntity | null) => void;
/** 设置活跃角色索引 */
setActiveRoleIndex: (index: number) => void;
/** 设置当前活跃角色的音频URL */
setActiveRoleAudio: (audioUrl: string) => void;
/**清空数据 */
clearData: () => void;
/** 上传人物头像并分析 */
AvatarAndAnalyzeFeatures: (imageUrl: string) => Promise<void>;
}
export const useTemplateStoryServiceHook = (): UseTemplateStoryService => {
const [templateStoryList, setTemplateStoryList] = useState<
StoryTemplateEntity[]
>([]);
const [selectedTemplate, setSelectedTemplate] =
useState<StoryTemplateEntity | null>(null);
const [activeRoleIndex, setActiveRoleIndex] = useState<number>(0);
const [isLoading, setIsLoading] = useState(false);
// 使用上传文件Hook
const { uploadFile } = useUploadFile();
/** 模板故事用例实例 */
const templateStoryUseCase = useMemo(() => new TemplateStoryUseCase(), []);
/** 计算属性:当前活跃角色信息 */
const activeRole = useMemo(() => {
if (
!selectedTemplate ||
activeRoleIndex < 0 ||
activeRoleIndex >= selectedTemplate.storyRole.length
) {
return null;
}
return selectedTemplate.storyRole[activeRoleIndex];
}, [selectedTemplate, activeRoleIndex]);
/**
* 获取模板列表函数
*/
const getTemplateStoryList = useCallback(async (): Promise<void> => {
try {
setIsLoading(true);
const templates = await templateStoryUseCase.getTemplateStoryList();
setTemplateStoryList(templates);
setSelectedTemplate(templates[0]);
setActiveRoleIndex(0);
console.log(selectedTemplate, activeRoleIndex)
} catch (err) {
console.error("获取模板列表失败:", err);
} finally {
setIsLoading(false);
}
}, [templateStoryUseCase]);
/**
* 设置活跃角色索引
*/
const handleSetActiveRoleIndex = useCallback((index: number): void => {
setActiveRoleIndex(index);
}, []);
/**
* 设置当前活跃角色的图片URL
*/
const setActiveRoleData = useCallback(
(imageUrl: string, desc: string): void => {
if (
!selectedTemplate ||
activeRoleIndex < 0 ||
activeRoleIndex >= selectedTemplate.storyRole.length
) {
console.log(selectedTemplate, activeRoleIndex);
return;
}
try {
const character_brief = {
name: selectedTemplate.storyRole[activeRoleIndex].role_name,
image_url: imageUrl,
character_analysis: JSON.parse(desc).character_analysis,
};
const updatedTemplate = {
...selectedTemplate,
storyRole: selectedTemplate.storyRole.map((role, index) =>
index === activeRoleIndex
? {
...role,
photo_url: imageUrl,
role_description: character_brief,
}
: role
),
};
setSelectedTemplate(updatedTemplate);
} catch (error) {
message.error("Image analysis failed");
console.log("error", error);
}
},
[selectedTemplate, activeRoleIndex]
);
/**
* 设置当前活跃角色的音频URL
*/
const setActiveRoleAudio = useCallback(
(audioUrl: string): void => {
if (
!selectedTemplate ||
activeRoleIndex < 0 ||
activeRoleIndex >= selectedTemplate.storyRole.length
) {
return;
}
const updatedTemplate = {
...selectedTemplate,
storyRole: selectedTemplate.storyRole.map((role, index) =>
index === activeRoleIndex ? { ...role, voice_url: audioUrl } : role
),
};
setSelectedTemplate(updatedTemplate);
},
[selectedTemplate, activeRoleIndex]
);
/**
* 上传人物头像并分析特征,替换旧的角色数据
* @param {string} characterName - 角色名称
*/
const AvatarAndAnalyzeFeatures = useCallback(
async (imageUrl: string): Promise<void> => {
try {
setIsLoading(true);
// 调用用例处理人物头像上传和特征分析
const result = await templateStoryUseCase.AvatarAndAnalyzeFeatures(
imageUrl
);
setActiveRoleData(result.crop_url, result.whisk_caption);
console.log("人物头像和特征描述更新成功:", result);
} catch (error) {
console.error("人物头像上传和特征分析失败:", error);
throw error;
} finally {
setIsLoading(false);
}
},
[templateStoryUseCase]
);
const actionStory = useCallback(
async (
user_id: string,
mode: "auto" | "manual" = "auto",
resolution: "720p" | "1080p" | "4k" = "720p",
language: string = "English"
) => {
try {
const params: CreateMovieProjectV3Request = {
user_id,
mode,
resolution,
storyRole: selectedTemplate?.storyRole || [],
language,
template_id: selectedTemplate?.template_id || "",
};
const result = await createMovieProjectV3(params);
return result.data.project_id as string;
} catch (error) {
console.error("创建电影项目失败:", error);
}
},
[selectedTemplate]
);
return {
templateStoryList,
selectedTemplate,
activeRoleIndex,
activeRole,
isLoading,
getTemplateStoryList,
actionStory,
setSelectedTemplate,
setActiveRoleIndex: handleSetActiveRoleIndex,
setActiveRoleAudio,
AvatarAndAnalyzeFeatures,
clearData: () => {
setTemplateStoryList([]);
setSelectedTemplate(null);
setActiveRoleIndex(0);
},
};
};