2025-08-13 22:14:19 +08:00

273 lines
7.7 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.

import { useState, useEffect, useCallback } from 'react';
import { detailScriptEpisodeNew, getScriptTitle, getRunningStreamData, StreamData } from '@/api/video_flow';
import { useSearchParams } from 'next/navigation';
import { ApiResponse } from '@/api/common';
import { SketchData, CharacterResponse, VideoData } from '@/api/DTO/movieEdit';
// 步骤映射
const STEP_MAP = {
'sketch': '1',
'character': '2',
'video': '3',
'music': '4',
'final_video': '6'
} as const;
type ApiStep = keyof typeof STEP_MAP;
interface TaskData {
sketch?: {
url: string;
script: string;
bg_rgb: string[];
}[];
roles?: {
name: string;
url: string;
sound: string;
soundDescription: string;
roleDescription: string;
}[];
videos?: {
url: string;
script: string;
audio: string;
}[];
music?: {
url: string;
script: string;
name: string;
duration: string;
totalDuration: string;
isLooped: boolean;
}[];
final?: {
url: string;
};
[key: string]: any; // 允许其他可能的字段
}
export const useApiData = () => {
const searchParams = useSearchParams();
const episodeId = searchParams.get('episodeId');
const [title, setTitle] = useState<string>('');
const [currentStep, setCurrentStep] = useState<string>('1');
const [currentLoadingText, setCurrentLoadingText] = useState<string>('');
const [needStreamData, setNeedStreamData] = useState<boolean>(false);
const [taskData, setTaskData] = useState<TaskData>({});
const [streamInterval, setStreamInterval] = useState<NodeJS.Timeout | null>(null);
// 处理流式数据
const handleStreamData = useCallback((streamData: ApiResponse<StreamData>) => {
const { category, message, data, status } = streamData.data;
// 更新加载文本
setCurrentLoadingText(message);
// 根据类别更新任务数据
setTaskData(prevData => {
const newData = { ...prevData };
switch (category) {
case 'sketch':
if (!newData.sketch) newData.sketch = [];
// 更新或追加分镜数据
const existingSketchIndex = newData.sketch.findIndex(s => s.url === data.url);
if (existingSketchIndex === -1) {
newData.sketch.push(data);
} else {
newData.sketch[existingSketchIndex] = data;
}
break;
case 'character':
if (!newData.roles) newData.roles = [];
// 更新或追加角色数据
const existingRoleIndex = newData.roles.findIndex(r => r.name === data.name);
if (existingRoleIndex === -1) {
newData.roles.push(data);
} else {
newData.roles[existingRoleIndex] = data;
}
break;
case 'video':
if (!newData.videos) newData.videos = [];
// 更新或追加视频数据
const existingVideoIndex = newData.videos.findIndex(v => v.url === data.url);
if (existingVideoIndex === -1) {
newData.videos.push(data);
} else {
newData.videos[existingVideoIndex] = data;
}
break;
case 'music':
if (!newData.music) newData.music = [];
newData.music = [data];
break;
case 'final_video':
newData.final = data;
break;
}
return newData;
});
// 如果状态为 completed停止获取流式数据
if (status === 'completed' || streamData.data.all_completed) {
setNeedStreamData(false);
}
}, []);
// 获取流式数据
const fetchStreamData = useCallback(async () => {
if (!episodeId || !needStreamData) return;
try {
const streamData = await getRunningStreamData({ project_id: episodeId });
handleStreamData(streamData);
} catch (error) {
console.error('获取流式数据失败:', error);
}
}, [episodeId, needStreamData, handleStreamData]);
// 启动流式数据轮询
useEffect(() => {
if (needStreamData && !streamInterval) {
const interval = setInterval(fetchStreamData, 10000); // 修改为10秒
setStreamInterval(interval);
} else if (!needStreamData && streamInterval) {
clearInterval(streamInterval);
setStreamInterval(null);
}
return () => {
if (streamInterval) {
clearInterval(streamInterval);
}
};
}, [needStreamData, fetchStreamData, streamInterval]);
// 获取标题的轮询函数
const pollTitle = useCallback(async () => {
if (!episodeId) return;
try {
const response = await getScriptTitle({ project_id: episodeId });
if (response.successful && response.data) {
setTitle(response.data.name);
return true;
}
} catch (error) {
console.error('获取标题失败:', error);
}
return false;
}, [episodeId]);
// 获取剧集详情
const fetchEpisodeDetail = useCallback(async () => {
if (!episodeId) return;
try {
const response = await detailScriptEpisodeNew({ project_id: episodeId });
if (response.successful) {
const { name, status, step, last_message, data } = response.data;
// 设置标题
if (name) {
setTitle(name);
}
// 设置步骤
if (step && STEP_MAP[step as ApiStep]) {
setCurrentStep(STEP_MAP[step as ApiStep]);
}
// 设置加载文本
setCurrentLoadingText(last_message || '');
// 设置是否需要流式数据
setNeedStreamData(status === 'running');
// 设置任务数据
if (data) {
// 将 ProjectContentData 转换为 TaskData
const convertedData: TaskData = {
sketch: data.sketch?.data?.map((item: SketchData) => ({
url: item.image_path,
script: item.prompt,
bg_rgb: ['255', '255', '255'] // 默认白色背景
})) || [],
roles: data.character?.data?.map((item: CharacterResponse) => ({
name: item.character_name,
url: item.image_path,
sound: '',
soundDescription: '',
roleDescription: item.character_description
})) || [],
videos: data.video?.data?.map((item: VideoData) => ({
url: item.urls?.[0] || '',
script: item.description || '',
audio: '' // 音频URL可能需要从其他地方获取
})) || [],
music: data.music?.data?.map((item: any) => ({
url: item.url || '',
script: item.description || '',
name: item.name || '',
duration: item.duration || '',
totalDuration: item.total_duration || '',
isLooped: item.is_looped || false
})) || [],
final: data.final_video ? {
url: data.final_video.video || ''
} : undefined
};
setTaskData(convertedData);
}
return true;
}
} catch (error) {
console.error('获取剧集详情失败:', error);
}
return false;
}, [episodeId]);
// 初始化数据
useEffect(() => {
let titleInterval: NodeJS.Timeout;
const initData = async () => {
const detailSuccess = await fetchEpisodeDetail();
// 如果详情接口没有返回标题,开始轮询标题
if (detailSuccess && !title) {
titleInterval = setInterval(async () => {
const success = await pollTitle();
if (success) {
clearInterval(titleInterval);
}
}, 3000);
}
};
initData();
return () => {
if (titleInterval) {
clearInterval(titleInterval);
}
};
}, [episodeId, fetchEpisodeDetail, pollTitle, title]);
return {
title,
currentStep,
currentLoadingText,
needStreamData,
taskData
};
};