Files
mxivideo/src/stores/useAIVideoStore.ts
root 8a497afa47 fix: 修复前端批量处理服务的 JSON-RPC 解析问题
🔧 前端修复:

1. 批量处理服务 JSON-RPC 支持:
   - 为 batchGenerateVideos 添加 JSON-RPC 2.0 格式解析
   - 检测 jsonrpc: '2.0' 并提取 result 字段
   - 处理 JSON-RPC 错误响应
   - 保持向后兼容直接 JSON 格式

2. 详细的调试日志:
   - 添加批量请求和响应的详细日志
   - 显示原始和解析后的结果
   - 区分 JSON-RPC 成功和错误响应
   - 便于问题排查和调试

3. Store 状态判断增强:
   - 添加详细的状态检查日志
   - 显示 result.status 的值和类型
   - 记录成功和失败的处理路径
   - 帮助诊断状态识别问题

4. 错误处理统一:
   - 批量处理和单个处理使用相同的 JSON-RPC 解析逻辑
   - 统一的错误信息格式
   - 完整的错误详情记录

🎯 问题解决:
- 批量处理服务缺少 JSON-RPC 解析 → 添加完整解析逻辑 ✓
- 前端显示失败状态 → 正确提取 JSON-RPC result ✓
- 调试信息不足 → 添加详细日志 ✓

 修复效果:
- 批量处理正确解析 JSON-RPC 响应
- 前端能够识别批量任务的真实状态
- 详细的调试信息便于问题排查
- 统一的 JSON-RPC 处理逻辑

现在批量处理应该能正确显示成功状态!
2025-07-10 13:20:54 +08:00

243 lines
7.4 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.
/**
* AI Video Store
* Manages AI video generation state and operations
*/
import { create } from 'zustand'
import { AIVideoService, AIVideoRequest, BatchAIVideoRequest } from '../services/tauri'
interface AIVideoJob {
id: string
type: 'single' | 'batch'
status: 'pending' | 'processing' | 'completed' | 'failed'
progress: number
request: AIVideoRequest | BatchAIVideoRequest
result?: any
error?: string
startTime: number
endTime?: number
}
interface AIVideoState {
// Jobs
jobs: AIVideoJob[]
// Current processing
isProcessing: boolean
// Settings
defaultPrompts: string[]
defaultDuration: string
defaultModelType: string
// Actions
addJob: (request: AIVideoRequest | BatchAIVideoRequest, type: 'single' | 'batch') => string
updateJob: (jobId: string, updates: Partial<AIVideoJob>) => void
removeJob: (jobId: string) => void
clearCompletedJobs: () => void
// AI Video operations
generateSingleVideo: (request: AIVideoRequest) => Promise<string>
batchGenerateVideos: (request: BatchAIVideoRequest) => Promise<string>
// Settings
setDefaultPrompts: (prompts: string[]) => void
setDefaultDuration: (duration: string) => void
setDefaultModelType: (modelType: string) => void
// Utility
setError: (error: string | null) => void
}
// Default prompts from the original GUI
const DEFAULT_PROMPTS = [
"女人扭动身体向摄像机展示身材,一只手撩了一下头发,镜头从左向右移动并放大画面",
"时尚模特抬头自信的展示身材,扭动身体,一只手放在了头上,镜头逐渐放大聚焦在了下衣上",
"女人扭动身体向摄像机展示身材,一只手撩了一下头发后放在了裤子上,镜头从左向右移动",
"自信步伐跟拍模特,模特步伐自信地同时行走,镜头紧紧跟随。抬起手捋一捋头发。传递出自信与时尚的气息。",
"女生两只手捏着拳头轻盈的左右摇摆跳舞动作幅度不大然后把手摊开放在胸口再做出像popping心脏跳动的动作左右身体都要非常协调",
"一个年轻女子自信地在相机前展示了她优美的身材,以自然的流体动作自由地摇摆,左手撩了一下头发之后停在了胸前。一个美女自拍跳舞扭动身体的视频,手从下到上最后放在胸前,妩媚的表情",
"美女向后退了一步站在那里展示服装,双手轻轻提了一下裤子两侧,镜头从上到下逐渐放大",
"女人低头看向裤子向镜头展示身材一只手放在了头上做pose动作",
"美女向后退了一步站在那里展示服装,低头并用手抚摸裤子,镜头从上到下逐渐放大",
"美女向后退了一步站在那里展示服装,双手从上到下整理衣服,自然扭动身体,自信的表情"
]
export const useAIVideoStore = create<AIVideoState>((set, get) => ({
// Initial state
jobs: [],
isProcessing: false,
defaultPrompts: DEFAULT_PROMPTS,
defaultDuration: '5',
defaultModelType: 'lite',
// Job management
addJob: (request, type) => {
const job: AIVideoJob = {
id: crypto.randomUUID(),
type,
status: 'pending',
progress: 0,
request,
startTime: Date.now()
}
set(state => ({
jobs: [...state.jobs, job]
}))
return job.id
},
updateJob: (jobId, updates) => {
// Throttle rapid updates to prevent excessive re-renders
const now = Date.now()
const lastUpdate = get().jobs.find(job => job.id === jobId)?.endTime || 0
if (now - lastUpdate < 100 && updates.progress !== undefined) {
// Skip rapid progress updates
return
}
set(state => ({
jobs: state.jobs.map(job =>
job.id === jobId ? { ...job, ...updates } : job
)
}))
},
removeJob: (jobId) => {
set(state => ({
jobs: state.jobs.filter(job => job.id !== jobId)
}))
},
clearCompletedJobs: () => {
set(state => ({
jobs: state.jobs.filter(job => job.status !== 'completed' && job.status !== 'failed')
}))
},
// AI Video operations
generateSingleVideo: async (request) => {
const { addJob, updateJob } = get()
const jobId = addJob(request, 'single')
try {
set({ isProcessing: true })
updateJob(jobId, { status: 'processing', progress: 0 })
const result = await AIVideoService.generateVideo(request)
// Check if the Python script actually succeeded
if (result && result.status === true) {
updateJob(jobId, {
status: 'completed',
progress: 100,
result,
endTime: Date.now()
})
} else {
// Python script returned failure
const errorMsg = result?.msg || 'Python script execution failed'
updateJob(jobId, {
status: 'failed',
error: errorMsg,
result,
endTime: Date.now()
})
throw new Error(errorMsg)
}
return jobId
} catch (error) {
updateJob(jobId, {
status: 'failed',
error: error instanceof Error ? error.message : 'Unknown error',
endTime: Date.now()
})
throw error
} finally {
set({ isProcessing: false })
}
},
batchGenerateVideos: async (request) => {
const { addJob, updateJob } = get()
const jobId = addJob(request, 'batch')
try {
set({ isProcessing: true })
updateJob(jobId, { status: 'processing', progress: 0 })
const result = await AIVideoService.batchGenerateVideos(request)
console.log('Batch processing result in store:', result)
console.log('Result status:', result?.status)
console.log('Result type:', typeof result?.status)
// Check if the Python script actually succeeded
if (result && result.status === true) {
console.log('Batch processing succeeded, updating job to completed')
updateJob(jobId, {
status: 'completed',
progress: 100,
result,
endTime: Date.now()
})
} else {
// Python script returned failure
console.log('Batch processing failed, result:', result)
const errorMsg = result?.msg || 'Batch processing failed'
updateJob(jobId, {
status: 'failed',
error: errorMsg,
result,
endTime: Date.now()
})
throw new Error(errorMsg)
}
return jobId
} catch (error) {
updateJob(jobId, {
status: 'failed',
error: error instanceof Error ? error.message : 'Unknown error',
endTime: Date.now()
})
throw error
} finally {
set({ isProcessing: false })
}
},
// Settings
setDefaultPrompts: (prompts) => {
set({ defaultPrompts: prompts })
},
setDefaultDuration: (duration) => {
set({ defaultDuration: duration })
},
setDefaultModelType: (modelType) => {
set({ defaultModelType: modelType })
},
// Utility
setError: (error) => {
// This could be used for global error handling
console.error('AI Video Error:', error)
}
}))
// Selectors for easier access to specific state
export const useAIVideoJobs = () => useAIVideoStore(state => state.jobs)
export const useAIVideoProcessing = () => useAIVideoStore(state => state.isProcessing)
export const useAIVideoSettings = () => useAIVideoStore(state => ({
defaultPrompts: state.defaultPrompts,
defaultDuration: state.defaultDuration,
defaultModelType: state.defaultModelType
}))