This commit is contained in:
海龙 2025-08-30 14:56:08 +08:00
commit b543e28b23
8 changed files with 79 additions and 18 deletions

View File

@ -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'
//

View File

@ -183,6 +183,28 @@ export type ConvertScenePromptRequest =
// 转换分镜头响应接口
export type ConvertScenePromptResponse = BaseApiResponse<ScenePrompts>;
// 生成剪辑计划请求接口
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<GenerateEditPlanResponseData>;
/**
*
* @param request - project_type
@ -269,6 +291,11 @@ export const getRunningStreamData = async (data: {
return post<ApiResponse<any>>("/movie/get_status", data);
};
// 获取 生成剪辑计划 接口
export const getGenerateEditPlan = async (data: GenerateEditPlanRequest): Promise<ApiResponse<GenerateEditPlanResponse>> => {
return post<ApiResponse<GenerateEditPlanResponse>>("/edit-plan/generate-by-project", data);
};
/**
*
* @param data -

View File

@ -39,7 +39,7 @@ export default function RootLayout({
return (
<html lang="en" suppressHydrationWarning>
<head>
<title>Movie Flow - Create Amazing Movies with AI</title>
<title>MovieFlow - Create Amazing Movies with AI</title>
<meta name="description" content="Professional AI-powered video creation platform with advanced editing tools" />
<meta name="robots" content="noindex" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

View File

@ -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()}

View File

@ -133,8 +133,8 @@ export default function SmartChatBox({
<span>Chat</span>
{/* System push toggle */}
<Switch
checkedChildren="System push: On"
unCheckedChildren="System push: Off"
checkedChildren="System: On"
unCheckedChildren="System: Off"
checked={systemPush}
onChange={toggleSystemPush}
className="ml-2 "

View File

@ -52,7 +52,9 @@ const WorkFlow = React.memo(function WorkFlow() {
setAnyAttribute,
applyScript,
fallbackToStep,
originalText
originalText,
showGotoCutButton,
generateEditPlan
} = useWorkflowData();
const {
@ -91,6 +93,8 @@ const WorkFlow = React.memo(function WorkFlow() {
currentLoadingText={currentLoadingText}
roles={taskObject.roles.data}
isPauseWorkFlow={isPauseWorkFlow}
showGotoCutButton={showGotoCutButton}
onGotoCut={generateEditPlan}
/>
</ErrorBoundary>
</div>

View File

@ -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({
</motion.div>
) : (
<motion.div
className="flex items-center gap-2 justify-center cursor-pointer"
className="flex items-center gap-2 justify-center"
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
onClick={() => setIsScriptModalOpen(true)}
>
<motion.div
className="w-1.5 h-1.5 rounded-full"
@ -303,7 +308,7 @@ export function TaskInfo({
{/* 主文字 - 颜色填充动画 */}
<motion.div
className="relative z-10"
className="relative z-10 cursor-pointer"
animate={!isPauseWorkFlow ? {
scale: [1, 1.02, 1],
transition: {
@ -312,6 +317,7 @@ export function TaskInfo({
ease: "easeInOut"
}
}: {}}
onClick={() => setIsScriptModalOpen(true)}
>
<motion.span
className="normalS400 subtitle-had8uE text-transparent bg-clip-text bg-gradient-to-r from-blue-600 via-cyan-500 to-purple-600"
@ -390,6 +396,13 @@ export function TaskInfo({
}
} : {}}
/>
{/* 跳转剪辑按钮 */}
{showGotoCutButton && (
<Tooltip placement="top" title='Go to Smart Cut'>
<GlassIconButton icon={Scissors} size='sm' onClick={onGotoCut} />
</Tooltip>
)}
</motion.div>
)}
</>

View File

@ -2,7 +2,7 @@
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';
@ -15,6 +15,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<TaskObject>({
@ -49,6 +50,7 @@ export function useWorkflowData() {
const [dataLoadError, setDataLoadError] = useState<string | null>(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 +112,16 @@ export function useWorkflowData() {
}
}, [taskObject.currentStage]);
useEffect(() => {
if (token && currentLoadingText.includes('Post-production')) {
const generateEditPlan = useCallback(async () => {
await getGenerateEditPlan({ project_id: episodeId });
window.open(`https://smartcut.huiying.video/ai-editor/${episodeId}?token=${token}`, '_self');
}
}, [currentLoadingText, token, episodeId]);
}, [episodeId]);
// useEffect(() => {
// if (!from && canGoToCut) {
// generateEditPlan();
// }
// }, [canGoToCut]);
useUpdateEffect(() => {
@ -191,6 +198,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 +560,9 @@ export function useWorkflowData() {
setAnyAttribute,
applyScript,
fallbackToStep,
originalText: state.originalText
originalText: state.originalText,
// showGotoCutButton: from && currentLoadingText.includes('Post-production') ? true : false,
showGotoCutButton: canGoToCut ? true : false,
generateEditPlan
};
}