diff --git a/api/constants.ts b/api/constants.ts index 8247920..3035281 100644 --- a/api/constants.ts +++ b/api/constants.ts @@ -1,3 +1,4 @@ -export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL +// export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL +export const BASE_URL = 'https://77.smartvideo.py.qikongjian.com' // export const BASE_URL ='http://192.168.120.5:8000' // diff --git a/api/video_flow.ts b/api/video_flow.ts index f37c65c..ff82f46 100644 --- a/api/video_flow.ts +++ b/api/video_flow.ts @@ -183,6 +183,28 @@ export type ConvertScenePromptRequest = // 转换分镜头响应接口 export type ConvertScenePromptResponse = BaseApiResponse; +// 生成剪辑计划请求接口 +interface GenerateEditPlanRequest { + project_id: string; +} + +interface GenerateEditPlanResponseData { + project_id: string; + director_intent: string; + success: boolean; + editing_plan: any; + error: string | null; + processing_time: number; + video_count: number; + from_cache: boolean; + total_videos: number; + analyzed_count: number; + unanalyzed_videos: string[]; +} + +// 生成剪辑计划响应接口 +export type GenerateEditPlanResponse = BaseApiResponse; + /** * 将剧本或视频转换为分镜头提示词 * @param request - 请求参数,根据 project_type 自动判断是剧本还是视频模式 @@ -269,6 +291,11 @@ export const getRunningStreamData = async (data: { return post>("/movie/get_status", data); }; +// 获取 生成剪辑计划 接口 +export const getGenerateEditPlan = async (data: GenerateEditPlanRequest): Promise> => { + return post>("/edit-plan/generate-by-project", data); +}; + /** * 获取项目任务列表接口 * @param data - 请求参数 diff --git a/app/layout.tsx b/app/layout.tsx index a55a4a8..cd9ec8b 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -39,7 +39,7 @@ export default function RootLayout({ return ( - Movie Flow - Create Amazing Movies with AI + MovieFlow - Create Amazing Movies with AI diff --git a/components/SmartChatBox/MessageRenderer.tsx b/components/SmartChatBox/MessageRenderer.tsx index ac2c4d8..68bed03 100644 --- a/components/SmartChatBox/MessageRenderer.tsx +++ b/components/SmartChatBox/MessageRenderer.tsx @@ -111,7 +111,7 @@ export function MessageRenderer({ msg }: MessageRendererProps) { src={b.url} poster={b.poster} className="w-full max-h-80 bg-black" - controlsList="nodownload noremoteplayback" + controlsList="noremoteplayback" disablePictureInPicture disableRemotePlayback onContextMenu={e => e.preventDefault()} diff --git a/components/SmartChatBox/SmartChatBox.tsx b/components/SmartChatBox/SmartChatBox.tsx index 7bbe0ec..a39640e 100644 --- a/components/SmartChatBox/SmartChatBox.tsx +++ b/components/SmartChatBox/SmartChatBox.tsx @@ -133,8 +133,8 @@ export default function SmartChatBox({ Chat {/* System push toggle */} diff --git a/components/pages/work-flow/task-info.tsx b/components/pages/work-flow/task-info.tsx index 467183f..052e0c3 100644 --- a/components/pages/work-flow/task-info.tsx +++ b/components/pages/work-flow/task-info.tsx @@ -11,12 +11,16 @@ import { Scissors } from 'lucide-react'; import { TaskObject } from '@/api/DTO/movieEdit'; +import { GlassIconButton } from '@/components/ui/glass-icon-button'; +import { Tooltip } from 'antd'; interface TaskInfoProps { taskObject: TaskObject; currentLoadingText: string; roles: any[]; isPauseWorkFlow: boolean; + showGotoCutButton: boolean; + onGotoCut?: () => void; } const stageIconMap = { @@ -38,7 +42,7 @@ const stageIconMap = { } } -const TAG_COLORS = ['#126821', '#A133FF', '#3333FF', '#a1115e']; +const TAG_COLORS = ['#A133FF', '#a1115e']; // 阶段图标组件 const StageIcons = ({ currentStage, isExpanded, isPauseWorkFlow }: { currentStage: number, isExpanded: boolean, isPauseWorkFlow: boolean }) => { @@ -123,7 +127,9 @@ export function TaskInfo({ taskObject, currentLoadingText, roles, - isPauseWorkFlow + isPauseWorkFlow, + showGotoCutButton, + onGotoCut }: TaskInfoProps) { const [isScriptModalOpen, setIsScriptModalOpen] = useState(false); const [currentStage, setCurrentStage] = useState(0); @@ -243,11 +249,10 @@ export function TaskInfo({ ) : ( setIsScriptModalOpen(true)} > setIsScriptModalOpen(true)} > + + {/* 跳转剪辑按钮 */} + {showGotoCutButton && ( + + + + )} )} diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index 0b060d1..5cd503d 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -2,10 +2,11 @@ import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { useSearchParams } from 'next/navigation'; -import { detailScriptEpisodeNew, getScriptTitle, getRunningStreamData, pauseMovieProjectPlan, resumeMovieProjectPlan } from '@/api/video_flow'; +import { detailScriptEpisodeNew, getScriptTitle, getRunningStreamData, pauseMovieProjectPlan, resumeMovieProjectPlan, getGenerateEditPlan } from '@/api/video_flow'; 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 { all } from 'axios'; export function useWorkflowData() { useEffect(() => { @@ -15,6 +16,7 @@ export function useWorkflowData() { const searchParams = useSearchParams(); const episodeId = searchParams.get('episodeId') || ''; + const from = searchParams.get('from') || ''; const token = localStorage.getItem('token') || ''; let tempTaskObject = useRef({ @@ -49,6 +51,7 @@ export function useWorkflowData() { const [dataLoadError, setDataLoadError] = useState(null); const [needStreamData, setNeedStreamData] = useState(false); const [isPauseWorkFlow, setIsPauseWorkFlow] = useState(false); + const [canGoToCut, setCanGoToCut] = useState(false); const [state, setState] = useState({ mode: 'automatic' as 'automatic' | 'manual' | 'auto', originalText: '', @@ -110,11 +113,16 @@ export function useWorkflowData() { } }, [taskObject.currentStage]); + const generateEditPlan = useCallback(async () => { + await getGenerateEditPlan({ project_id: episodeId }); + window.open(`https://smartcut.huiying.video/ai-editor/${episodeId}?token=${token}`, '_self'); + }, [episodeId]); + useEffect(() => { - if (token && currentLoadingText.includes('Post-production')) { - window.open(`https://smartcut.huiying.video/ai-editor/${episodeId}?token=${token}`, '_self'); + if (!from && canGoToCut) { + generateEditPlan(); } - }, [currentLoadingText, token, episodeId]); + }, [canGoToCut]); useUpdateEffect(() => { @@ -191,6 +199,12 @@ export function useWorkflowData() { // 收集所有需要更新的状态 let stateUpdates = JSON.stringify(taskCurrent); + // 视频分析 + let analyze_video_completed_count = all_task_data.filter((item: any) => item.task_name === 'generate_analyze_video' && item.task_status !== 'IN_PROCESS').length; + let analyze_video_total_count = all_task_data.filter((item: any) => item.task_name === 'generate_analyze_video').length; + if (analyze_video_completed_count === analyze_video_total_count) { + setCanGoToCut(true); + } for (const task of all_task_data) { // 如果有已完成的数据,同步到状态 @@ -547,6 +561,8 @@ export function useWorkflowData() { setAnyAttribute, applyScript, fallbackToStep, - originalText: state.originalText + originalText: state.originalText, + showGotoCutButton: from && currentLoadingText.includes('Post-production') ? true : false, + generateEditPlan }; }