video-flow-b/app/service/Interaction/templateStoryService.ts
2025-09-16 19:20:00 +08:00

311 lines
9.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
},
};
};