Merge branch 'dev' into prod

This commit is contained in:
北枳 2025-10-10 21:30:23 +08:00
commit 9541eb8a8d
10 changed files with 71 additions and 850 deletions

View File

@ -1,16 +1,16 @@
# 测试
NEXT_PUBLIC_JAVA_URL = https://auth.test.movieflow.ai
NEXT_PUBLIC_BASE_URL = https://77.smartvideo.py.qikongjian.com
NEXT_PUBLIC_CUT_URL = https://77.smartcut.py.qikongjian.com
NEXT_PUBLIC_GOOGLE_REDIRECT_URI=https://www.movieflow.net/api/auth/google/callback
NEXT_PUBLIC_CUT_URL_TO = https://smartcut.huiying.video
# NEXT_PUBLIC_JAVA_URL = https://auth.test.movieflow.ai
# NEXT_PUBLIC_BASE_URL = https://77.smartvideo.py.qikongjian.com
# NEXT_PUBLIC_CUT_URL = https://77.smartcut.py.qikongjian.com
# NEXT_PUBLIC_GOOGLE_REDIRECT_URI=https://www.movieflow.net/api/auth/google/callback
# NEXT_PUBLIC_CUT_URL_TO = https://smartcut.huiying.video
# 生产
# NEXT_PUBLIC_JAVA_URL = https://auth.movieflow.ai
# NEXT_PUBLIC_BASE_URL = https://api.video.movieflow.ai
# NEXT_PUBLIC_CUT_URL = https://smartcut.api.movieflow.ai
# NEXT_PUBLIC_GOOGLE_REDIRECT_URI=https://www.movieflow.ai/api/auth/google/callback
# NEXT_PUBLIC_CUT_URL_TO = https://smartcut.movieflow.ai
NEXT_PUBLIC_JAVA_URL = https://auth.movieflow.ai
NEXT_PUBLIC_BASE_URL = https://api.video.movieflow.ai
NEXT_PUBLIC_CUT_URL = https://smartcut.api.movieflow.ai
NEXT_PUBLIC_GOOGLE_REDIRECT_URI=https://www.movieflow.ai/api/auth/google/callback
NEXT_PUBLIC_CUT_URL_TO = https://smartcut.movieflow.ai
# 通用
# 当前域名配置
NEXT_PUBLIC_FRONTEND_URL = https://www.movieflow.ai

View File

@ -1,6 +1,5 @@
import { ApiResponse } from './common';
import { showH5QueueNotification } from '../components/QueueBox/H5QueueNotication';
import { notification } from 'antd';
import { showQueueNotification } from '../components/QueueBox/QueueNotication';
/** 队列状态枚举 */
export enum QueueStatus {
@ -75,7 +74,6 @@ export async function withQueuePolling<T>(
const cancel = () => {
isCancelled = true;
try { closeModal?.(); } catch {}
notification.destroy(); // 兼容旧弹层
onCancel?.();
cancelTokens.delete(pollId);
};
@ -104,14 +102,14 @@ export async function withQueuePolling<T>(
position !== undefined && waiting !== undefined) {
// 打开或更新 H5 弹窗(仅允许 Cancel 关闭Refresh 触发刷新)
try { closeModal?.(); } catch {}
closeModal = showH5QueueNotification(
closeModal = showQueueNotification(
position,
waiting,
status,
cancel,
async () => {
// 触发一次立刻刷新:重置 attempts 的等待,直接递归调用 poll()
// 不关闭弹窗,由 showH5QueueNotification 保持打开
// 不关闭弹窗,由 showQueueNotification 保持打开
attempts = Math.max(0, attempts - 1);
}
);
@ -123,7 +121,6 @@ export async function withQueuePolling<T>(
// 检查是否达到最大尝试次数
if (attempts >= maxAttempts) {
notification.destroy(); // 关闭通知
throw new Error('Exceeded the maximum polling limit');
}
@ -135,17 +132,14 @@ export async function withQueuePolling<T>(
// 如果状态为ready结束轮询
if (response.code !== 202 && response.data) {
try { closeModal?.(); } catch {}
notification.destroy(); // 兼容旧弹层
onSuccess?.(response.data);
return response;
}
try { closeModal?.(); } catch {}
notification.destroy(); // 兼容旧弹层
return response;
} catch (error) {
try { closeModal?.(); } catch {}
notification.destroy(); // 兼容旧弹层
if (error instanceof Error) {
onError?.(error);
}

View File

@ -14,7 +14,7 @@ interface H5QueueNotificationProps {
onClose?: () => void;
}
function H5QueueNotificationModal(props: H5QueueNotificationProps) {
function QueueNotificationModal(props: H5QueueNotificationProps) {
const { position, estimatedMinutes, status, onCancel, onClose } = props;
const containerRef = useRef<HTMLDivElement | null>(null);
@ -39,11 +39,11 @@ function H5QueueNotificationModal(props: H5QueueNotificationProps) {
return (
<div
ref={containerRef}
data-alt="h5-queue-overlay"
data-alt="queue-overlay"
className="fixed inset-0 z-[1000] flex items-center justify-center bg-black/60"
>
<div
data-alt="h5-queue-modal"
data-alt="queue-modal"
className="relative w-11/12 max-w-sm rounded-2xl bg-white/5 border border-white/10 backdrop-blur-xl shadow-2xl p-6 text-white"
>
{/* 去除右上角关闭按钮,避免除取消以外的关闭路径 */}
@ -116,7 +116,7 @@ function H5QueueNotificationModal(props: H5QueueNotificationProps) {
* @param {() => void} onCancel - Callback when user confirms cancel.
* @returns {() => void} - Close function to dismiss the modal programmatically.
*/
export function showH5QueueNotification(
export function showQueueNotification(
position: number,
estimatedMinutes: number,
status: QueueStatus,
@ -128,7 +128,7 @@ export function showH5QueueNotification(
}
const mount = document.createElement('div');
mount.setAttribute('data-alt', 'h5-queue-root');
mount.setAttribute('data-alt', 'queue-root');
document.body.appendChild(mount);
let root: Root | null = null;
@ -151,7 +151,7 @@ export function showH5QueueNotification(
};
root.render(
<H5QueueNotificationModal
<QueueNotificationModal
position={position}
estimatedMinutes={estimatedMinutes}
status={status}

View File

@ -1,237 +0,0 @@
import { notification } from 'antd';
const darkGlassStyle = {
background: 'rgba(30, 32, 40, 0.95)',
backdropFilter: 'blur(10px)',
WebkitBackdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.08)',
borderRadius: '8px',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.4)',
padding: '12px 16px',
};
/** AI导演工作室容器样式 */
const studioContainerStyle = {
position: 'relative' as const,
width: '100%',
height: '100px',
marginBottom: '16px',
background: 'rgba(26, 27, 30, 0.6)',
borderRadius: '8px',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
};
/** AI导演组件 */
const AIDirector = () => (
<div className="ai-director">
<svg width="80" height="80" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* AI导演的圆形头部 */}
<circle cx="50" cy="40" r="25" fill="#F6B266"/>
{/* 眼睛 */}
<circle cx="40" cy="35" r="5" fill="#2A2B2E"/>
<circle cx="60" cy="35" r="5" fill="#2A2B2E"/>
{/* 笑容 */}
<path d="M40 45 Q50 55 60 45" stroke="#2A2B2E" strokeWidth="3" strokeLinecap="round"/>
{/* 导演帽 */}
<path d="M25 30 H75 V25 H25" fill="#2A2B2E"/>
{/* 身体 */}
<rect x="35" y="65" width="30" height="25" fill="#F6B266"/>
{/* 手臂 - 动画中会移动 */}
<rect className="director-arm" x="25" y="70" width="15" height="5" fill="#F6B266"/>
<rect className="director-arm" x="60" y="70" width="15" height="5" fill="#F6B266"/>
</svg>
</div>
);
/** 工作进度条组件 */
const ProgressTimeline = () => (
<div className="progress-timeline" style={{
position: 'absolute',
bottom: '10px',
left: '20px',
right: '20px',
height: '4px',
background: 'rgba(255, 255, 255, 0.1)',
borderRadius: '2px',
}}>
<div className="progress-indicator" style={{
width: '30%',
height: '100%',
background: '#F6B266',
borderRadius: '2px',
animation: 'progress 2s ease-in-out infinite',
}}/>
</div>
);
/** 工作台元素组件 */
const Workstation = () => (
<div className="workstation" style={{
position: 'absolute',
bottom: '20px',
width: '100%',
display: 'flex',
justifyContent: 'space-around',
}}>
{/* 小型场景图标,会在动画中浮动 */}
{[...Array(3)].map((_, i) => (
<div key={i} className={`scene-icon scene-${i}`} style={{
width: '20px',
height: '20px',
background: 'rgba(246, 178, 102, 0.3)',
borderRadius: '4px',
animation: `float ${1 + i * 0.5}s ease-in-out infinite alternate`,
}}/>
))}
</div>
);
/**
*
* @param position -
* @param estimatedMinutes -
*/
export const showQueueNotification = (
position: number,
estimatedMinutes: number,
status: string,
onCancel: () => void
) => {
const notificationKey = 'queueNotification';
// 创建或更新通知内容
const notificationContent = (
<div data-alt="queue-notification" style={{ minWidth: '320px' }}>
{/* AI导演工作室场景 */}
<div style={studioContainerStyle}>
<AIDirector />
<Workstation />
<ProgressTimeline />
</div>
{/* 队列信息 */}
<div style={{
fontSize: '13px',
color: 'rgba(255, 255, 255, 0.9)',
marginBottom: '12px',
display: 'flex',
alignItems: 'center',
background: 'rgba(246, 178, 102, 0.1)',
padding: '8px 12px',
borderRadius: '6px',
}}>
<span style={{ marginRight: '8px' }}>🎬</span>
{status === 'process' ? `Your work is being produced. Please wait until it is completed before creating a new work.` : `Your work is waiting for production at the ${position} position`}
</div>
{/* 预计等待时间 */}
<div style={{
fontSize: '12px',
color: 'rgba(255, 255, 255, 0.65)',
marginBottom: '12px',
}}>
{status !== 'process' && `Estimated waiting time: about ${estimatedMinutes} minutes`}
</div>
{/* 取消按钮 */}
<button
onClick={() => {
onCancel?.();
notification.destroy(notificationKey);
}}
style={{
color: 'rgb(250 173 20 / 90%)',
background: 'transparent',
border: 'none',
cursor: 'pointer',
padding: 0,
fontSize: '12px',
fontWeight: 500,
textDecoration: 'underline',
textUnderlineOffset: '2px',
textDecorationColor: 'rgb(250 173 20 / 60%)',
transition: 'all 0.2s ease',
}}
data-alt="cancel-queue-button"
>
Cancel queue
</button>
</div>
);
// 打开或更新通知
notification.open({
key: notificationKey,
message: null,
description: notificationContent,
duration: 0,
placement: 'topRight',
style: {
...darkGlassStyle,
border: '1px solid rgba(246, 178, 102, 0.2)',
},
className: 'director-studio-notification',
closeIcon: null,
});
};
// 添加必要的CSS动画
const styles = `
.ai-director {
animation: bounce 2s ease-in-out infinite;
}
.director-arm {
transform-origin: center;
animation: wave 1s ease-in-out infinite alternate;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
@keyframes wave {
0% { transform: rotate(-5deg); }
100% { transform: rotate(5deg); }
}
@keyframes float {
0% { transform: translateY(0); }
100% { transform: translateY(-10px); }
}
@keyframes progress {
0% { width: 0%; }
50% { width: 60%; }
100% { width: 30%; }
}
.director-studio-notification {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
.scene-0 { animation-delay: 0s; }
.scene-1 { animation-delay: 0.2s; }
.scene-2 { animation-delay: 0.4s; }
`;
// 将样式注入到页面
if (typeof document !== 'undefined') {
const styleSheet = document.createElement('style');
styleSheet.textContent = styles;
document.head.appendChild(styleSheet);
}
// 配置通知
notification.config({
maxCount: 3,
});

View File

@ -17,30 +17,12 @@ import { SaveEditUseCase } from "@/app/service/usecase/SaveEditUseCase";
import { useSearchParams } from "next/navigation";
import SmartChatBox from "@/components/SmartChatBox/SmartChatBox";
import { Drawer, Tooltip, notification } from 'antd';
import { exportVideoWithRetry } from '@/utils/export-service';
import { EditPoint as EditPointType } from './work-flow/video-edit/types';
import { useDeviceType } from '@/hooks/useDeviceType';
import { H5ProgressToastProvider, useH5ProgressToast } from '@/components/ui/h5-progress-toast';
const WorkFlow = React.memo(function WorkFlow() {
const { isMobile, isTablet, isDesktop } = useDeviceType();
// 通过全局事件桥接 H5ProgressToastProvider 在本组件 JSX 中,逻辑层无法直接使用 hook
const emitToastShow = useCallback((params: { title?: string; progress?: number }) => {
window.dispatchEvent(new CustomEvent('h5Toast:show', { detail: params }));
}, []);
const emitToastUpdate = useCallback((params: { title?: string; progress?: number }) => {
window.dispatchEvent(new CustomEvent('h5Toast:update', { detail: params }));
}, []);
const emitToastHide = useCallback(() => {
window.dispatchEvent(new CustomEvent('h5Toast:hide'));
}, []);
useEffect(() => {
console.log("init-WorkFlow");
return () => {
console.log("unmount-WorkFlow");
// 不在卸载时强制隐藏,避免严格模式下二次卸载导致刚显示就被关闭
};
}, [emitToastHide]);
const containerRef = useRef<HTMLDivElement>(null);
const [isEditModalOpen, setIsEditModalOpen] = React.useState(false);
const [activeEditTab, setActiveEditTab] = React.useState('1');
@ -53,22 +35,6 @@ const WorkFlow = React.memo(function WorkFlow() {
const [selectedView, setSelectedView] = React.useState<'final' | 'video' | null>(null);
const [aiEditingResult, setAiEditingResult] = React.useState<any>(null);
// const aiEditingButtonRef = useRef<{ handleAIEditing: () => Promise<void> }>(null);
const [editingStatus, setEditingStatus] = React.useState<'initial' | 'idle' | 'success' | 'error'>('initial');
// const [iframeAiEditingKey, setIframeAiEditingKey] = React.useState<string>(`iframe-ai-editing-${Date.now()}`);
const [isEditingInProgress, setIsEditingInProgress] = React.useState(false);
const isEditingInProgressRef = useRef(false);
// 导出进度状态
const [exportProgress, setExportProgress] = React.useState<{
status: 'processing' | 'completed' | 'failed';
percentage: number;
message: string;
stage?: string;
taskId?: string;
} | null>(null);
const editingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const editingProgressIntervalRef = useRef<NodeJS.Timeout | null>(null);
const editingProgressStartRef = useRef<number>(0);
const searchParams = useSearchParams();
const episodeId = searchParams.get('episodeId') || '';
@ -76,133 +42,6 @@ const WorkFlow = React.memo(function WorkFlow() {
const userId = JSON.parse(localStorage.getItem("currentUser") || '{}').id || NaN;
SaveEditUseCase.setProjectId(episodeId);
const [isHandleEdit, setIsHandleEdit] = React.useState(false);
// 使用 ref 存储 handleTestExport 避免循环依赖
const handleTestExportRef = useRef<(() => Promise<any>) | null>(null);
// 导出进度回调处理
const handleExportProgress = useCallback((progressData: {
status: 'processing' | 'completed' | 'failed';
percentage: number;
message: string;
stage?: string;
taskId?: string;
}) => {
console.log('📊 导出进度更新:', progressData);
setExportProgress(progressData);
// 根据状态显示不同的通知 - 已注释
/*
if (progressData.status === 'processing') {
notification.info({
message: '导出进度',
description: `${progressData.message} (${progressData.percentage}%)`,
placement: 'topRight',
duration: 2,
key: 'export-progress'
});
} else if (progressData.status === 'completed') {
notification.success({
message: '导出成功',
description: progressData.message,
placement: 'topRight',
duration: 5,
key: 'export-progress'
});
} else if (progressData.status === 'failed') {
notification.error({
message: '导出失败',
description: progressData.message,
placement: 'topRight',
duration: 8,
key: 'export-progress'
});
}
*/
}, []);
// 处理编辑计划生成完成的回调
const handleEditPlanGenerated = useCallback(() => {
console.log('✨ 编辑计划生成完成开始AI剪辑');
setIsHandleEdit(true);
setEditingStatus('idle');
// setIsEditingInProgress(true); // 已移除该状态变量
isEditingInProgressRef.current = true;
// 改为调用测试剪辑计划导出按钮方法
// aiEditingButtonRef.current?.handleAIEditing();
// 使用 ref 调用避免循环依赖
setTimeout(() => {
handleTestExportRef.current?.();
}, 0);
const title = isMobile ? 'editing...' : 'Performing intelligent editing...';
// 显示进度提示并启动超时定时器
// emitToastShow({ title: title, progress: 0 });
// 启动自动推进到 90% 的进度8分钟
if (editingProgressIntervalRef.current) clearInterval(editingProgressIntervalRef.current);
editingProgressStartRef.current = Date.now();
const totalMs = 8 * 60 * 1000;
// editingProgressIntervalRef.current = setInterval(() => {
// const elapsed = Date.now() - editingProgressStartRef.current;
// const pct = Math.min(90, Math.max(0, Math.floor((elapsed / totalMs) * 90)));
// emitToastUpdate({ progress: pct });
// }, 250);
if (editingTimeoutRef.current) clearTimeout(editingTimeoutRef.current);
editingTimeoutRef.current = setTimeout(() => {
console.log('❌ Editing timeout - retrying...');
localStorage.removeItem(`isLoaded_plan_${episodeId}`);
if (editingProgressIntervalRef.current) {
clearInterval(editingProgressIntervalRef.current);
editingProgressIntervalRef.current = null;
}
// emitToastHide();
setTimeout(() => {
// emitToastShow({ title: 'Retry intelligent editing...', progress: 0 });
// 重试阶段自动推进5分钟到 90%
if (editingProgressIntervalRef.current) clearInterval(editingProgressIntervalRef.current);
editingProgressStartRef.current = Date.now();
const retryTotalMs = 5 * 60 * 1000;
editingProgressIntervalRef.current = setInterval(() => {
const elapsed = Date.now() - editingProgressStartRef.current;
const pct = Math.min(90, Math.max(0, Math.floor((elapsed / retryTotalMs) * 90)));
// emitToastUpdate({ progress: pct });
}, 250);
if (editingTimeoutRef.current) clearTimeout(editingTimeoutRef.current);
editingTimeoutRef.current = setTimeout(() => {
console.log('Editing retry failed');
localStorage.removeItem(`isLoaded_plan_${episodeId}`);
setTimeout(() => {
setEditingStatus('error');
setIsEditingInProgress(false);
isEditingInProgressRef.current = false;
if (editingProgressIntervalRef.current) {
clearInterval(editingProgressIntervalRef.current);
editingProgressIntervalRef.current = null;
}
// emitToastHide();
}, 5000);
}, 5 * 60 * 1000);
}, 200);
}, 8 * 60 * 1000);
}, [episodeId, emitToastHide, emitToastShow, emitToastUpdate]); // 移除 isEditingInProgress 依赖
/** 处理导出失败 */
const handleExportFailed = useCallback(() => {
console.log('Export failed, setting error status');
setEditingStatus('error');
// setIsEditingInProgress(false); // 已移除该状态变量
isEditingInProgressRef.current = false;
if (editingTimeoutRef.current) {
clearTimeout(editingTimeoutRef.current);
editingTimeoutRef.current = null;
}
if (editingProgressIntervalRef.current) {
clearInterval(editingProgressIntervalRef.current);
editingProgressIntervalRef.current = null;
}
// emitToastHide();
}, [emitToastHide]);
// 使用自定义 hooks 管理状态
const {
@ -222,12 +61,8 @@ const WorkFlow = React.memo(function WorkFlow() {
showGotoCutButton,
generateEditPlan,
handleRetryVideo,
isShowAutoEditing,
aspectRatio
} = useWorkflowData({
onEditPlanGenerated: handleEditPlanGenerated,
editingStatus: editingStatus,
onExportFailed: handleExportFailed
});
const {
@ -251,63 +86,11 @@ const WorkFlow = React.memo(function WorkFlow() {
}
}, [taskObject?.final?.url]);
// 监听粗剪是否完成
useEffect(() => {
console.log('🎬 final video useEffect triggered:', {
finalUrl: taskObject.final.url,
isHandleEdit
});
if (taskObject.final.url && isHandleEdit) {
console.log('🎉 显示编辑完成通知');
// 完成:推进到 100 并清理超时计时器
if (editingTimeoutRef.current) {
clearTimeout(editingTimeoutRef.current);
editingTimeoutRef.current = null;
}
if (editingProgressIntervalRef.current) {
clearInterval(editingProgressIntervalRef.current);
editingProgressIntervalRef.current = null;
}
// emitToastUpdate({ title: 'Editing successful', progress: 100 });
console.log('Editing successful');
localStorage.setItem(`isLoaded_plan_${episodeId}`, 'true');
setEditingStatus('success');
setIsEditingInProgress(false);
isEditingInProgressRef.current = false;
// setTimeout(() => {
// emitToastHide();
// }, 3000);
}
}, [taskObject.final, isHandleEdit, episodeId, emitToastHide, emitToastUpdate]);
const handleEditModalOpen = useCallback((tab: string) => {
setActiveEditTab(tab);
setIsEditModalOpen(true);
}, []);
// AI剪辑回调函数
const handleAIEditingComplete = useCallback((finalVideoUrl: string) => {
console.log('🎉 AI剪辑完成最终视频URL:', finalVideoUrl);
// 更新任务对象的最终视频状态
setAnyAttribute('final', {
url: finalVideoUrl,
note: 'ai_edited'
});
// 切换到最终视频阶段
setAnyAttribute('currentStage', 'final_video');
// setAiEditingInProgress(false); // 已移除该状态变量
}, [setAnyAttribute]);
const handleAIEditingError = useCallback((error: string) => {
console.error('❌ AI剪辑失败:', error);
// 这里可以显示错误提示
// setAiEditingInProgress(false); // 已移除该状态变量
}, []);
// 视频编辑描述提交处理函数
const handleVideoEditDescriptionSubmit = useCallback((editPoint: EditPointType, description: string) => {
console.log('🎬 视频编辑描述提交:', { editPoint, description });
@ -339,77 +122,6 @@ Please process this video editing request.`;
});
}, [currentSketchIndex, isSmartChatBoxOpen]);
// 测试导出接口的处理函数(使用封装的导出服务)
const handleTestExport = useCallback(async () => {
console.log('🧪 开始测试导出接口...');
console.log('📊 当前taskObject状态:', {
currentStage: taskObject.currentStage,
videosCount: taskObject.videos?.data?.length || 0,
completedVideos: taskObject.videos?.data?.filter(v => v.video_status === 1).length || 0
});
try {
// 使用封装的导出服务,传递进度回调
const result = await exportVideoWithRetry(episodeId, taskObject, handleExportProgress);
console.log('🎉 导出服务完成,结果:', result);
return result;
} catch (error) {
console.error('❌ 导出服务失败:', error);
throw error;
}
}, [episodeId, taskObject, handleExportProgress]);
// 将 handleTestExport 赋值给 ref
React.useEffect(() => {
handleTestExportRef.current = handleTestExport;
}, [handleTestExport]);
// iframe智能剪辑回调函数 - 已注释
/*
const handleIframeAIEditingComplete = useCallback((result: any) => {
console.log('🎉 iframe AI剪辑完成结果:', result);
// 保存剪辑结果
setAiEditingResult(result);
// 更新任务对象的最终视频状态
setAnyAttribute('final', {
url: result.videoUrl,
note: 'ai_edited_iframe'
});
// 切换到最终视频阶段
setAnyAttribute('currentStage', 'final_video');
// setAiEditingInProgress(false); // 已移除该状态变量
}, [setAnyAttribute]);
*/
/*
const handleIframeAIEditingError = useCallback((error: string) => {
console.error('❌ iframe AI剪辑失败:', error);
// setAiEditingInProgress(false); // 已移除该状态变量
}, []);
*/
/*
const handleIframeAIEditingProgress = useCallback((progress: number, message: string) => {
console.log(`📊 AI剪辑进度: ${progress}% - ${message}`);
setAiEditingInProgress(true);
// 收到显式进度时停止自动推进,防止倒退
if (editingProgressIntervalRef.current) {
clearInterval(editingProgressIntervalRef.current);
editingProgressIntervalRef.current = null;
}
emitToastUpdate({ title: message, progress });
}, [emitToastUpdate]);
*/
return (
<H5ProgressToastProvider>
<H5ToastBridge />
@ -441,7 +153,6 @@ Please process this video editing request.`;
currentLoadingText={currentLoadingText}
roles={taskObject.roles.data}
isPauseWorkFlow={isPauseWorkFlow}
showGotoCutButton={showGotoCutButton || editingStatus !== 'idle'}
onGotoCut={generateEditPlan}
setIsPauseWorkFlow={setIsPauseWorkFlow}
/>
@ -478,7 +189,7 @@ Please process this video editing request.`;
setPreviewVideoUrl(url);
setPreviewVideoId(id);
}}
showGotoCutButton={showGotoCutButton || editingStatus !== 'idle'}
showGotoCutButton={showGotoCutButton}
onGotoCut={generateEditPlan}
isSmartChatBoxOpen={isSmartChatBoxOpen}
onRetryVideo={(video_id) => handleRetryVideo(video_id)}
@ -535,8 +246,6 @@ Please process this video editing request.`;
setPreviewVideoUrl(url);
setPreviewVideoId(id);
}}
showGotoCutButton={showGotoCutButton || editingStatus !== 'idle'}
onGotoCut={generateEditPlan}
isSmartChatBoxOpen={isSmartChatBoxOpen}
onRetryVideo={(video_id) => handleRetryVideo(video_id)}
onSelectView={(view) => setSelectedView(view)}
@ -585,68 +294,6 @@ Please process this video editing request.`;
</div>
</div>
{/* AI剪辑按钮 - 已注释不加载iframe */}
{/*
{
isShowAutoEditing && (
<div className="fixed right-[2rem] top-[8rem] z-[49]">
<Tooltip title="AI智能剪辑" placement="left">
<AIEditingIframeButton
key={iframeAiEditingKey}
ref={aiEditingButtonRef}
projectId={episodeId}
token={localStorage.getItem("token") || ""}
userId={userId.toString()}
size="md"
onComplete={handleIframeAIEditingComplete}
onError={handleIframeAIEditingError}
onProgress={handleIframeAIEditingProgress}
autoStart={process.env.NODE_ENV !== 'development'}
/>
</Tooltip>
</div>
)
}
*/}
{/* 导出进度显示 - 已注释 */}
{/*
{exportProgress && exportProgress.status === 'processing' && (
<div className="fixed right-[1rem] bottom-[20rem] z-[49]">
<div className="backdrop-blur-lg bg-black/30 border border-white/20 rounded-lg p-4 max-w-xs">
<div className="text-white text-sm mb-2">
: {exportProgress.percentage}%
</div>
<div className="w-full bg-gray-700 rounded-full h-2 mb-2">
<div
className="bg-blue-500 h-2 rounded-full transition-all duration-300"
style={{ width: `${exportProgress.percentage}%` }}
/>
</div>
<div className="text-gray-300 text-xs">
{exportProgress.message}
{exportProgress.stage && ` (${exportProgress.stage})`}
</div>
</div>
</div>
)}
*/}
{/* 测试导出接口按钮 - 隐藏显示(仍可通过逻辑调用) */}
<div
className="fixed right-[1rem] bottom-[16rem] z-[49]"
style={{ display: 'none' }}
>
<Tooltip title="测试剪辑计划导出接口" placement="left">
<GlassIconButton
icon={TestTube}
size='md'
onClick={handleTestExport}
className="backdrop-blur-lg"
/>
</Tooltip>
</div>
{/* 智能对话按钮 */}
<div
className={`fixed right-[1rem] z-[49] ${isMobile ? 'bottom-[9rem]' : 'bottom-[10rem]'}`}

View File

@ -35,10 +35,6 @@ interface H5MediaViewerProps {
onOpenChat?: () => void;
/** 设置聊天预览视频 */
setVideoPreview?: (url: string, id: string) => void;
/** 显示跳转至剪辑平台按钮 */
showGotoCutButton?: boolean;
/** 跳转至剪辑平台 */
onGotoCut?: () => void;
/** 智能对话是否打开H5可忽略布局调整仅占位 */
isSmartChatBoxOpen?: boolean;
/** 失败重试生成视频 */
@ -210,8 +206,6 @@ export function H5MediaViewer({
setCurrentSketchIndex,
onOpenChat,
setVideoPreview,
showGotoCutButton,
onGotoCut,
isSmartChatBoxOpen,
onRetryVideo,
onSelectView,
@ -369,9 +363,6 @@ export function H5MediaViewer({
<video
ref={(el) => (videoRefs.current[idx] = el)}
className="w-full h-full object-contain [transform:translateZ(0)] [backface-visibility:hidden] [will-change:transform] bg-black cursor-pointer"
style={{
maxHeight: '100%',
}}
src={url}
preload="metadata"
playsInline
@ -453,9 +444,7 @@ export function H5MediaViewer({
return (
<div key={`h5-image-${idx}`} data-alt="image-slide" className="relative w-full h-full flex justify-center">
{showImage ? (
<img src={url} alt="scene" className="w-full h-full object-contain" style={{
maxHeight: '100%',
}} />
<img src={url} alt="scene" className="w-full h-full object-contain" />
) : (
<div className="w-full aspect-auto min-h-[200px] flex items-center justify-center bg-black/10 relative" data-alt="image-status" style={{
height: imageWrapperHeight,
@ -632,7 +621,6 @@ export function H5MediaViewer({
if (hasFinalVideo) {
all.push(taskObject.final.url);
}
console.log('h5-media-viewer:all', all);
await downloadAllVideos(all);
},
});

View File

@ -27,8 +27,16 @@ const H5ProgressBar: React.FC<H5ProgressBarProps> = ({
currentLoadingText,
className
}) => {
/** Check if task has failed */
const isFailed = taskObject.status === 'FAILED'
/** Calculate current stage based on taskObject state */
const currentStage = useMemo(() => {
/** If task failed, show at final stage */
if (isFailed) {
return 3 /** Final stage to show failure */
}
/** Check if roles & scenes are completed */
const rolesCompleted =
taskObject.roles?.total_count > 0 &&
@ -71,6 +79,7 @@ const H5ProgressBar: React.FC<H5ProgressBarProps> = ({
return 0 /** Default to script stage */
}, [
isFailed,
taskObject.currentStage,
taskObject.roles?.data,
taskObject.roles?.total_count,
@ -134,6 +143,10 @@ const H5ProgressBar: React.FC<H5ProgressBarProps> = ({
return Math.min(Math.round((videosCount / videosTotal) * 100), 95)
case 3: /** Final video stage */
/** If task failed, show 60% progress */
if (isFailed) {
return 60
}
/** If final.url exists, show 100% */
if (taskObject.final?.url) {
return 100
@ -164,9 +177,9 @@ const H5ProgressBar: React.FC<H5ProgressBarProps> = ({
segmentProgress
}
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
currentStage,
isFailed,
scriptData,
taskObject.roles?.data,
taskObject.roles?.total_count,
@ -185,6 +198,8 @@ const H5ProgressBar: React.FC<H5ProgressBarProps> = ({
<div data-alt="progress-segments" className="flex items-center gap-1 relative">
{segments.map(({ stage, config, isCompleted, isCurrent, segmentProgress }) => {
const Icon = config.icon
/** Check if this is the last stage and task has failed */
const isFailedStage = isFailed && stage === 3
return (
<div
@ -197,7 +212,7 @@ const H5ProgressBar: React.FC<H5ProgressBarProps> = ({
data-alt="progress-fill"
className="absolute inset-0 rounded-full z-0 backdrop-blur-md"
style={{
background: `${config.color}80`
background: isFailedStage ? '#ef444480' : `${config.color}80`
}}
initial={{ width: '0%' }}
animate={{ width: `${segmentProgress}%` }}
@ -219,13 +234,13 @@ const H5ProgressBar: React.FC<H5ProgressBarProps> = ({
x: '-50%',
opacity: 1,
scale: 1,
rotate: [0, 360],
rotate: isFailedStage ? 0 : [0, 360],
transition: {
left: { duration: 0.6, ease: 'easeInOut' },
x: { duration: 0 },
opacity: { duration: 0.3 },
scale: { duration: 0.3 },
rotate: { duration: 2, repeat: Infinity, ease: 'linear' }
rotate: isFailedStage ? { duration: 0 } : { duration: 2, repeat: Infinity, ease: 'linear' }
}
}}
exit={{
@ -238,8 +253,8 @@ const H5ProgressBar: React.FC<H5ProgressBarProps> = ({
data-alt="icon-wrapper"
className="w-3 h-3 rounded-full bg-white/90 backdrop-blur-sm flex items-center justify-center shadow-lg"
style={{
boxShadow: `0 0 8px ${config.color}80`,
background: `${config.color}`
boxShadow: isFailedStage ? '0 0 8px #ef444480' : `0 0 8px ${config.color}80`,
background: isFailedStage ? '#ef4444' : `${config.color}`
}}
>
{/* <Icon className="w-2 h-2" style={{ color: config.color }} /> */}
@ -250,7 +265,7 @@ const H5ProgressBar: React.FC<H5ProgressBarProps> = ({
</AnimatePresence>
{/* Glow effect for current stage */}
{isCurrent && segmentProgress < 100 && (
{isCurrent && segmentProgress < 100 && !isFailedStage && (
<motion.div
data-alt="glow-effect"
className="absolute inset-0 rounded-full z-10"

View File

@ -21,7 +21,6 @@ interface TaskInfoProps {
currentLoadingText: string;
roles: any[];
isPauseWorkFlow: boolean;
showGotoCutButton: boolean;
onGotoCut?: () => void;
setIsPauseWorkFlow: (isPauseWorkFlow: boolean) => void;
}
@ -136,7 +135,6 @@ export function TaskInfo({
currentLoadingText,
roles,
isPauseWorkFlow,
showGotoCutButton,
onGotoCut,
setIsPauseWorkFlow
}: TaskInfoProps) {
@ -151,6 +149,7 @@ export function TaskInfo({
// 监听 currentLoadingText
useEffect(() => {
console.log('currentLoadingText', currentLoadingText);
// 清理之前的定时器
if (timerRef.current) {
clearTimeout(timerRef.current);
@ -358,42 +357,6 @@ export function TaskInfo({
/>
</motion.div>
</motion.div>
{/* <motion.div
className="w-1.5 h-1.5 rounded-full"
style={{ backgroundColor: stageColor }}
animate={!isPauseWorkFlow ? {
scale: [1, 1.5, 1],
opacity: [1, 0.5, 1],
transition: {
duration: 1,
repeat: Infinity,
repeatDelay: 0.2,
delay: 0.3
}
} : {}}
/>
<motion.div
className="w-1.5 h-1.5 rounded-full"
style={{ backgroundColor: stageColor }}
animate={!isPauseWorkFlow ? {
scale: [1, 1.5, 1],
opacity: [1, 0.5, 1],
transition: {
duration: 1,
repeat: Infinity,
repeatDelay: 0.2,
delay: 0.3
}
} : {}}
/> */}
{/* //
{showGotoCutButton && (
<Tooltip placement="top" title='AI-powered editing platform'>
<GlassIconButton icon={Scissors} size='sm' onClick={onGotoCut} />
</Tooltip>
)} */}
</motion.div>
)
)

View File

@ -118,7 +118,6 @@ export function ThumbnailGrid({
}
}
console.log('changedIndex_thumbnail-grid', changedIndex);
if (changedIndex !== -1) {

View File

@ -7,49 +7,24 @@ import { useScriptService } from "@/app/service/Interaction/ScriptService";
import { useUpdateEffect } from '@/app/hooks/useUpdateEffect';
import { LOADING_TEXT_MAP, TaskObject, Status, Stage } from '@/api/DTO/movieEdit';
import { AspectRatioValue } from '@/components/ChatInputBox/AspectRatioSelector';
import { useDeviceType } from '@/hooks/useDeviceType';
import { cutUrlTo, errorConfig } from '@/lib/env';
interface UseWorkflowDataProps {
onEditPlanGenerated?: () => void;
editingStatus?: string;
onExportFailed?: () => void;
}
export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFailed }: UseWorkflowDataProps = {}) {
export function useWorkflowData({}: UseWorkflowDataProps = {}) {
const searchParams = useSearchParams();
const episodeId = searchParams.get('episodeId') || '';
const from = searchParams.get('from') || '';
const token = localStorage.getItem('token') || '';
const useid = JSON.parse(localStorage.getItem("currentUser") || '{}').id || NaN;
// H5进度提示事件桥接
const emitToastShow = (params: { title?: string; progress?: number }) => {
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('h5Toast:show', { detail: params }));
}
};
const emitToastUpdate = (params: { title?: string; progress?: number }) => {
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('h5Toast:update', { detail: params }));
}
};
const emitToastHide = () => {
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('h5Toast:hide'));
}
};
const { isMobile, isTablet, isDesktop } = useDeviceType();
const cutUrl = cutUrlTo;
console.log('cutUrl', cutUrl);
useEffect(() => {
console.log("init-useWorkflowData");
return () => {
console.log("unmount-useWorkflowData");
// 组件卸载时隐藏H5进度提示
// emitToastHide();
};
}, []);
// 查看缓存中 是否已经 加载过 这个项目的 剪辑计划
@ -90,7 +65,6 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
const [canGoToCut, setCanGoToCut] = useState(false);
const [isShowError, setIsShowError] = useState(false);
const [isAnalyzing, setIsAnalyzing] = useState(false);
const [isGenerateEditPlan, setIsGenerateEditPlan] = useState(false);
const [retryCount, setRetryCount] = useState(0);
const [isLoadingGenerateEditPlan, setIsLoadingGenerateEditPlan] = useState(false);
const [state, setState] = useState({
@ -156,102 +130,13 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
}
}, [taskObject.currentStage]);
const generateEditPlan = useCallback(async (retryCount: number) => {
if (isLoadedRef.current) {
return;
}
// 显示生成剪辑计划进度提示
// !emitToastShow({ title: isMobile ? 'Preparing for editing...' : `Generating intelligent editing plan... ${retryCount ? 'Retry Time: ' + retryCount : ''}`, progress: 0 });
// 平滑推进到 80%,后续阶段接管
const start = Date.now();
const duration = 3 * 60 * 1000; // 3分钟推进到 80%
let interval: NodeJS.Timeout | null = null;
const stop = () => { if (interval) { clearInterval(interval); interval = null; } };
interval = setInterval(() => {
const elapsed = Date.now() - start;
const pct = Math.min(80, Math.floor((elapsed / duration) * 80));
// emitToastUpdate({ progress: pct });
if (pct >= 80) stop();
}, 300);
// 先停止轮询
await new Promise(resolve => {
setNeedStreamData(false);
resolve(true);
});
setIsLoadingGenerateEditPlan(true);
try {
const response = await getGenerateEditPlan({ project_id: episodeId });
if (!response.data.editing_plan) {
throw new Error(response.message);
}
console.error('生成剪辑计划成功');
setIsGenerateEditPlan(true);
isLoadedRef.current = 'true';
setNeedStreamData(true);
// 触发回调,通知父组件计划生成完成
console.log('📞 calling onEditPlanGenerated callback');
onEditPlanGenerated?.();
setIsLoadingGenerateEditPlan(false);
stop();
} catch (error) {
console.error('生成剪辑计划失败:', error);
setNeedStreamData(true);
setIsGenerateEditPlan(false);
setTimeout(() => {
// emitToastHide();
setIsLoadingGenerateEditPlan(false);
}, 8000);
stop();
}
}, [episodeId, onEditPlanGenerated]);
const openEditPlan = useCallback(async () => {
window.open(`${cutUrl}/ai-editor/${episodeId}?token=${token}&user_id=${useid}`, '_target');
}, [episodeId, cutUrl, token, useid]);
// useEffect(() => {
// // 主动触发剪辑
// if (canGoToCut && taskObject.currentStage === 'video' && !isShowError) {
// generateEditPlan(retryCount - 1);
// }
// }, [canGoToCut, taskObject.currentStage, isShowError, generateEditPlan, retryCount]);
useEffect(() => {
// 加载剪辑计划结束 并且 失败了 重试
if (!isLoadingGenerateEditPlan && !isGenerateEditPlan) {
setRetryCount((r) => r + 1);
}
}, [isLoadingGenerateEditPlan, isGenerateEditPlan]);
useEffect(() => {
if (isShowError) {
window.msg.error('Too many failed storyboards, unable to execute automatic editing.', 8000);
setCurrentLoadingText(LOADING_TEXT_MAP.toManyFailed);
// 停止轮询
setNeedStreamData(false);
// emitToastHide();
}
if (editingStatus === 'error') {
window.msg.error('Editing failed, Please click the scissors button to go to the intelligent editing platform.', 8000);
setCurrentLoadingText(LOADING_TEXT_MAP.editingError);
// 停止轮询
setNeedStreamData(false);
// emitToastHide();
}
}, [isShowError, editingStatus]);
useUpdateEffect(() => {
console.log('-----look-taskObject_find_changed-----', taskObject);
if (taskObject.currentStage === 'script') {
if (scriptBlocksMemo.length > 0) {
console.log('应用剧本');
// 自动模式下 应用剧本;手动模式 需要点击 下一步 触发
// 确保仅自动触发一次
// state.mode.includes('auto') && loadingText.current !== LOADING_TEXT_MAP.character && applyScript();
loadingText.current = LOADING_TEXT_MAP.character;
} else {
loadingText.current = LOADING_TEXT_MAP.script;
@ -288,10 +173,18 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
if (taskObject.status === 'COMPLETED') {
loadingText.current = LOADING_TEXT_MAP.complete;
}
setCurrentLoadingText(loadingText.current);
}, [scriptBlocksMemo, taskObject.currentStage, taskObject.scenes.data, taskObject.roles.data, taskObject.videos.data, taskObject.status], {mode: 'none'});
if (taskObject.status === 'FAILED') {
if (isShowError) {
loadingText.current = LOADING_TEXT_MAP.toManyFailed;
window.msg.error('Too many failed storyboards, unable to execute automatic editing.', 8000);
} else {
loadingText.current = LOADING_TEXT_MAP.editingError;
window.msg.error('Editing failed, Please click the scissors button to go to the intelligent editing platform.', 8000);
}
}
// 将 sketchCount 和 videoCount 放到 redux 中 每一次变化也要更新
setCurrentLoadingText(loadingText.current);
}, [isShowError, scriptBlocksMemo, taskObject.currentStage, taskObject.scenes.data, taskObject.roles.data, taskObject.videos.data, taskObject.status], {mode: 'none'});
// 添加手动播放控制
const handleManualPlay = useCallback(async () => {
@ -312,9 +205,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
const all_task_data = response.data;
const { current: taskCurrent } = tempTaskObject;
console.log('---look-all_task_data', all_task_data);
console.log('---look-tempTaskObject', taskCurrent);
let combinerVideoUrl = '';
// 收集所有需要更新的状态
let stateUpdates = JSON.stringify(taskCurrent);
@ -353,7 +244,6 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
if (task.task_status === 'COMPLETED') {
realSketchResultData = taskCurrent.scenes.data.filter((item: any) => item.status !== 0);
}
console.log('---look-realSketchResultData', realSketchResultData);
taskCurrent.scenes.total_count = task.task_result.total_count;
if (task.task_status !== 'COMPLETED' || taskCurrent.scenes.total_count !== realSketchResultData.length) {
taskCurrent.currentStage = 'scene';
@ -402,39 +292,18 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
});
}
taskCurrent.videos.data = videoList;
console.log('----------正在生成视频中', realTaskResultData.length);
break;
}
if (task.task_status === 'COMPLETED') {
console.log('----------视频生成完成');
// 视频生成完成
// 暂时没有音频生成 直接跳过
// 视频分析
const error_totle = taskCurrent.videos.data.filter((item: any) => item.video_status === 2).length;
const total_count = taskCurrent.videos.data.length;
let analyze_video_completed_count = all_task_data.filter((item: any) => item.task_name === 'generate_analyze_video' && item.task_status !== 'INIT' && item.task_status !== 'RUNNING').length;
let analyze_video_total_count = all_task_data.filter((item: any) => item.task_name === 'generate_analyze_video').length;
// 检查是否需要开始显示视频分析进度
// 只在第一次检测到视频分析任务时显示通知
if (analyze_video_total_count > 0 && !isAnalyzing && analyze_video_completed_count !== analyze_video_total_count) {
setIsAnalyzing(true);
// 显示准备剪辑计划的提示
// emitToastShow({ title: isMobile ? 'Preparing for editing...' : 'Preparing intelligent editing plan...', progress: 0 });
}
if (analyze_video_total_count && analyze_video_completed_count === analyze_video_total_count) {
// 视频分析完成
if(error_totle !== total_count) {
setCanGoToCut(true);
// 重置进度条,显示生成剪辑计划进度
setIsAnalyzing(false);
// 不主动隐藏,交由后续阶段覆盖标题与进度
} else {
setIsShowError(true);
setIsAnalyzing(false);
// emitToastHide();
}
if(error_totle === total_count) {
setIsShowError(true);
taskCurrent.status = 'FAILED';
setNeedStreamData(false);
}
}
}
@ -442,18 +311,10 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
// 合成视频
if (task.task_name === 'combiner_videos') {
if (task.task_status === 'COMPLETED') {
taskCurrent.currentStage = 'final_video';
taskCurrent.final.url = task.task_result.video_url;
taskCurrent.final.note = 'combiner';
taskCurrent.status = 'COMPLETED';
combinerVideoUrl = task.task_result.video_url;
}
if (task.task_status === 'FAILED' || task.task_status === 'ERROR') {
taskCurrent.status = 'FAILED';
// 触发导出失败回调
if (onExportFailed) {
onExportFailed();
}
// 停止轮询
setNeedStreamData(false);
}
@ -470,6 +331,11 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
setNeedStreamData(false);
}
if (task.task_status === 'FAILED' || task.task_status === 'ERROR') {
// 使用合成视频地址
taskCurrent.currentStage = 'final_video';
taskCurrent.final.url = combinerVideoUrl;
taskCurrent.final.note = 'combiner';
taskCurrent.status = 'COMPLETED';
// 停止轮询
setNeedStreamData(false);
}
@ -489,13 +355,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
}
}
console.log('-----look-tempTaskObject-----', loadingText.current);
// 设置最终的状态更新
setCurrentLoadingText(loadingText.current);
if (JSON.stringify(taskCurrent) !== stateUpdates) {
console.log('-----look-tempTaskObject-changed-----', taskCurrent);
// 强制更新,使用新的对象引用确保触发更新
setTaskObject(prev => {
const newState = JSON.parse(JSON.stringify({...prev, ...taskCurrent}));
@ -506,7 +366,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
} catch (error) {
console.error('获取数据失败:', error);
}
}, [episodeId, needStreamData, onExportFailed, errorConfig, isAnalyzing]);
}, [episodeId, needStreamData, errorConfig, isAnalyzing]);
// 轮询获取流式数据
useUpdateEffect(() => {
@ -560,7 +420,6 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
setCurrentLoadingText(LOADING_TEXT_MAP.initializing);
// 如果没有标题,轮询获取
const titleResponse = await getScriptTitle({ project_id: episodeId });
console.log('titleResponse', titleResponse);
if (titleResponse.successful) {
taskCurrent.title = titleResponse.data.name;
taskCurrent.tags = titleResponse.data.description.tags || [];
@ -622,10 +481,8 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
taskCurrent.videos.total_count = data.video.total_count;
const videoList = [];
let videoUrls: string[] = [];
console.log('----------data.video.data', data.video.data);
for (const video of data.video.data) {
videoUrls = video.urls ? video.urls.filter((url: null | string) => url !== null) : [];
console.log('----------videoUrls', videoUrls);
let video_status = video.video_status === undefined ? (videoUrls.length > 0 ? 1 : 0) : video.video_status;
// 每一项 video 有多个视频 默认取存在的项
videoList.push({
@ -676,10 +533,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
taskCurrent.status = 'COMPLETED';
}
console.log('---look-taskData', taskCurrent);
if (taskCurrent.currentStage === 'script') {
console.log('开始初始化剧本', original_text,episodeId);
// TODO 为什么一开始没项目id
original_text && initializeFromProject(episodeId, original_text).then(() => {
});
@ -698,8 +552,7 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
});
// 设置是否需要获取流式数据
// setNeedStreamData(taskCurrent.status !== 'COMPLETED');
setNeedStreamData(true);
setNeedStreamData(taskCurrent.status !== 'COMPLETED');
} catch (error) {
console.error('初始化失败:', error);
@ -786,10 +639,9 @@ export function useWorkflowData({ onEditPlanGenerated, editingStatus, onExportFa
applyScript,
fallbackToStep,
originalText: state.originalText,
showGotoCutButton: (canGoToCut && (isGenerateEditPlan || taskObject.currentStage === 'final_video') || isShowError) ? true : false,
showGotoCutButton: (taskObject.status === 'FAILED' || taskObject.status === 'COMPLETED') ? true : false,
generateEditPlan: openEditPlan,
handleRetryVideo,
isShowAutoEditing: canGoToCut && taskObject.currentStage !== 'final_video' && isGenerateEditPlan && !isShowError ? true : false,
aspectRatio: state.aspectRatio
};
}