diff --git a/README_TEXT_VIDEO_AGENT_API.md b/README_TEXT_VIDEO_AGENT_API.md new file mode 100644 index 0000000..8134f41 --- /dev/null +++ b/README_TEXT_VIDEO_AGENT_API.md @@ -0,0 +1,430 @@ +# Text Video Agent API 工具库 + +基于 `https://bowongai-dev--text-video-agent-fastapi-app.modal.run` API 的完整 TypeScript 工具库封装。 + +## 📋 目录 + +- [功能特性](#功能特性) +- [安装使用](#安装使用) +- [API 文档](#api-文档) +- [React Hook](#react-hook) +- [组件示例](#组件示例) +- [类型定义](#类型定义) +- [使用示例](#使用示例) + +## 🚀 功能特性 + +### 核心功能 +- ✅ **图片生成**: 基于 Midjourney 的高质量图片生成 +- ✅ **视频生成**: 基于极梦的视频生成功能 +- ✅ **文件上传**: 支持文件上传到云存储 +- ✅ **图片描述**: AI 图片内容分析和描述 +- ✅ **任务管理**: 异步任务创建、查询和管理 + +### 高级特性 +- ✅ **重试机制**: 自动重试失败的请求 +- ✅ **进度跟踪**: 实时进度更新和状态监控 +- ✅ **类型安全**: 完整的 TypeScript 类型定义 +- ✅ **React 集成**: 专用的 React Hook 和组件 +- ✅ **错误处理**: 完善的错误处理和用户反馈 + +## 📦 安装使用 + +### 基础安装 + +```bash +# 复制相关文件到你的项目 +cp src/services/textVideoAgentAPI.ts your-project/src/services/ +cp src/services/textVideoAgentTypes.ts your-project/src/services/ +cp src/hooks/useTextVideoAgent.ts your-project/src/hooks/ +cp src/components/TextVideoGenerator.tsx your-project/src/components/ +``` + +### 依赖要求 + +```json +{ + "dependencies": { + "react": "^18.0.0", + "lucide-react": "^0.263.1" + }, + "devDependencies": { + "typescript": "^5.0.0" + } +} +``` + +## 🔧 API 文档 + +### 基础用法 + +```typescript +import { textVideoAgentAPI } from './services/textVideoAgentAPI' + +// 健康检查 +const health = await textVideoAgentAPI.healthCheck() + +// 生成图片 +const imageResult = await textVideoAgentAPI.generateImageSync({ + prompt: '一个美丽的风景', + max_wait_time: 120 +}) + +// 生成视频 +const videoResult = await textVideoAgentAPI.generateVideoSync({ + prompt: '动态的自然风光', + img_url: 'https://example.com/image.jpg', + duration: '5' +}) +``` + +### 主要方法 + +#### 图片生成 +```typescript +// 同步生成(推荐) +generateImageSync(params: ImageGenerationParams): Promise + +// 异步生成 +generateImageAsync(prompt: string, imgFile?: File): Promise + +// 带重试的生成 +generateImageWithRetry(params: ImageGenerationParams, maxRetries?: number): Promise +``` + +#### 视频生成 +```typescript +// 同步生成 +generateVideoSync(params: VideoGenerationParams): Promise + +// 异步生成 +generateVideoAsync(params: VideoGenerationParams): Promise + +// 带重试的生成 +generateVideoWithRetry(params: VideoGenerationParams, maxRetries?: number): Promise +``` + +#### 任务管理 +```typescript +// 创建任务 +createTask(request: TaskRequest): Promise + +// 查询任务状态 +getTaskStatusAsync(taskId: string): Promise + +// 同步等待任务完成 +getTaskResultSync(taskId: string): Promise +``` + +#### 高级功能 +```typescript +// 端到端内容生成 +generateContentEndToEnd(prompt: string, options?: GenerationOptions): Promise + +// 轮询任务直到完成 +pollTaskUntilComplete(taskId: string, options?: PollingOptions): Promise +``` + +## 🎣 React Hook + +### useTextVideoAgent + +```typescript +import { useTextVideoAgent } from './hooks/useTextVideoAgent' + +function MyComponent() { + const { + state, + generateImage, + generateVideo, + generateContentEndToEnd, + reset, + cancel + } = useTextVideoAgent() + + const handleGenerate = async () => { + const result = await generateContentEndToEnd('美丽的风景', { + taskType: TaskType.VLOG, + aspectRatio: AspectRatio.PORTRAIT, + generateVideo: true + }) + + console.log('生成结果:', result) + } + + return ( +
+ {state.isLoading && ( +
+

{state.currentStep}

+ +
+ )} + + {state.error && ( +
{state.error}
+ )} + + +
+ ) +} +``` + +### useTaskPolling + +```typescript +import { useTaskPolling } from './hooks/useTextVideoAgent' + +function TaskMonitor({ taskId }: { taskId: string }) { + const { status, isPolling } = useTaskPolling(taskId, { + onComplete: (result) => { + console.log('任务完成:', result) + }, + onError: (error) => { + console.error('任务失败:', error) + }, + onProgress: (status) => { + console.log('进度更新:', status) + } + }) + + return ( +
+ {isPolling &&

任务进行中...

} + {status &&
{JSON.stringify(status, null, 2)}
} +
+ ) +} +``` + +## 🧩 组件示例 + +### TextVideoGenerator 组件 + +```typescript +import TextVideoGenerator from './components/TextVideoGenerator' + +function App() { + return ( + { + console.log('图片生成完成:', imageUrl) + }} + onVideoGenerated={(videoUrl) => { + console.log('视频生成完成:', videoUrl) + }} + /> + ) +} +``` + +## 📝 类型定义 + +### 主要接口 + +```typescript +// API 响应 +interface APIResponse { + status: boolean + msg: string + data?: T +} + +// 图片生成参数 +interface ImageGenerationParams { + prompt: string + img_file?: File + max_wait_time?: number + poll_interval?: number +} + +// 视频生成参数 +interface VideoGenerationParams { + prompt: string + img_url?: string + img_file?: File + duration?: string + max_wait_time?: number + poll_interval?: number +} + +// 任务请求 +interface TaskRequest { + task_type?: string + prompt: string + img_url?: string + ar?: string +} +``` + +### 枚举类型 + +```typescript +enum TaskType { + TEA = 'tea', + CHOP = 'chop', + LADY = 'lady', + VLOG = 'vlog' +} + +enum AspectRatio { + SQUARE = '1:1', + PORTRAIT = '9:16', + LANDSCAPE = '16:9' +} + +enum VideoDuration { + SHORT = '3', + MEDIUM = '5', + LONG = '10' +} +``` + +## 💡 使用示例 + +### 示例1: 基础图片生成 + +```typescript +import { textVideoAgentAPI, TaskType, AspectRatio } from './services/textVideoAgentAPI' + +async function generateImage() { + try { + const result = await textVideoAgentAPI.generateImageSync({ + prompt: '一个现代化的咖啡厅,温暖的灯光,舒适的环境', + max_wait_time: 120 + }) + + if (result.status) { + console.log('图片URL:', result.data.image_url) + } + } catch (error) { + console.error('生成失败:', error) + } +} +``` + +### 示例2: 端到端内容生成 + +```typescript +async function generateContent() { + try { + const result = await textVideoAgentAPI.generateContentEndToEnd( + '制作一个关于健康生活的短视频', + { + taskType: TaskType.VLOG, + aspectRatio: AspectRatio.PORTRAIT, + videoDuration: VideoDuration.MEDIUM, + generateVideo: true, + onProgress: (step, progress) => { + console.log(`${step}: ${progress}%`) + } + } + ) + + console.log('生成完成:', result) + } catch (error) { + console.error('生成失败:', error) + } +} +``` + +### 示例3: 批量处理 + +```typescript +async function batchGenerate() { + const prompts = [ + '春天的樱花', + '夏日的海滩', + '秋天的枫叶', + '冬日的雪景' + ] + + const results = await Promise.allSettled( + prompts.map(prompt => + textVideoAgentAPI.generateImageSync({ prompt }) + ) + ) + + results.forEach((result, index) => { + if (result.status === 'fulfilled') { + console.log(`图片 ${index + 1} 生成成功:`, result.value.data?.image_url) + } else { + console.error(`图片 ${index + 1} 生成失败:`, result.reason) + } + }) +} +``` + +## 🔧 配置选项 + +### 预设配置 + +```typescript +import { PRESET_CONFIGS } from './services/textVideoAgentTypes' + +// 快速生成(低质量) +const fastConfig = PRESET_CONFIGS.FAST + +// 标准生成 +const standardConfig = PRESET_CONFIGS.STANDARD + +// 高质量生成 +const highQualityConfig = PRESET_CONFIGS.HIGH_QUALITY +``` + +### 自定义配置 + +```typescript +const customAPI = new TextVideoAgentAPI('https://your-custom-endpoint.com') +``` + +## 🚨 错误处理 + +```typescript +try { + const result = await textVideoAgentAPI.generateImageSync(params) +} catch (error) { + if (error instanceof Error) { + console.error('错误信息:', error.message) + } + + // 处理特定错误类型 + if (error.message.includes('timeout')) { + // 处理超时错误 + } else if (error.message.includes('quota')) { + // 处理配额不足错误 + } +} +``` + +## 📊 性能优化 + +### 缓存策略 +- 图片生成结果自动缓存 +- 任务状态智能轮询 +- 网络请求去重 + +### 最佳实践 +- 使用适当的超时时间 +- 合理设置轮询间隔 +- 及时取消不需要的请求 +- 使用预设配置提高效率 + +## 🤝 贡献指南 + +1. Fork 项目 +2. 创建功能分支 +3. 提交更改 +4. 推送到分支 +5. 创建 Pull Request + +## 📄 许可证 + +MIT License + +## 🆘 支持 + +如有问题或建议,请创建 Issue 或联系开发团队。 diff --git a/examples/textVideoAgentUsage.ts b/examples/textVideoAgentUsage.ts new file mode 100644 index 0000000..ad8f815 --- /dev/null +++ b/examples/textVideoAgentUsage.ts @@ -0,0 +1,416 @@ +/** + * Text Video Agent API 使用示例 + */ + +import { + TextVideoAgentAPI, + textVideoAgentAPI, + ImageGenerationParams, + VideoGenerationParams, + TaskRequest +} from '../src/services/textVideoAgentAPI' + +import { + TaskType, + AspectRatio, + VideoDuration, + PRESET_CONFIGS +} from '../src/services/textVideoAgentTypes' + +// ==================== 基础使用示例 ==================== + +/** + * 示例1: 基础健康检查 + */ +async function example1_HealthCheck() { + console.log('=== 健康检查示例 ===') + + try { + const health = await textVideoAgentAPI.healthCheck() + console.log('服务状态:', health) + + const mjHealth = await textVideoAgentAPI.mjHealthCheck() + console.log('Midjourney状态:', mjHealth) + + const jmHealth = await textVideoAgentAPI.jmHealthCheck() + console.log('极梦状态:', jmHealth) + } catch (error) { + console.error('健康检查失败:', error) + } +} + +/** + * 示例2: 获取示例提示词 + */ +async function example2_GetSamplePrompts() { + console.log('=== 获取示例提示词 ===') + + try { + const prompts = await textVideoAgentAPI.getSamplePrompt(TaskType.VLOG) + console.log('VLOG类型示例提示词:', prompts) + } catch (error) { + console.error('获取提示词失败:', error) + } +} + +/** + * 示例3: 文件上传 + */ +async function example3_FileUpload() { + console.log('=== 文件上传示例 ===') + + // 注意:这里需要实际的文件对象 + // const fileInput = document.getElementById('fileInput') as HTMLInputElement + // const file = fileInput.files?.[0] + + // 模拟文件对象(实际使用时替换为真实文件) + const mockFile = new File(['mock content'], 'test.jpg', { type: 'image/jpeg' }) + + try { + const uploadResult = await textVideoAgentAPI.uploadFile(mockFile) + console.log('文件上传结果:', uploadResult) + return uploadResult.data // 返回文件URL + } catch (error) { + console.error('文件上传失败:', error) + } +} + +// ==================== 图片生成示例 ==================== + +/** + * 示例4: 基础图片生成 + */ +async function example4_BasicImageGeneration() { + console.log('=== 基础图片生成示例 ===') + + const params: ImageGenerationParams = { + prompt: '一个美丽的女孩在喝茶,温馨的下午时光,自然光线,高质量摄影', + max_wait_time: 120, + poll_interval: 2 + } + + try { + const result = await textVideoAgentAPI.generateImageSync(params) + console.log('图片生成结果:', result) + return result.data?.image_url + } catch (error) { + console.error('图片生成失败:', error) + } +} + +/** + * 示例5: 带参考图片的图片生成 + */ +async function example5_ImageGenerationWithReference() { + console.log('=== 带参考图片的图片生成示例 ===') + + // 模拟参考图片文件 + const referenceImage = new File(['reference'], 'reference.jpg', { type: 'image/jpeg' }) + + const params: ImageGenerationParams = { + prompt: '保持人物特征,改变背景为咖啡厅环境', + img_file: referenceImage, + ...PRESET_CONFIGS.STANDARD + } + + try { + const result = await textVideoAgentAPI.generateImageWithRetry(params, 3) + console.log('带参考图片生成结果:', result) + return result.data?.image_url + } catch (error) { + console.error('带参考图片生成失败:', error) + } +} + +/** + * 示例6: 异步图片生成 + */ +async function example6_AsyncImageGeneration() { + console.log('=== 异步图片生成示例 ===') + + const prompt = '现代简约风格的室内设计,明亮的客厅' + + try { + // 提交异步任务 + const taskResult = await textVideoAgentAPI.generateImageAsync(prompt) + const taskId = taskResult.data?.task_id + + if (!taskId) { + throw new Error('未获取到任务ID') + } + + console.log('任务已提交,ID:', taskId) + + // 轮询任务状态 + const finalResult = await textVideoAgentAPI.pollTaskUntilComplete( + taskId, + 2000, // 2秒轮询间隔 + 120000, // 2分钟超时 + (status) => { + console.log('任务状态更新:', status) + } + ) + + console.log('异步图片生成完成:', finalResult) + return finalResult.data?.image_url + } catch (error) { + console.error('异步图片生成失败:', error) + } +} + +// ==================== 视频生成示例 ==================== + +/** + * 示例7: 基础视频生成 + */ +async function example7_BasicVideoGeneration() { + console.log('=== 基础视频生成示例 ===') + + // 首先生成一张图片作为视频的基础 + const imageUrl = await example4_BasicImageGeneration() + + if (!imageUrl) { + console.error('无法获取基础图片,跳过视频生成') + return + } + + const params: VideoGenerationParams = { + prompt: '女孩优雅地品茶,轻柔的动作,温馨的氛围', + img_url: imageUrl, + duration: VideoDuration.MEDIUM, + max_wait_time: 300, + poll_interval: 5 + } + + try { + const result = await textVideoAgentAPI.generateVideoSync(params) + console.log('视频生成结果:', result) + return result.data?.video_url + } catch (error) { + console.error('视频生成失败:', error) + } +} + +/** + * 示例8: 异步视频生成 + */ +async function example8_AsyncVideoGeneration() { + console.log('=== 异步视频生成示例 ===') + + const params: VideoGenerationParams = { + prompt: '城市夜景延时摄影,车流如水,霓虹闪烁', + duration: VideoDuration.LONG + } + + try { + const taskResult = await textVideoAgentAPI.generateVideoAsync(params) + const taskId = taskResult.data?.task_id + + if (!taskId) { + throw new Error('未获取到任务ID') + } + + console.log('视频生成任务已提交,ID:', taskId) + + // 轮询任务状态 + const finalResult = await textVideoAgentAPI.pollTaskUntilComplete( + taskId, + 5000, // 5秒轮询间隔 + 600000, // 10分钟超时 + (status) => { + console.log('视频生成进度:', status) + } + ) + + console.log('异步视频生成完成:', finalResult) + return finalResult.data?.video_url + } catch (error) { + console.error('异步视频生成失败:', error) + } +} + +// ==================== 高级功能示例 ==================== + +/** + * 示例9: 图片描述功能 + */ +async function example9_ImageDescription() { + console.log('=== 图片描述示例 ===') + + const imageUrl = 'https://example.com/sample-image.jpg' + + try { + const description = await textVideoAgentAPI.describeImageByUrl({ + image_url: imageUrl, + max_wait_time: 60 + }) + + console.log('图片描述结果:', description) + return description.data?.description + } catch (error) { + console.error('图片描述失败:', error) + } +} + +/** + * 示例10: 任务管理 + */ +async function example10_TaskManagement() { + console.log('=== 任务管理示例 ===') + + const taskRequest: TaskRequest = { + task_type: TaskType.VLOG, + prompt: '制作一个关于健康生活方式的短视频', + ar: AspectRatio.PORTRAIT + } + + try { + // 创建任务 + const createResult = await textVideoAgentAPI.createTask(taskRequest) + const taskId = createResult.data?.task_id + + if (!taskId) { + throw new Error('任务创建失败') + } + + console.log('任务创建成功,ID:', taskId) + + // 查询任务状态 + const statusResult = await textVideoAgentAPI.getTaskStatusAsync(taskId) + console.log('任务状态:', statusResult) + + // 等待任务完成(同步方式) + const finalResult = await textVideoAgentAPI.getTaskResultSync(taskId) + console.log('任务最终结果:', finalResult) + + return finalResult + } catch (error) { + console.error('任务管理失败:', error) + } +} + +/** + * 示例11: 端到端内容生成 + */ +async function example11_EndToEndGeneration() { + console.log('=== 端到端内容生成示例 ===') + + try { + const result = await textVideoAgentAPI.generateContentEndToEnd( + '一个关于咖啡文化的精美内容,展现咖啡师的专业技艺', + { + taskType: TaskType.VLOG, + aspectRatio: AspectRatio.PORTRAIT, + videoDuration: VideoDuration.MEDIUM, + generateVideo: true, + onProgress: (step, progress) => { + console.log(`进度更新: ${step} - ${progress}%`) + } + } + ) + + console.log('端到端生成完成:', result) + return result + } catch (error) { + console.error('端到端生成失败:', error) + } +} + +// ==================== 批量处理示例 ==================== + +/** + * 示例12: 批量图片生成 + */ +async function example12_BatchImageGeneration() { + console.log('=== 批量图片生成示例 ===') + + const prompts = [ + '春天的樱花盛开', + '夏日的海滩风光', + '秋天的枫叶满山', + '冬日的雪景如画' + ] + + try { + const results = await Promise.allSettled( + prompts.map(async (prompt, index) => { + const params: ImageGenerationParams = { + prompt, + ...PRESET_CONFIGS.FAST // 使用快速配置 + } + + console.log(`开始生成图片 ${index + 1}: ${prompt}`) + const result = await textVideoAgentAPI.generateImageSync(params) + console.log(`图片 ${index + 1} 生成完成`) + + return { + prompt, + result: result.data?.image_url, + success: result.status + } + }) + ) + + console.log('批量图片生成结果:', results) + return results + } catch (error) { + console.error('批量图片生成失败:', error) + } +} + +// ==================== 主函数 ==================== + +/** + * 运行所有示例 + */ +async function runAllExamples() { + console.log('开始运行 Text Video Agent API 示例...\n') + + // 基础功能示例 + await example1_HealthCheck() + await example2_GetSamplePrompts() + + // 文件操作示例 + // await example3_FileUpload() // 需要实际文件 + + // 图片生成示例 + await example4_BasicImageGeneration() + // await example5_ImageGenerationWithReference() // 需要实际文件 + await example6_AsyncImageGeneration() + + // 视频生成示例 + await example7_BasicVideoGeneration() + await example8_AsyncVideoGeneration() + + // 高级功能示例 + await example9_ImageDescription() + await example10_TaskManagement() + await example11_EndToEndGeneration() + + // 批量处理示例 + await example12_BatchImageGeneration() + + console.log('\n所有示例运行完成!') +} + +// 导出示例函数 +export { + example1_HealthCheck, + example2_GetSamplePrompts, + example3_FileUpload, + example4_BasicImageGeneration, + example5_ImageGenerationWithReference, + example6_AsyncImageGeneration, + example7_BasicVideoGeneration, + example8_AsyncVideoGeneration, + example9_ImageDescription, + example10_TaskManagement, + example11_EndToEndGeneration, + example12_BatchImageGeneration, + runAllExamples +} + +// 如果直接运行此文件,执行所有示例 +if (require.main === module) { + runAllExamples().catch(console.error) +} diff --git a/src/App.tsx b/src/App.tsx index b0c9192..b55541b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react' +import React from 'react' import { Routes, Route } from 'react-router-dom' import Layout from './components/Layout' import HomePage from './pages/HomePage' @@ -14,6 +14,7 @@ import ModelManagePage from './pages/ModelManagePage' import AudioLibraryPage from './pages/AudioLibraryPage' import MediaLibraryPage from './pages/MediaLibraryPage' import KVTestPage from './pages/KVTestPage' +import TextVideoGeneratorPage from './pages/TextVideoGeneratorPage' function App() { return ( @@ -27,6 +28,7 @@ function App() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/src/components/QuickActions.tsx b/src/components/QuickActions.tsx index 8ce4f91..2ee1a89 100644 --- a/src/components/QuickActions.tsx +++ b/src/components/QuickActions.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Link } from 'react-router-dom' -import { FolderOpen, Music, Zap, Sparkles, Database, LucideIcon } from 'lucide-react' +import { FolderOpen, Music, Zap, Sparkles, Database, LucideIcon, Wand2 } from 'lucide-react' interface QuickAction { icon: LucideIcon @@ -11,6 +11,7 @@ interface QuickAction { const QuickActions: React.FC = () => { const quickActions: QuickAction[] = [ + { icon: Wand2, label: 'AI 内容生成', description: '基于 Text Video Agent API 的智能内容生成', path: '/text-video-generator' }, { icon: Sparkles, label: 'AI 视频生成', description: '使用 AI 将图片转换为动态视频', path: '/ai-video' }, { icon: Music, label: '音频处理', description: '处理音频文件,添加效果', path: '/audio' }, { icon: Zap, label: 'AI 自动剪辑', description: '使用 AI 自动生成视频剪辑', path: '/editor' }, diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index f82c2b9..e4441d8 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { Link, useLocation } from 'react-router-dom' -import { Home, Video, Settings, FolderOpen, Music, Image, Sparkles, Layout, Clock, CheckCircle, XCircle, List, Tags, User } from 'lucide-react' +import { Home, Video, Settings, FolderOpen, Music, Image, Sparkles, Layout, Clock, CheckCircle, XCircle, List, Tags, User, Wand2 } from 'lucide-react' import { clsx } from 'clsx' const Sidebar: React.FC = () => { @@ -22,6 +22,7 @@ const Sidebar: React.FC = () => { { path: '/', icon: Home, label: '首页' }, { path: '/editor', icon: Video, label: '编辑器' }, { path: '/ai-video', icon: Sparkles, label: 'AI 视频' }, + { path: '/text-video-generator', icon: Wand2, label: 'AI 内容生成' }, { path: '/templates', icon: Layout, label: '模板库' }, { path: '/resource-categories', icon: Tags, label: '分类管理' }, { path: '/projects', icon: FolderOpen, label: '项目库' }, diff --git a/src/components/TextVideoGenerator.tsx b/src/components/TextVideoGenerator.tsx new file mode 100644 index 0000000..6c8fa11 --- /dev/null +++ b/src/components/TextVideoGenerator.tsx @@ -0,0 +1,433 @@ +/** + * Text Video Generator Component + * 使用 Text Video Agent API 的 React 组件示例 + */ + +import React, { useState, useCallback } from 'react' +import { + Upload, + Image, + Video, + Loader, + AlertCircle, + CheckCircle, + Play, + Download, + RefreshCw +} from 'lucide-react' + +import { useTextVideoAgent, useTaskPolling } from '../hooks/useTextVideoAgent' +import { TaskType, AspectRatio, VideoDuration } from '../services/textVideoAgentTypes' + +interface TextVideoGeneratorProps { + onImageGenerated?: (imageUrl: string) => void + onVideoGenerated?: (videoUrl: string) => void +} + +const TextVideoGenerator: React.FC = ({ + onImageGenerated, + onVideoGenerated +}) => { + // Hook状态 + const { + state, + generateImage, + generateVideo, + generateContentEndToEnd, + uploadFile, + describeImageByFile, + reset, + cancel + } = useTextVideoAgent() + + // 本地状态 + const [prompt, setPrompt] = useState('') + const [taskType, setTaskType] = useState(TaskType.VLOG) + const [aspectRatio, setAspectRatio] = useState(AspectRatio.PORTRAIT) + const [videoDuration, setVideoDuration] = useState(VideoDuration.MEDIUM) + const [referenceFile, setReferenceFile] = useState(null) + const [generateVideoEnabled, setGenerateVideoEnabled] = useState(false) + const [results, setResults] = useState<{ + imageUrl?: string + videoUrl?: string + taskId?: string + }>({}) + + // 处理文件上传 + const handleFileUpload = useCallback(async (event: React.ChangeEvent) => { + const file = event.target.files?.[0] + if (!file) return + + setReferenceFile(file) + + // 自动分析图片内容 + try { + const description = await describeImageByFile(file) + if (description) { + setPrompt(prev => prev ? `${prev}\n\n参考图片描述: ${description}` : description) + } + } catch (error) { + console.error('图片分析失败:', error) + } + }, [describeImageByFile]) + + // 生成内容 + const handleGenerate = useCallback(async () => { + if (!prompt.trim()) { + alert('请输入生成提示词') + return + } + + try { + const result = await generateContentEndToEnd(prompt, { + taskType, + aspectRatio, + videoDuration, + generateVideo: generateVideoEnabled + }) + + if (result) { + setResults(result) + + if (result.imageUrl && onImageGenerated) { + onImageGenerated(result.imageUrl) + } + + if (result.videoUrl && onVideoGenerated) { + onVideoGenerated(result.videoUrl) + } + } + } catch (error) { + console.error('生成失败:', error) + } + }, [ + prompt, + taskType, + aspectRatio, + videoDuration, + generateVideoEnabled, + generateContentEndToEnd, + onImageGenerated, + onVideoGenerated + ]) + + // 只生成图片 + const handleGenerateImageOnly = useCallback(async () => { + if (!prompt.trim()) { + alert('请输入生成提示词') + return + } + + try { + const imageUrl = await generateImage({ + prompt, + img_file: referenceFile || undefined, + max_wait_time: 120 + }) + + if (imageUrl) { + setResults(prev => ({ ...prev, imageUrl })) + onImageGenerated?.(imageUrl) + } + } catch (error) { + console.error('图片生成失败:', error) + } + }, [prompt, referenceFile, generateImage, onImageGenerated]) + + // 基于图片生成视频 + const handleGenerateVideoFromImage = useCallback(async () => { + if (!results.imageUrl) { + alert('请先生成图片') + return + } + + try { + const videoUrl = await generateVideo({ + prompt, + img_url: results.imageUrl, + duration: videoDuration, + max_wait_time: 300 + }) + + if (videoUrl) { + setResults(prev => ({ ...prev, videoUrl })) + onVideoGenerated?.(videoUrl) + } + } catch (error) { + console.error('视频生成失败:', error) + } + }, [results.imageUrl, prompt, videoDuration, generateVideo, onVideoGenerated]) + + // 重置所有状态 + const handleReset = useCallback(() => { + reset() + setPrompt('') + setReferenceFile(null) + setResults({}) + }, [reset]) + + return ( +
+ {/* 标题 */} +
+

AI 内容生成器

+

基于 Text Video Agent API 的智能内容生成工具

+
+ + {/* 输入区域 */} +
+ {/* 提示词输入 */} +
+ +