🎬 主要功能: - ✅ 完整的 AI 视频生成模块 (Python) - ✅ 图片转视频 API 集成 (字节跳动 Seedance) - ✅ 云存储支持 (腾讯云 COS) - ✅ 单张图片和批量处理模式 - ✅ 现代化 React 界面组件 - ✅ Tauri 桥接通信 🛠️ 技术实现: - Python 模块:VideoGenerator, CloudStorage, APIClient - Rust 命令:generate_ai_video, batch_generate_ai_videos - React 组件:AIVideoGenerator, AIVideoPage - 状态管理:useAIVideoStore (Zustand) - 路由集成:/ai-video 页面 �� 新增文件: - python_core/ai_video/ - AI 视频生成核心模块 - src/components/AIVideoGenerator.tsx - 主要 UI 组件 - src/pages/AIVideoPage.tsx - AI 视频生成页面 - src/stores/useAIVideoStore.ts - 状态管理 🎯 功能特性: - 支持 Lite (720p) 和 Pro (1080p) 模型 - 可配置视频时长 (5秒/10秒) - 实时进度跟踪和任务管理 - 批量处理多张图片 - 云存储自动上传下载 - 错误处理和重试机制 🔗 界面集成: - 侧边栏导航添加 'AI 视频' 入口 - 首页快速操作卡片 - 完整的用户引导和帮助文档 这是从原始 Tkinter GUI 到现代 Web 应用的完整迁移!
406 lines
8.7 KiB
TypeScript
406 lines
8.7 KiB
TypeScript
/**
|
|
* Tauri API Service
|
|
* Handles communication between frontend and Tauri backend
|
|
*/
|
|
|
|
import { invoke } from '@tauri-apps/api/core'
|
|
|
|
// Types for video processing
|
|
export interface VideoProcessRequest {
|
|
input_path: string
|
|
output_path: string
|
|
operation: string
|
|
parameters: Record<string, any>
|
|
}
|
|
|
|
export interface AudioAnalysisRequest {
|
|
file_path: string
|
|
analysis_type: string
|
|
}
|
|
|
|
export interface AIVideoRequest {
|
|
image_path: string
|
|
prompt: string
|
|
duration: string
|
|
model_type: string
|
|
output_path?: string
|
|
timeout?: number
|
|
}
|
|
|
|
export interface BatchAIVideoRequest {
|
|
image_folder: string
|
|
prompts: string[]
|
|
output_folder: string
|
|
duration: string
|
|
model_type: string
|
|
timeout?: number
|
|
}
|
|
|
|
export interface ProjectInfo {
|
|
id: string
|
|
name: string
|
|
path: string
|
|
created_at: string
|
|
modified_at: string
|
|
video_tracks: VideoTrack[]
|
|
audio_tracks: AudioTrack[]
|
|
timeline: {
|
|
duration: number
|
|
zoom: number
|
|
position: number
|
|
}
|
|
}
|
|
|
|
export interface VideoTrack {
|
|
id: string
|
|
name: string
|
|
file_path: string
|
|
start_time: number
|
|
duration: number
|
|
}
|
|
|
|
export interface AudioTrack {
|
|
id: string
|
|
name: string
|
|
file_path: string
|
|
start_time: number
|
|
duration: number
|
|
volume: number
|
|
}
|
|
|
|
// Tauri command wrappers
|
|
export class TauriService {
|
|
/**
|
|
* Test connection to backend
|
|
*/
|
|
static async greet(name: string): Promise<string> {
|
|
try {
|
|
return await invoke('greet', { name })
|
|
} catch (error) {
|
|
console.error('Failed to greet:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process video with specified operation
|
|
*/
|
|
static async processVideo(request: VideoProcessRequest): Promise<any> {
|
|
try {
|
|
const result = await invoke('process_video', { request })
|
|
return JSON.parse(result as string)
|
|
} catch (error) {
|
|
console.error('Failed to process video:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Analyze audio file
|
|
*/
|
|
static async analyzeAudio(request: AudioAnalysisRequest): Promise<any> {
|
|
try {
|
|
const result = await invoke('analyze_audio', { request })
|
|
return JSON.parse(result as string)
|
|
} catch (error) {
|
|
console.error('Failed to analyze audio:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get project information
|
|
*/
|
|
static async getProjectInfo(projectPath: string): Promise<ProjectInfo> {
|
|
try {
|
|
return await invoke('get_project_info', { projectPath })
|
|
} catch (error) {
|
|
console.error('Failed to get project info:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save project
|
|
*/
|
|
static async saveProject(projectInfo: ProjectInfo): Promise<string> {
|
|
try {
|
|
return await invoke('save_project', { projectInfo })
|
|
} catch (error) {
|
|
console.error('Failed to save project:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load project
|
|
*/
|
|
static async loadProject(projectPath: string): Promise<ProjectInfo> {
|
|
try {
|
|
return await invoke('load_project', { projectPath })
|
|
} catch (error) {
|
|
console.error('Failed to load project:', error)
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
|
|
// Video processing operations
|
|
export class VideoService {
|
|
/**
|
|
* Trim video to specified duration
|
|
*/
|
|
static async trimVideo(
|
|
inputPath: string,
|
|
outputPath: string,
|
|
startTime: number,
|
|
endTime: number
|
|
): Promise<any> {
|
|
return TauriService.processVideo({
|
|
input_path: inputPath,
|
|
output_path: outputPath,
|
|
operation: 'trim',
|
|
parameters: { start_time: startTime, end_time: endTime }
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Resize video to specified dimensions
|
|
*/
|
|
static async resizeVideo(
|
|
inputPath: string,
|
|
outputPath: string,
|
|
width?: number,
|
|
height?: number
|
|
): Promise<any> {
|
|
return TauriService.processVideo({
|
|
input_path: inputPath,
|
|
output_path: outputPath,
|
|
operation: 'resize',
|
|
parameters: { width, height }
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Crop video to specified region
|
|
*/
|
|
static async cropVideo(
|
|
inputPath: string,
|
|
outputPath: string,
|
|
x1: number,
|
|
y1: number,
|
|
x2: number,
|
|
y2: number
|
|
): Promise<any> {
|
|
return TauriService.processVideo({
|
|
input_path: inputPath,
|
|
output_path: outputPath,
|
|
operation: 'crop',
|
|
parameters: { x1, y1, x2, y2 }
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Adjust video brightness
|
|
*/
|
|
static async adjustBrightness(
|
|
inputPath: string,
|
|
outputPath: string,
|
|
factor: number
|
|
): Promise<any> {
|
|
return TauriService.processVideo({
|
|
input_path: inputPath,
|
|
output_path: outputPath,
|
|
operation: 'adjust_brightness',
|
|
parameters: { factor }
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Adjust video contrast
|
|
*/
|
|
static async adjustContrast(
|
|
inputPath: string,
|
|
outputPath: string,
|
|
factor: number
|
|
): Promise<any> {
|
|
return TauriService.processVideo({
|
|
input_path: inputPath,
|
|
output_path: outputPath,
|
|
operation: 'adjust_contrast',
|
|
parameters: { factor }
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Add text overlay to video
|
|
*/
|
|
static async addText(
|
|
inputPath: string,
|
|
outputPath: string,
|
|
text: string,
|
|
options: {
|
|
fontsize?: number
|
|
color?: string
|
|
position?: [string, string]
|
|
duration?: number
|
|
} = {}
|
|
): Promise<any> {
|
|
return TauriService.processVideo({
|
|
input_path: inputPath,
|
|
output_path: outputPath,
|
|
operation: 'add_text',
|
|
parameters: { text, ...options }
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Merge multiple videos
|
|
*/
|
|
static async mergeVideos(
|
|
videoPaths: string[],
|
|
outputPath: string
|
|
): Promise<any> {
|
|
return TauriService.processVideo({
|
|
input_path: '', // Not used for merge
|
|
output_path: outputPath,
|
|
operation: 'merge',
|
|
parameters: { video_paths: videoPaths }
|
|
})
|
|
}
|
|
}
|
|
|
|
// Audio processing operations
|
|
export class AudioService {
|
|
/**
|
|
* Analyze rhythm and beat tracking
|
|
*/
|
|
static async analyzeRhythm(filePath: string): Promise<any> {
|
|
return TauriService.analyzeAudio({
|
|
file_path: filePath,
|
|
analysis_type: 'rhythm'
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Analyze spectral features
|
|
*/
|
|
static async analyzeSpectral(filePath: string): Promise<any> {
|
|
return TauriService.analyzeAudio({
|
|
file_path: filePath,
|
|
analysis_type: 'spectral'
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Analyze tempo
|
|
*/
|
|
static async analyzeTempo(filePath: string): Promise<any> {
|
|
return TauriService.analyzeAudio({
|
|
file_path: filePath,
|
|
analysis_type: 'tempo'
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Analyze pitch
|
|
*/
|
|
static async analyzePitch(filePath: string): Promise<any> {
|
|
return TauriService.analyzeAudio({
|
|
file_path: filePath,
|
|
analysis_type: 'pitch'
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Analyze energy
|
|
*/
|
|
static async analyzeEnergy(filePath: string): Promise<any> {
|
|
return TauriService.analyzeAudio({
|
|
file_path: filePath,
|
|
analysis_type: 'energy'
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Analyze MFCC features
|
|
*/
|
|
static async analyzeMFCC(filePath: string): Promise<any> {
|
|
return TauriService.analyzeAudio({
|
|
file_path: filePath,
|
|
analysis_type: 'mfcc'
|
|
})
|
|
}
|
|
}
|
|
|
|
// Project management operations
|
|
export class ProjectService {
|
|
/**
|
|
* Create new project
|
|
*/
|
|
static async createProject(name: string, description: string = ''): Promise<ProjectInfo> {
|
|
// This would typically call a Python script to create the project
|
|
// For now, we'll create a basic project structure
|
|
const projectInfo: ProjectInfo = {
|
|
id: crypto.randomUUID(),
|
|
name,
|
|
path: `/projects/${name}`,
|
|
created_at: new Date().toISOString(),
|
|
modified_at: new Date().toISOString(),
|
|
video_tracks: [],
|
|
audio_tracks: [],
|
|
timeline: {
|
|
duration: 0,
|
|
zoom: 1,
|
|
position: 0
|
|
}
|
|
}
|
|
|
|
await TauriService.saveProject(projectInfo)
|
|
return projectInfo
|
|
}
|
|
|
|
/**
|
|
* Load existing project
|
|
*/
|
|
static async loadProject(projectPath: string): Promise<ProjectInfo> {
|
|
return TauriService.loadProject(projectPath)
|
|
}
|
|
|
|
/**
|
|
* Save project
|
|
*/
|
|
static async saveProject(projectInfo: ProjectInfo): Promise<void> {
|
|
await TauriService.saveProject(projectInfo)
|
|
}
|
|
}
|
|
|
|
// AI Video generation operations
|
|
export class AIVideoService {
|
|
/**
|
|
* Generate video from single image
|
|
*/
|
|
static async generateVideo(request: AIVideoRequest): Promise<any> {
|
|
try {
|
|
const result = await invoke('generate_ai_video', { request })
|
|
return JSON.parse(result as string)
|
|
} catch (error) {
|
|
console.error('Failed to generate AI video:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Batch generate videos from multiple images
|
|
*/
|
|
static async batchGenerateVideos(request: BatchAIVideoRequest): Promise<any> {
|
|
try {
|
|
const result = await invoke('batch_generate_ai_videos', { request })
|
|
return JSON.parse(result as string)
|
|
} catch (error) {
|
|
console.error('Failed to batch generate AI videos:', error)
|
|
throw error
|
|
}
|
|
}
|
|
}
|