diff --git a/api/video_flow.ts b/api/video_flow.ts index bc7433f..ce3a9fe 100644 --- a/api/video_flow.ts +++ b/api/video_flow.ts @@ -872,7 +872,18 @@ export const updateShotPrompt = async (request: { /** 镜头描述 */ shot_descriptions: task_item; }): Promise> => { - return post("/movie/update_shot_prompt", request); + // 过滤掉第一层的空字符串字段 + const filteredDesc = Object.entries(request.shot_descriptions).reduce>((acc, [key, value]) => { + if (value !== '') { + acc[key] = value; + } + return acc; + }, {}); + + return post("/movie/update_shot_prompt", { + ...request, + shot_descriptions: filteredDesc + }); }; /** diff --git a/app/service/Interaction/ShotService.ts b/app/service/Interaction/ShotService.ts index 44a71fa..1aeb607 100644 --- a/app/service/Interaction/ShotService.ts +++ b/app/service/Interaction/ShotService.ts @@ -180,6 +180,8 @@ export const useShotService = (): UseShotService => { try { setLoading(true); + console.log('shotInfo-selectedSegment', selectedSegment); + // 调用API重新生成视频片段,返回任务状态信息 const taskResult = await vidoEditUseCase.regenerateVideoSegment( projectId, @@ -209,6 +211,7 @@ export const useShotService = (): UseShotService => { ) ); } + setIntervalIdHandler(projectId); // 返回当前选中的片段,因为现在API返回的是任务状态而不是完整的片段 return selectedSegment!; } catch (error) { diff --git a/app/service/adapter/textToShot.ts b/app/service/adapter/textToShot.ts index cc466a3..6315f72 100644 --- a/app/service/adapter/textToShot.ts +++ b/app/service/adapter/textToShot.ts @@ -256,7 +256,7 @@ export class TextToShotAdapter { currentScript += node.text; } if (node.type === 'characterToken') { - currentScript = currentScript + node.attrs.name + ' ' + node.attrs.id; + currentScript = currentScript + node.attrs.name + ' [' + node.attrs.id + ']'; } }); } diff --git a/app/service/usecase/ShotEditUsecase.ts b/app/service/usecase/ShotEditUsecase.ts index 1ed7458..2753140 100644 --- a/app/service/usecase/ShotEditUsecase.ts +++ b/app/service/usecase/ShotEditUsecase.ts @@ -188,9 +188,9 @@ export class VideoSegmentEditUseCase { this.loading = true; const shot_descriptions = VideoSegmentEntityAdapter.lensTypeToTaskItem(shot_Lens); // 如果有shot_id,先保存分镜数据 - if (shot_id) { - await this.saveShotPrompt(project_id, shot_id, shot_descriptions); - } + // if (shot_id) { + // await this.saveShotPrompt(project_id, shot_id, shot_descriptions); + // } const response = await regenerateShot({ project_id, diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index eca57f9..a6939fa 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -119,7 +119,7 @@ export function useWorkflowData() { }, [scriptBlocksMemo]); // 监听继续 请求更新数据 useUpdateEffect(() => { - if (taskObject.status !== 'IN_PROGRESS') { + if (taskObject.status === 'COMPLETED' || taskObject.status === 'FAILED') { return; } if (isPauseWorkFlow) { diff --git a/components/ui/character-editor.tsx b/components/ui/character-editor.tsx index 58689af..01b8fa9 100644 --- a/components/ui/character-editor.tsx +++ b/components/ui/character-editor.tsx @@ -82,3 +82,5 @@ export const CharacterEditor = forwardRef(({ ); }); + +CharacterEditor.displayName = 'CharacterEditor'; diff --git a/components/ui/edit-modal.tsx b/components/ui/edit-modal.tsx index b8c6c4f..da429cf 100644 --- a/components/ui/edit-modal.tsx +++ b/components/ui/edit-modal.tsx @@ -58,6 +58,7 @@ export function EditModal({ // 添加一个状态来标记是否是从切换tab触发的提醒 const [pendingSwitchTabId, setPendingSwitchTabId] = useState(null); const [disabledBtn, setDisabledBtn] = useState(false); + const [isRemindCloseOpen, setIsRemindCloseOpen] = useState(false); useEffect(() => { setCurrentIndex(currentSketchIndex); @@ -166,6 +167,23 @@ export function EditModal({ setResetKey(resetKey + 1); } + const handleClickClose = () => { + // TODO 关闭前 检查 当前tab 下是否有更新 如果有更新 则提醒用户 是否确认应用 + // 暂时 默认弹出提醒 + setIsRemindCloseOpen(true); + } + + const handleConfirmApply = () => { + console.log('handleConfirmApply'); + setIsRemindCloseOpen(false); + handleConfirmGotoFallback(); + } + + const handleCloseRemindClosePanel = () => { + setIsRemindCloseOpen(false); + onClose(); + } + const renderTabContent = () => { switch (activeTab) { case '0': @@ -235,7 +253,6 @@ export function EditModal({ initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} - onClick={onClose} /> {/* 弹窗内容 */} @@ -290,7 +307,7 @@ export function EditModal({ {/* 关闭按钮 */} @@ -407,6 +424,38 @@ export function EditModal({ + + {/* 提醒用户 关闭当前弹窗 是否确认应用 */} + +
+
+ +

If you have modified the content, closing it will lose the modified content. Do you want to apply the modifications?

+
+
+ + +
+
+
)} diff --git a/components/ui/shot-editor/CharacterToken.tsx b/components/ui/shot-editor/CharacterToken.tsx index 5cbcd90..a793080 100644 --- a/components/ui/shot-editor/CharacterToken.tsx +++ b/components/ui/shot-editor/CharacterToken.tsx @@ -11,7 +11,7 @@ interface CharacterTokenOptions { export function CharacterToken(props: ReactNodeViewProps) { const [showRoleList, setShowRoleList] = useState(false); - const [listPosition, setListPosition] = useState({ top: 0, left: 0 }); + const [listPosition, setListPosition] = useState({ top: 0, left: 0, bottom: 0 }); const { name } = props.node.attrs as ScriptRoleEntity; const extension = props.extension as Node; const roles = extension.options.roles || []; @@ -31,6 +31,7 @@ export function CharacterToken(props: ReactNodeViewProps) { // 计算理想的顶部位置(在token下方) let top = tokenRect.bottom + 8; // 8px 间距 let left = tokenRect.left; + let bottom = tokenRect.top; // 检查是否超出底部 if (top + listRect.height > viewportHeight) { @@ -44,10 +45,18 @@ export function CharacterToken(props: ReactNodeViewProps) { left = viewportWidth - listRect.width - 8; } + // 检查是否超出顶部 + if (bottom - listRect.height < 0) { + // 如果超出顶部,将列表显示在token下方 + bottom = tokenRect.bottom + 8; + } + // 确保不会超出左侧 left = Math.max(8, left); + // 确保不会超顶部 + top = Math.max(0, top); - setListPosition({ top, left }); + setListPosition({ top, left, bottom }); }; // 监听窗口大小变化 @@ -107,7 +116,7 @@ export function CharacterToken(props: ReactNodeViewProps) { exit={{ opacity: 0, y: 4 }} transition={{ duration: 0.2 }} ref={listRef} - className="fixed w-64 rounded-lg backdrop-blur-md bg-white/10 border border-white/20 p-2 z-[51]" + className="fixed w-64 rounded-lg backdrop-blur-md bg-white/10 border border-white/20 p-2 z-[51] overflow-y-auto" style={{ top: listPosition.top, left: listPosition.left diff --git a/components/ui/shot-editor/ShotsEditor.tsx b/components/ui/shot-editor/ShotsEditor.tsx index 9a562d9..4d80bf2 100644 --- a/components/ui/shot-editor/ShotsEditor.tsx +++ b/components/ui/shot-editor/ShotsEditor.tsx @@ -26,17 +26,11 @@ const createEmptyShot = (): Shot => ({ name: `shot${Date.now()}`, shotDescContent: [{ type: 'paragraph', - content: [{ - type: 'text', - text: 'Add shot description here...' - }] + content: [] }], shotDialogsContent: [{ type: 'paragraph', - content: [{ - type: 'text', - text: 'Add shot dialogue here...' - }] + content: [] }] }); diff --git a/components/ui/shot-tab-content.tsx b/components/ui/shot-tab-content.tsx index 8dcdfa6..50eb4c4 100644 --- a/components/ui/shot-tab-content.tsx +++ b/components/ui/shot-tab-content.tsx @@ -56,10 +56,12 @@ export const ShotTabContent = (props: ShotTabContentProps) => { useEffect(() => { if (pendingRegeneration) { + console.log('pendingRegeneration', pendingRegeneration, shotData[selectedIndex]?.lens); regenerateVideoSegment(); setPendingRegeneration(false); + setIsRegenerate(false); } - }, [shotData[selectedIndex]?.lens]); + }, [pendingRegeneration]); // 监听当前选中index变化 useEffect(() => { @@ -179,7 +181,9 @@ export const ShotTabContent = (props: ShotTabContentProps) => { ...shotData[selectedIndex], lens: shotInfo }); - setPendingRegeneration(true); + setTimeout(() => { + setPendingRegeneration(true); + }, 1000); }; // 新增分镜 @@ -342,55 +346,60 @@ export const ShotTabContent = (props: ShotTabContentProps) => {
{/* 选中的视频预览 */} <> - {shotData[selectedIndex]?.status === 0 && ( + {(shotData[selectedIndex]?.status === 0) && (
Loading...
)} - {shotData[selectedIndex]?.status === 1 && ( - - - - {/* 人物替换按钮 */} - handleScan()} - className={`p-2 backdrop-blur-sm transition-colors z-10 rounded-full - ${scanState === 'detected' - ? 'bg-cyan-500/50 hover:bg-cyan-500/70 text-white' - : 'bg-black/50 hover:bg-black/70 text-white' - }`} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - {scanState === 'scanning' ? ( - - ) : scanState === 'detected' ? ( - - ) : ( - - )} - + + {shotData[selectedIndex]?.status === 1 && shotData[selectedIndex]?.videoUrl.length && ( + + + + {/* 人物替换按钮 */} + handleScan()} + className={`p-2 backdrop-blur-sm transition-colors z-10 rounded-full + ${scanState === 'detected' + ? 'bg-cyan-500/50 hover:bg-cyan-500/70 text-white' + : 'bg-black/50 hover:bg-black/70 text-white' + }`} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + {scanState === 'scanning' ? ( + + ) : scanState === 'detected' ? ( + + ) : ( + + )} + + - - )} - {shotData[selectedIndex]?.status === 2 && ( + )} + + {(shotData[selectedIndex]?.status === 2 || !shotData[selectedIndex]?.videoUrl.length) && (
- 任务失败,点击重新生成 + Failed, click to regenerate
)}