forked from 77media/video-flow
227 lines
6.5 KiB
TypeScript
227 lines
6.5 KiB
TypeScript
import { ImageStoryEntity } from "../domain/Entities";
|
|
import { useUploadFile } from "../domain/service";
|
|
import { ImageStoryUseCase } from "../usecase/imageStoryUseCase";
|
|
import { useState, useCallback, useMemo } from "react";
|
|
|
|
interface UseImageStoryService {
|
|
/** 当前图片故事数据 */
|
|
imageStory: Partial<ImageStoryEntity>;
|
|
/** 当前活跃的图片地址 */
|
|
activeImageUrl: string;
|
|
/** 分析故事结果内容 */
|
|
analyzedStoryContent: string;
|
|
/** 当前选中的分类 */
|
|
selectedCategory: string;
|
|
/** 是否正在分析图片 */
|
|
isAnalyzing: boolean;
|
|
/** 是否正在上传 */
|
|
isUploading: boolean;
|
|
/** 故事类型选项 */
|
|
storyTypeOptions: Array<{ key: string; label: string }>;
|
|
/** 上传图片并分析 */
|
|
uploadAndAnalyzeImage: (imageUrl: string) => Promise<void>;
|
|
/** 触发文件选择并自动分析 */
|
|
triggerFileSelectionAndAnalyze: () => Promise<void>;
|
|
/** 触发生成剧本函数 */
|
|
generateScript: () => Promise<string>;
|
|
/** 更新故事类型 */
|
|
updateStoryType: (storyType: string) => void;
|
|
/** 更新故事内容 */
|
|
updateStoryContent: (content: string) => void;
|
|
/** 重置图片故事数据 */
|
|
resetImageStory: () => void;
|
|
}
|
|
|
|
export const useImageStoryServiceHook = (
|
|
): UseImageStoryService => {
|
|
const [imageStory, setImageStory] = useState<Partial<ImageStoryEntity>>({
|
|
imageUrl: "",
|
|
imageStory: "",
|
|
storyType: "auto",
|
|
});
|
|
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
|
const [isUploading, setIsUploading] = useState(false);
|
|
// 使用上传文件Hook
|
|
const { uploadFile } = useUploadFile();
|
|
|
|
/** 图片故事用例实例 */
|
|
const imageStoryUseCase = useMemo(() => new ImageStoryUseCase(), []);
|
|
|
|
/** 当前活跃的图片地址 */
|
|
const [activeImageUrl, setActiveImageUrl] = useState<string>("");
|
|
|
|
/** 分析故事结果内容 */
|
|
const [analyzedStoryContent, setAnalyzedStoryContent] = useState<string>("");
|
|
|
|
/** 当前选中的分类 */
|
|
const [selectedCategory, setSelectedCategory] = useState<string>("auto");
|
|
|
|
/** 故事类型选项 */
|
|
const storyTypeOptions = useMemo(() => imageStoryUseCase.getStoryTypeOptions(), [imageStoryUseCase]);
|
|
|
|
/**
|
|
* 上传图片并分析
|
|
* @param {string} imageUrl - 已上传的图片URL
|
|
*/
|
|
const uploadAndAnalyzeImage = useCallback(async (imageUrl: string): Promise<void> => {
|
|
try {
|
|
setIsUploading(true);
|
|
setIsAnalyzing(true);
|
|
|
|
// 调用用例处理图片上传和分析
|
|
await imageStoryUseCase.handleImageUpload(imageUrl);
|
|
|
|
// 获取更新后的数据
|
|
const updatedStory = imageStoryUseCase.getImageStory();
|
|
setImageStory(updatedStory);
|
|
|
|
// 更新活跃状态
|
|
setActiveImageUrl(imageUrl);
|
|
setAnalyzedStoryContent(updatedStory.imageStory || "");
|
|
setSelectedCategory(updatedStory.storyType || "auto");
|
|
|
|
|
|
|
|
} catch (error) {
|
|
console.error('图片上传分析失败:', error);
|
|
throw error;
|
|
} finally {
|
|
setIsUploading(false);
|
|
setIsAnalyzing(false);
|
|
}
|
|
}, [imageStoryUseCase]);
|
|
|
|
/**
|
|
* 触发生成剧本函数
|
|
* @returns {Promise<string>} 生成的剧本ID或内容
|
|
*/
|
|
const generateScript = useCallback(async (): Promise<string> => {
|
|
if (!activeImageUrl) {
|
|
throw new Error('请先上传图片');
|
|
}
|
|
|
|
if (!analyzedStoryContent) {
|
|
throw new Error('请先输入或生成故事内容');
|
|
}
|
|
|
|
try {
|
|
setIsAnalyzing(true);
|
|
|
|
// 这里可以调用后端API生成剧本
|
|
// 暂时返回一个模拟的剧本ID
|
|
const scriptId = `script_${Date.now()}`;
|
|
|
|
// TODO: 实现实际的剧本生成逻辑
|
|
// const response = await generateScriptFromImage(imageStory);
|
|
// return response.scriptId;
|
|
|
|
return scriptId;
|
|
} catch (error) {
|
|
console.error('生成剧本失败:', error);
|
|
throw error;
|
|
} finally {
|
|
setIsAnalyzing(false);
|
|
}
|
|
}, [activeImageUrl, analyzedStoryContent, imageStory]);
|
|
|
|
/**
|
|
* 更新故事类型
|
|
* @param {string} storyType - 新的故事类型
|
|
*/
|
|
const updateStoryType = useCallback((storyType: string): void => {
|
|
imageStoryUseCase.updateStoryType(storyType);
|
|
setImageStory(prev => ({ ...prev, storyType }));
|
|
setSelectedCategory(storyType);
|
|
}, [imageStoryUseCase]);
|
|
|
|
/**
|
|
* 更新故事内容
|
|
* @param {string} content - 新的故事内容
|
|
*/
|
|
const updateStoryContent = useCallback((content: string): void => {
|
|
imageStoryUseCase.updateStoryContent(content);
|
|
setImageStory(prev => ({ ...prev, imageStory: content }));
|
|
setAnalyzedStoryContent(content);
|
|
|
|
|
|
}, [imageStoryUseCase]);
|
|
|
|
/**
|
|
* 重置图片故事数据
|
|
*/
|
|
const resetImageStory = useCallback((): void => {
|
|
imageStoryUseCase.resetImageStory();
|
|
setImageStory({
|
|
imageUrl: "",
|
|
imageStory: "",
|
|
storyType: "auto",
|
|
});
|
|
// 重置活跃状态
|
|
setActiveImageUrl("");
|
|
setAnalyzedStoryContent("");
|
|
setSelectedCategory("auto");
|
|
setIsAnalyzing(false);
|
|
setIsUploading(false);
|
|
|
|
|
|
}, [imageStoryUseCase]);
|
|
|
|
/**
|
|
* 触发文件选择并自动分析
|
|
*/
|
|
const triggerFileSelectionAndAnalyze = useCallback(async (): Promise<void> => {
|
|
return new Promise((resolve, reject) => {
|
|
// 创建文件输入元素
|
|
const fileInput = document.createElement("input");
|
|
fileInput.type = "file";
|
|
fileInput.accept = "image/*";
|
|
fileInput.style.display = "none";
|
|
|
|
fileInput.onchange = async (e) => {
|
|
try {
|
|
const target = e.target as HTMLInputElement;
|
|
if (target.files && target.files[0]) {
|
|
// 使用传入的文件上传函数
|
|
const uploadedImageUrl = await uploadFile(target.files[0], (progress) => {
|
|
console.log("上传进度:", progress);
|
|
});
|
|
console.log('uploadedImageUrl', uploadedImageUrl)
|
|
// await uploadAndAnalyzeImage(uploadedImageUrl);
|
|
setActiveImageUrl(uploadedImageUrl);
|
|
}
|
|
resolve();
|
|
} catch (error) {
|
|
reject(error);
|
|
} finally {
|
|
// 清理DOM
|
|
document.body.removeChild(fileInput);
|
|
}
|
|
};
|
|
|
|
fileInput.oncancel = () => {
|
|
document.body.removeChild(fileInput);
|
|
reject();
|
|
};
|
|
|
|
document.body.appendChild(fileInput);
|
|
fileInput.click();
|
|
});
|
|
}, [uploadFile]);
|
|
|
|
return {
|
|
imageStory,
|
|
activeImageUrl,
|
|
analyzedStoryContent,
|
|
selectedCategory,
|
|
isAnalyzing,
|
|
isUploading,
|
|
storyTypeOptions,
|
|
uploadAndAnalyzeImage,
|
|
triggerFileSelectionAndAnalyze,
|
|
generateScript,
|
|
updateStoryType,
|
|
updateStoryContent,
|
|
resetImageStory,
|
|
};
|
|
};
|