forked from 77media/video-flow
修复接口请求
This commit is contained in:
parent
a5ea76a28e
commit
3019383c5e
@ -34,7 +34,7 @@ const request: AxiosInstance = axios.create({
|
||||
request.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
// 从 localStorage 获取 token
|
||||
const token = localStorage?.getItem('token') || 'mock-token';
|
||||
const token = localStorage?.getItem('token');
|
||||
if (token && config.headers) {
|
||||
(config.headers as AxiosHeaders).set('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
|
||||
@ -269,52 +269,29 @@ export const getRunningStreamData = async (data: {
|
||||
return post<ApiResponse<any>>("/movie/get_status", data);
|
||||
};
|
||||
|
||||
// 新增:获取项目任务列表接口(优化版本)
|
||||
/**
|
||||
* 获取项目任务列表接口
|
||||
* @param data - 请求参数
|
||||
* @param data.project_id - 项目ID
|
||||
* @returns Promise<ApiResponse<TaskItem[]>> - 任务列表响应
|
||||
*/
|
||||
export const getProjectTaskList = async (data: {
|
||||
project_id: string;
|
||||
}): Promise<ApiResponse<TaskItem[]>> => {
|
||||
const fullUrl = "https://77.smartvideo.py.qikongjian.com/task/get_project_task_list";
|
||||
|
||||
// 添加请求超时控制
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30秒超时
|
||||
const startTime = Date.now();
|
||||
console.log(`[API] 开始请求任务列表,项目ID: ${data.project_id}`);
|
||||
|
||||
try {
|
||||
console.log(`[API] 开始请求任务列表,项目ID: ${data.project_id}`);
|
||||
const startTime = Date.now();
|
||||
const result = await post<ApiResponse<TaskItem[]>>('/task/get_project_task_list', data);
|
||||
|
||||
const response = await fetch(fullUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${localStorage?.getItem('token') || 'mock-token'}`,
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
signal: controller.signal, // 添加超时控制
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
const endTime = Date.now();
|
||||
console.log(`[API] 请求完成,耗时: ${endTime - startTime}ms`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log(`[API] 数据解析完成,任务数量: ${Array.isArray(result.data) ? result.data.length : 0}`);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.error('[API] 请求超时 (30秒)');
|
||||
throw new Error('请求超时,请检查网络连接');
|
||||
}
|
||||
|
||||
console.error('[API] 获取任务列表失败:', error);
|
||||
throw error instanceof Error ? error : new Error('未知错误');
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ export default function Login() {
|
||||
|
||||
// 根据错误类型显示不同的错误消息
|
||||
setFormError(
|
||||
"Login failed, please check your credentials and try again."
|
||||
"Login failed, please try again."
|
||||
);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
|
||||
@ -1,273 +0,0 @@
|
||||
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
|
||||
};
|
||||
};
|
||||
@ -36,6 +36,7 @@ const nextConfig = {
|
||||
async rewrites() {
|
||||
// 使用环境变量,如果没有则使用默认值
|
||||
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || 'https://77.smartvideo.py.qikongjian.com'
|
||||
// const BASE_URL = 'http://192.168.120.5:8000'
|
||||
return [
|
||||
{
|
||||
source: '/api/proxy/:path*',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user