import { message } from "antd"; import { StoryTemplateEntity } from "../domain/Entities"; import { useUploadFile } from "../domain/service"; import { debounce } from "lodash"; import { TemplateStoryUseCase } from "../usecase/templateStoryUseCase"; import { useState, useCallback, useMemo } from "react"; import { generateTextToImage } from "@/api/movie_start"; import { MovieProjectService, MovieProjectMode } from "./MovieProjectService"; import { CreateMovieProjectV3Request } from "@/api/DTO/movie_start_dto"; /** 模板角色接口 */ interface TemplateRole { /** 角色名 */ role_name: string; /** 照片URL */ photo_url: string; /** 声音URL */ voice_url: string; } interface UseTemplateStoryService { /** 模板列表 */ templateStoryList: StoryTemplateEntity[]; /** 当前选中要使用的模板 */ selectedTemplate: StoryTemplateEntity | null; /** 当前选中的活跃角色索引 */ activeRoleIndex: number; /** 计算属性:当前活跃角色信息 */ activeRole: TemplateRole | null; /** 加载状态 */ isLoading: boolean; /** 获取模板列表函数 */ /** 获取模板列表函数 */ getTemplateStoryList: () => Promise; /** * action 生成电影函数 * @param {string} user_id - 用户ID * @param {"auto" | "manual"} mode - 生成模式 * @param {"720p" | "1080p" | "4k"} resolution - 分辨率 * @param {string} language - 语言 * @returns {Promise} - 生成的电影ID */ actionStory: ( user_id: string, mode: "auto" | "manual", resolution: "720p" | "1080p" | "4k", language: string ) => Promise; /** 设置选中的模板 */ setSelectedTemplate: (template: StoryTemplateEntity | null) => void; /** 设置活跃角色索引 */ setActiveRoleIndex: (index: number) => void; /** 设置当前活跃角色的音频URL */ setActiveRoleAudio: (audioUrl: string) => void; /**清空数据 */ clearData: () => void; /** 上传人物头像并分析 */ AvatarAndAnalyzeFeatures: (imageUrl: string, roleName?: string) => Promise; /** 更新指定角色的图片 */ updateRoleImage: (roleName: string, imageUrl: string) => void; /** 更新变量字段值 */ updateFillableContentField: (fieldName: string, fieldValue: string) => void; /** 带防抖的失焦处理函数 */ handleFieldBlur: (fieldName: string, fieldValue: string) => void; } export const useTemplateStoryServiceHook = (): UseTemplateStoryService => { const [templateStoryList, setTemplateStoryList] = useState< StoryTemplateEntity[] >([]); const [selectedTemplate, setSelectedTemplate] = useState(null); const [activeRoleIndex, setActiveRoleIndex] = useState(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 => { 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): 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 ), }; 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] ); /** * 更新变量字段值 */ const updateFillableContentField = useCallback( (fieldName: string, fieldValue: string): void => { if (!selectedTemplate) return; const updatedTemplate = { ...selectedTemplate, fillable_content: selectedTemplate.fillable_content.map((field) => field.field_name === fieldName ? { ...field, value: fieldValue } : field ), }; setSelectedTemplate(updatedTemplate); }, [selectedTemplate] ); /** * 上传人物头像并分析特征,替换旧的角色数据 * @param {string} imageUrl - 图片URL * @param {string} roleName - 角色名称(可选,如果不提供则使用当前活跃角色) */ const AvatarAndAnalyzeFeatures = useCallback( async (imageUrl: string, roleName?: string): Promise => { try { setIsLoading(true); // 如果提供了角色名称,更新指定角色;否则更新当前活跃角色 if (roleName) { updateRoleImage(roleName, imageUrl); } else { setActiveRoleData(imageUrl); } // 调用用例处理人物头像上传和特征分析 // const result = await templateStoryUseCase.AvatarAndAnalyzeFeatures( // imageUrl // ); } catch (error) { console.error("人物头像上传和特征分析失败:", error); throw error; } finally { setIsLoading(false); } }, [setActiveRoleData] ); /** * 更新指定角色的图片 * @param {string} roleName - 角色名称 * @param {string} imageUrl - 新的图片URL */ const updateRoleImage = useCallback( (roleName: string, imageUrl: string): void => { if (!selectedTemplate) return; const updatedTemplate = { ...selectedTemplate, storyRole: selectedTemplate.storyRole.map((role) => role.role_name === roleName ? { ...role, photo_url: imageUrl } : role ), }; setSelectedTemplate(updatedTemplate); }, [selectedTemplate] ); /** * 带防抖的失焦处理函数 * @param {string} fieldName - 字段名称 * @param {string} fieldValue - 字段值 */ const handleFieldBlur = useCallback( debounce(async (fieldName: string, fieldValue: string): Promise => { try { // 设置 loading 状态 setIsLoading(true); // 调用图片生成接口 const result = await generateTextToImage({ description: fieldValue }); if (result.successful && result.data?.image_url) { // 更新对应角色的图片 updateRoleImage(fieldName, result.data.image_url); console.log(`字段 ${fieldName} 图片生成成功:`, result.data.image_url); } else { console.error(`字段 ${fieldName} 图片生成失败:`, result.message); } } catch (error) { console.error(`字段 ${fieldName} 处理失败:`, error); } finally { // 清除 loading 状态 setIsLoading(false); } }, 500), [updateRoleImage, setIsLoading] ); const actionStory = useCallback( async ( user_id: string, mode: "auto" | "manual" = "auto", resolution: "720p" | "1080p" | "4k" = "720p", language: string = "English" ) => { console.log('selectedTemplate', selectedTemplate) try { // 设置 loading 状态 setIsLoading(true); const params: CreateMovieProjectV3Request = { script: selectedTemplate?.generateText || "", category: selectedTemplate?.category || "", user_id, mode, resolution, storyRole: selectedTemplate?.storyRole || [], language, template_id: selectedTemplate?.template_id || "", fillable_content: selectedTemplate?.fillable_content || [], }; console.log("params", params); const result = await MovieProjectService.createProject( MovieProjectMode.TEMPLATE, params ); return result.project_id; } catch (error) { console.error("创建电影项目失败:", error); } finally { // 清除 loading 状态 setIsLoading(false); } }, [selectedTemplate] ); return { templateStoryList, selectedTemplate, activeRoleIndex, activeRole, isLoading, getTemplateStoryList, actionStory, setSelectedTemplate, setActiveRoleIndex: handleSetActiveRoleIndex, setActiveRoleAudio, AvatarAndAnalyzeFeatures, updateRoleImage, updateFillableContentField, handleFieldBlur, clearData: () => { setTemplateStoryList([]); setSelectedTemplate(null); setActiveRoleIndex(0); }, }; };