forked from 77media/video-flow
356 lines
12 KiB
TypeScript
356 lines
12 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||
import { getRandomMockData, STEP_MESSAGES, MOCK_DELAY_TIME, MOCK_DATA } from '@/components/work-flow/constants';
|
||
|
||
// 当前选择的mock数据
|
||
let selectedMockData: any = null;
|
||
|
||
export function useWorkflowData() {
|
||
const [taskObject, setTaskObject] = useState<any>(null);
|
||
const [taskSketch, setTaskSketch] = useState<any[]>([]);
|
||
const [taskRoles, setTaskRoles] = useState<any[]>([]);
|
||
const [taskVideos, setTaskVideos] = useState<any[]>([]);
|
||
const [sketchCount, setSketchCount] = useState(0);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [currentStep, setCurrentStep] = useState('0');
|
||
const [currentSketchIndex, setCurrentSketchIndex] = useState(0);
|
||
const [isGeneratingSketch, setIsGeneratingSketch] = useState(false);
|
||
const [isGeneratingVideo, setIsGeneratingVideo] = useState(false);
|
||
const [currentLoadingText, setCurrentLoadingText] = useState('正在加载项目数据...');
|
||
const [dataLoadError, setDataLoadError] = useState<string | null>(null);
|
||
const [isLoadingData, setIsLoadingData] = useState(false);
|
||
|
||
// 异步加载数据 - 改进错误处理和fallback机制
|
||
const loadMockData = async () => {
|
||
try {
|
||
setIsLoadingData(true);
|
||
setDataLoadError(null);
|
||
setCurrentLoadingText('正在从服务器获取项目数据...');
|
||
|
||
// 尝试从接口获取数据
|
||
selectedMockData = await getRandomMockData();
|
||
|
||
console.log('成功从接口获取数据:', selectedMockData);
|
||
setCurrentLoadingText('项目数据加载完成');
|
||
|
||
} catch (error) {
|
||
console.warn('接口获取数据失败,使用本地fallback数据:', error);
|
||
|
||
try {
|
||
// 使用本地fallback数据
|
||
setCurrentLoadingText('正在加载本地数据...');
|
||
const randomIndex = Math.floor(Math.random() * MOCK_DATA.length);
|
||
selectedMockData = MOCK_DATA[randomIndex];
|
||
|
||
console.log('使用本地fallback数据:', selectedMockData);
|
||
setCurrentLoadingText('本地数据加载完成');
|
||
|
||
} catch (fallbackError) {
|
||
console.error('本地数据加载也失败:', fallbackError);
|
||
// 最后的fallback - 直接使用第一组数据
|
||
selectedMockData = MOCK_DATA[0];
|
||
setDataLoadError('服务器连接失败,已切换到演示模式');
|
||
setCurrentLoadingText('演示数据加载完成');
|
||
}
|
||
} finally {
|
||
setIsLoadingData(false);
|
||
}
|
||
};
|
||
|
||
// 重试加载数据
|
||
const retryLoadData = async () => {
|
||
console.log('用户点击重试,重新加载数据...');
|
||
selectedMockData = null; // 重置数据
|
||
setDataLoadError(null);
|
||
setIsLoading(true);
|
||
setCurrentStep('0');
|
||
|
||
// 重新初始化整个流程
|
||
await initializeWorkflow();
|
||
};
|
||
|
||
// 模拟接口请求 获取任务详情
|
||
const getTaskDetail = async (taskId: string) => {
|
||
// 确保已经加载了数据
|
||
if (!selectedMockData) {
|
||
console.warn('selectedMockData为空,重新加载数据');
|
||
await loadMockData();
|
||
}
|
||
|
||
// 确保数据结构正确
|
||
if (!selectedMockData || !selectedMockData.detail) {
|
||
throw new Error('数据结构不正确');
|
||
}
|
||
|
||
const data = {
|
||
projectId: selectedMockData.detail.projectId,
|
||
projectName: selectedMockData.detail.projectName,
|
||
taskId: taskId,
|
||
taskName: selectedMockData.detail.taskName,
|
||
taskDescription: selectedMockData.detail.taskDescription,
|
||
taskStatus: selectedMockData.detail.taskStatus,
|
||
taskProgress: 0,
|
||
mode: selectedMockData.detail.mode,
|
||
resolution: selectedMockData.detail.resolution.toString(),
|
||
};
|
||
return data;
|
||
};
|
||
|
||
// 模拟接口请求 每次获取一个分镜草图 轮询获取
|
||
const getTaskSketch = async (taskId: string) => {
|
||
if (isGeneratingSketch || taskSketch.length > 0) return;
|
||
|
||
setIsGeneratingSketch(true);
|
||
setTaskSketch([]);
|
||
|
||
const sketchData = selectedMockData.sketch;
|
||
const totalSketches = sketchData.length;
|
||
|
||
// 模拟分批获取分镜草图
|
||
for (let i = 0; i < totalSketches; i++) {
|
||
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.sketch)); // 10s
|
||
|
||
const newSketch = {
|
||
id: `sketch-${i}`,
|
||
url: sketchData[i].url,
|
||
script: sketchData[i].script,
|
||
bg_rgb: sketchData[i].bg_rgb,
|
||
status: 'done'
|
||
};
|
||
|
||
setTaskSketch(prev => {
|
||
if (prev.find(sketch => sketch.id === newSketch.id)) {
|
||
return prev;
|
||
}
|
||
return [...prev, newSketch];
|
||
});
|
||
setCurrentSketchIndex(i);
|
||
setSketchCount(i + 1);
|
||
}
|
||
|
||
// 等待最后一个动画完成再设置生成状态为false
|
||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||
setIsGeneratingSketch(false);
|
||
};
|
||
|
||
// 模拟接口请求 每次获取一个角色 轮询获取
|
||
const getTaskRole = async (taskId: string) => {
|
||
setTaskRoles([]);
|
||
const roleData = selectedMockData.roles;
|
||
const totalRoles = roleData.length;
|
||
|
||
for (let i = 0; i < totalRoles; i++) {
|
||
// 先更新loading文字显示当前正在生成的角色
|
||
setCurrentLoadingText(STEP_MESSAGES.newCharacter(i, totalRoles));
|
||
|
||
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.character)); // 2s 一个角色
|
||
|
||
// 添加角色到列表
|
||
setTaskRoles(prev => [...prev, roleData[i]]);
|
||
|
||
// 更新loading文字显示已完成的角色数量
|
||
setCurrentLoadingText(STEP_MESSAGES.newCharacter(i + 1, totalRoles));
|
||
|
||
// 如果不是最后一个角色,稍微延迟一下让用户看到更新
|
||
if (i < totalRoles - 1) {
|
||
await new Promise(resolve => setTimeout(resolve, 500));
|
||
}
|
||
}
|
||
};
|
||
|
||
// 模拟接口请求 获取背景音
|
||
const getTaskBackgroundAudio = async (taskId: string) => {
|
||
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.audio)); // 10s
|
||
};
|
||
|
||
// 模拟接口请求 获取最终成品
|
||
const getTaskFinalProduct = async (taskId: string) => {
|
||
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.final)); // 50s
|
||
};
|
||
|
||
// 模拟接口请求 每次获取一个分镜视频 轮询获取
|
||
const getTaskVideo = async (taskId: string) => {
|
||
setIsGeneratingVideo(true);
|
||
setTaskVideos([]);
|
||
|
||
const videoData = selectedMockData.video;
|
||
const totalVideos = videoData.length;
|
||
|
||
// 模拟分批获取分镜视频
|
||
for (let i = 0; i < totalVideos; i++) {
|
||
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.video)); // 60s
|
||
|
||
const newVideo = {
|
||
id: `video-${i}`,
|
||
url: videoData[i].url,
|
||
script: videoData[i].script,
|
||
status: 'done'
|
||
};
|
||
|
||
setTaskVideos(prev => {
|
||
if (prev.find(video => video.id === newVideo.id)) {
|
||
return prev;
|
||
}
|
||
return [...prev, newVideo];
|
||
});
|
||
setCurrentSketchIndex(i);
|
||
}
|
||
|
||
// 等待最后一个动画完成再设置生成状态为false
|
||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||
setIsGeneratingVideo(false);
|
||
};
|
||
|
||
// 更新加载文字
|
||
useEffect(() => {
|
||
if (isLoading) {
|
||
// 在初始加载阶段,保持当前loading文字不变
|
||
return;
|
||
}
|
||
|
||
const totalSketches = selectedMockData?.sketch?.length || 0;
|
||
const totalVideos = selectedMockData?.video?.length || 0;
|
||
const totalCharacters = selectedMockData?.roles?.length || 0;
|
||
|
||
if (currentStep === '1') {
|
||
if (isGeneratingSketch) {
|
||
setCurrentLoadingText(STEP_MESSAGES.sketch(sketchCount, totalSketches));
|
||
} else {
|
||
setCurrentLoadingText(STEP_MESSAGES.sketchComplete);
|
||
}
|
||
} else if (currentStep === '2') {
|
||
// 在角色生成阶段,loading文字已经在 getTaskRole 函数中直接管理
|
||
// 这里不需要额外设置,避免覆盖
|
||
if (taskRoles.length === totalCharacters) {
|
||
setCurrentLoadingText(STEP_MESSAGES.newCharacter(totalCharacters, totalCharacters));
|
||
}
|
||
} else if (currentStep === '3') {
|
||
if (isGeneratingVideo) {
|
||
setCurrentLoadingText(STEP_MESSAGES.video(taskVideos.length, totalVideos));
|
||
} else {
|
||
setCurrentLoadingText(STEP_MESSAGES.videoComplete);
|
||
}
|
||
} else if (currentStep === '4') {
|
||
setCurrentLoadingText(STEP_MESSAGES.audio);
|
||
} else if (currentStep === '5') {
|
||
setCurrentLoadingText(STEP_MESSAGES.final);
|
||
} else {
|
||
setCurrentLoadingText(STEP_MESSAGES.complete);
|
||
}
|
||
}, [isLoading, currentStep, isGeneratingSketch, sketchCount, isGeneratingVideo, taskVideos.length, taskSketch.length, taskRoles.length]);
|
||
|
||
// 工作流初始化函数
|
||
const initializeWorkflow = async () => {
|
||
try {
|
||
setIsLoading(true);
|
||
setCurrentLoadingText('正在初始化工作流...');
|
||
|
||
const taskId = (typeof window !== 'undefined' ? localStorage.getItem("taskId") : null) || "taskId-123";
|
||
|
||
// 首先加载数据
|
||
await loadMockData();
|
||
|
||
// 然后获取任务详情
|
||
setCurrentLoadingText('正在加载任务详情...');
|
||
const data = await getTaskDetail(taskId);
|
||
setTaskObject(data);
|
||
|
||
// 数据加载完成,进入工作流
|
||
setIsLoading(false);
|
||
setCurrentStep('1');
|
||
|
||
// 只在任务详情加载完成后获取分镜草图
|
||
await getTaskSketch(taskId);
|
||
|
||
// 修改 taskObject 下的 taskStatus 为 '2'
|
||
setTaskObject((prev: any) => ({
|
||
...prev,
|
||
taskStatus: '2'
|
||
}));
|
||
setCurrentStep('2');
|
||
|
||
// 获取分镜草图后,开始绘制角色
|
||
await getTaskRole(taskId);
|
||
|
||
// 修改 taskObject 下的 taskStatus 为 '3'
|
||
setTaskObject((prev: any) => ({
|
||
...prev,
|
||
taskStatus: '3'
|
||
}));
|
||
setCurrentStep('3');
|
||
|
||
// 获取绘制角色后,开始获取分镜视频
|
||
await getTaskVideo(taskId);
|
||
|
||
// 修改 taskObject 下的 taskStatus 为 '4'
|
||
setTaskObject((prev: any) => ({
|
||
...prev,
|
||
taskStatus: '4'
|
||
}));
|
||
setCurrentStep('4');
|
||
|
||
// 获取分镜视频后,开始获取背景音
|
||
await getTaskBackgroundAudio(taskId);
|
||
// 后期制作:抽卡中 对口型中 配音中 一致性处理中
|
||
setCurrentLoadingText(STEP_MESSAGES.postProduction('Selecting optimal frames'));
|
||
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.postProduction));
|
||
setCurrentLoadingText(STEP_MESSAGES.postProduction('Aligning lip sync'));
|
||
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.postProduction));
|
||
setCurrentLoadingText(STEP_MESSAGES.postProduction('Adding background audio'));
|
||
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.postProduction));
|
||
setCurrentLoadingText(STEP_MESSAGES.postProduction('Consistency processing'));
|
||
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.postProduction));
|
||
|
||
// 修改 taskObject 下的 taskStatus 为 '5'
|
||
setTaskObject((prev: any) => ({
|
||
...prev,
|
||
taskStatus: '5'
|
||
}));
|
||
setCurrentStep('5');
|
||
|
||
// 获取背景音后,开始获取最终成品
|
||
await getTaskFinalProduct(taskId);
|
||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||
|
||
// 修改 taskObject 下的 taskStatus 为 '6'
|
||
setTaskObject((prev: any) => ({
|
||
...prev,
|
||
taskStatus: '6'
|
||
}));
|
||
setCurrentStep('6');
|
||
|
||
} catch (error) {
|
||
console.error('工作流初始化失败:', error);
|
||
setDataLoadError('工作流初始化失败,请刷新页面重试');
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
// 初始化数据
|
||
useEffect(() => {
|
||
initializeWorkflow();
|
||
}, []);
|
||
|
||
return {
|
||
// 状态数据
|
||
taskObject,
|
||
taskSketch,
|
||
taskVideos,
|
||
sketchCount,
|
||
isLoading: isLoading || isLoadingData, // 合并loading状态
|
||
currentStep,
|
||
currentSketchIndex,
|
||
isGeneratingSketch,
|
||
isGeneratingVideo,
|
||
currentLoadingText,
|
||
totalSketchCount: selectedMockData?.sketch?.length || 0,
|
||
roles: selectedMockData?.roles || [],
|
||
music: selectedMockData?.music || {},
|
||
final: selectedMockData?.final || {},
|
||
dataLoadError,
|
||
// 操作方法
|
||
setCurrentSketchIndex,
|
||
retryLoadData,
|
||
};
|
||
}
|