forked from 77media/video-flow
311 lines
9.9 KiB
TypeScript
311 lines
9.9 KiB
TypeScript
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 UseTemplateStoryService {
|
||
/** 模板列表 */
|
||
templateStoryList: StoryTemplateEntity[];
|
||
/** 当前选中要使用的模板 */
|
||
selectedTemplate: StoryTemplateEntity | 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;
|
||
/** 清空数据 */
|
||
clearData: () => void;
|
||
/** 上传人物头像并分析 */
|
||
AvatarAndAnalyzeFeatures: (imageUrl: string, roleName: string) => Promise<void>;
|
||
/** 更新指定角色的图片 */
|
||
updateRoleImage: (roleName: string, imageUrl: string) => void;
|
||
/** 更新指定道具的图片 */
|
||
updateItemImage: (itemName: string, imageUrl: string) => void;
|
||
/** 带防抖的失焦处理函数 - 角色图片生成 */
|
||
handleRoleFieldBlur: (roleName: string, fieldValue: string) => void;
|
||
/** 带防抖的失焦处理函数 - 道具图片生成 */
|
||
handleItemFieldBlur: (itemName: string, fieldValue: string) => void;
|
||
}
|
||
|
||
export const useTemplateStoryServiceHook = (): UseTemplateStoryService => {
|
||
const [templateStoryList, setTemplateStoryList] = useState<
|
||
StoryTemplateEntity[]
|
||
>([]);
|
||
const [selectedTemplate, setSelectedTemplate] =
|
||
useState<StoryTemplateEntity | null>(null);
|
||
const [isLoading, setIsLoading] = useState(false);
|
||
|
||
// 使用上传文件Hook
|
||
const { uploadFile } = useUploadFile();
|
||
|
||
/** 模板故事用例实例 */
|
||
const templateStoryUseCase = useMemo(() => new TemplateStoryUseCase(), []);
|
||
|
||
/**
|
||
* 获取模板列表函数
|
||
*/
|
||
const getTemplateStoryList = useCallback(async (): Promise<void> => {
|
||
try {
|
||
setIsLoading(true);
|
||
|
||
const templates = await templateStoryUseCase.getTemplateStoryList();
|
||
// templates.forEach(template => {
|
||
// if (template.id === 'f944abad-f42b-4899-b54a-a6beb9d27805') {
|
||
// template.freeInputItem = {
|
||
// user_tips: "How is coffee made?",
|
||
// constraints: "",
|
||
// free_input_text: ""
|
||
// };
|
||
// // template.storyRole = [];
|
||
// }
|
||
// if (template.id === 'e7438cd8-a23d-4974-8cde-13b5671b410c') {
|
||
// // template.freeInputItem = {
|
||
// // user_tips: "Input an English word you wanna learn",
|
||
// // constraints: "",
|
||
// // free_input_text: ""
|
||
// // };
|
||
// template.storyItem = [{
|
||
// ...template.storyItem[0],
|
||
// item_name: "Choose an English word you wanna learn"
|
||
// }];
|
||
// }
|
||
// });
|
||
|
||
setTemplateStoryList(templates);
|
||
setSelectedTemplate(templates[0]);
|
||
console.log(selectedTemplate);
|
||
} catch (err) {
|
||
console.error("获取模板列表失败:", err);
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
}, [templateStoryUseCase]);
|
||
|
||
/**
|
||
* 更新指定角色的图片
|
||
* @param {string} roleName - 角色名称
|
||
* @param {string} imageUrl - 新的图片URL
|
||
*/
|
||
const updateRoleImage = useCallback(
|
||
(roleName: string, imageUrl: string): void => {
|
||
if (!selectedTemplate) {
|
||
console.warn("updateRoleImage: selectedTemplate 为空");
|
||
return;
|
||
}
|
||
|
||
console.log(`更新角色 ${roleName} 的图片:`, imageUrl);
|
||
|
||
const updatedTemplate = {
|
||
...selectedTemplate,
|
||
storyRole: selectedTemplate.storyRole.map((role) =>
|
||
role.role_name === roleName
|
||
? { ...role, photo_url: imageUrl }
|
||
: role
|
||
),
|
||
};
|
||
|
||
console.log("更新后的模板:", updatedTemplate);
|
||
setSelectedTemplate(updatedTemplate);
|
||
},
|
||
[selectedTemplate]
|
||
);
|
||
|
||
/**
|
||
* 更新指定道具的图片
|
||
* @param {string} itemName - 道具名称
|
||
* @param {string} imageUrl - 新的图片URL
|
||
*/
|
||
const updateItemImage = useCallback(
|
||
(itemName: string, imageUrl: string): void => {
|
||
if (!selectedTemplate) {
|
||
console.warn("updateItemImage: selectedTemplate 为空");
|
||
return;
|
||
}
|
||
|
||
console.log(`更新道具 ${itemName} 的图片:`, imageUrl);
|
||
|
||
const updatedTemplate = {
|
||
...selectedTemplate,
|
||
storyItem: selectedTemplate.storyItem.map((item) =>
|
||
item.item_name === itemName
|
||
? { ...item, photo_url: imageUrl }
|
||
: item
|
||
),
|
||
};
|
||
|
||
console.log("更新后的模板:", updatedTemplate);
|
||
setSelectedTemplate(updatedTemplate);
|
||
},
|
||
[selectedTemplate]
|
||
);
|
||
|
||
/**
|
||
* 上传人物头像并分析特征,替换旧的角色数据
|
||
* @param {string} imageUrl - 图片URL
|
||
* @param {string} roleName - 角色名称
|
||
*/
|
||
const AvatarAndAnalyzeFeatures = useCallback(
|
||
async (imageUrl: string, roleName: string): Promise<void> => {
|
||
try {
|
||
setIsLoading(true);
|
||
// 直接更新指定角色的图片
|
||
updateRoleImage(roleName, imageUrl);
|
||
} catch (error) {
|
||
console.error("人物头像上传和特征分析失败:", error);
|
||
throw error;
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
},
|
||
[updateRoleImage]
|
||
);
|
||
|
||
/**
|
||
* 带防抖的失焦处理函数 - 角色图片生成
|
||
* @param {string} roleName - 角色名称
|
||
* @param {string} fieldValue - 字段值
|
||
*/
|
||
const handleRoleFieldBlur = useCallback(
|
||
debounce(async (roleName: string, fieldValue: string): Promise<void> => {
|
||
try {
|
||
// 设置 loading 状态
|
||
setIsLoading(true);
|
||
|
||
// 调用图片生成接口
|
||
const result = await generateTextToImage({
|
||
description: fieldValue
|
||
});
|
||
|
||
if (result.successful && result.data?.image_url) {
|
||
// 更新对应角色的图片
|
||
updateRoleImage(roleName, result.data.image_url);
|
||
console.log(`角色 ${roleName} 图片生成成功:`, result.data.image_url);
|
||
} else {
|
||
console.error(`角色 ${roleName} 图片生成失败:`, result.message);
|
||
}
|
||
} catch (error) {
|
||
console.error(`角色 ${roleName} 处理失败:`, error);
|
||
} finally {
|
||
// 清除 loading 状态
|
||
setIsLoading(false);
|
||
}
|
||
}, 500),
|
||
[updateRoleImage, setIsLoading]
|
||
);
|
||
|
||
/**
|
||
* 带防抖的失焦处理函数 - 道具图片生成
|
||
* @param {string} itemName - 道具名称
|
||
* @param {string} fieldValue - 字段值
|
||
*/
|
||
const handleItemFieldBlur = useCallback(
|
||
debounce(async (itemName: string, fieldValue: string): Promise<void> => {
|
||
try {
|
||
// 设置 loading 状态
|
||
setIsLoading(true);
|
||
|
||
// 调用图片生成接口
|
||
const result = await generateTextToImage({
|
||
description: fieldValue
|
||
});
|
||
|
||
if (result.successful && result.data?.image_url) {
|
||
// 更新对应道具的图片
|
||
updateItemImage(itemName, result.data.image_url);
|
||
console.log(`道具 ${itemName} 图片生成成功:`, result.data.image_url);
|
||
} else {
|
||
console.error(`道具 ${itemName} 图片生成失败:`, result.message);
|
||
}
|
||
} catch (error) {
|
||
console.error(`道具 ${itemName} 处理失败:`, error);
|
||
} finally {
|
||
// 清除 loading 状态
|
||
setIsLoading(false);
|
||
}
|
||
}, 500),
|
||
[updateItemImage, 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);
|
||
// 没有角色以及道具 时,需要设置为true
|
||
const one_query_mode = selectedTemplate?.storyRole?.length === 0 && selectedTemplate?.storyItem?.length === 0;
|
||
const script = selectedTemplate?.freeInputItem && selectedTemplate.freeInputItem.length > 0 ? selectedTemplate.freeInputItem[0].free_input_text : selectedTemplate?.generateText || "";
|
||
|
||
const params: CreateMovieProjectV3Request = {
|
||
script: script,
|
||
category: selectedTemplate?.category || "",
|
||
user_id,
|
||
mode,
|
||
resolution,
|
||
storyRole: selectedTemplate?.storyRole || [],
|
||
storyItem: selectedTemplate?.storyItem || [],
|
||
freeInput: selectedTemplate?.freeInputItem || [],
|
||
language,
|
||
template_id: selectedTemplate?.template_id || "",
|
||
one_query_mode: one_query_mode
|
||
};
|
||
console.log("params", params);
|
||
const result = await MovieProjectService.createProject(
|
||
MovieProjectMode.TEMPLATE,
|
||
params
|
||
);
|
||
return result.project_id;
|
||
} catch (error) {
|
||
console.error("创建电影项目失败:", error);
|
||
throw error;
|
||
} finally {
|
||
// 清除 loading 状态
|
||
setIsLoading(false);
|
||
}
|
||
},
|
||
[selectedTemplate]
|
||
);
|
||
|
||
return {
|
||
templateStoryList,
|
||
selectedTemplate,
|
||
isLoading,
|
||
getTemplateStoryList,
|
||
actionStory,
|
||
setSelectedTemplate,
|
||
AvatarAndAnalyzeFeatures,
|
||
updateRoleImage,
|
||
updateItemImage,
|
||
handleRoleFieldBlur,
|
||
handleItemFieldBlur,
|
||
clearData: () => {
|
||
setTemplateStoryList([]);
|
||
setSelectedTemplate(null);
|
||
},
|
||
};
|
||
};
|