diff --git a/app/service/Interaction/RoleService.ts b/app/service/Interaction/RoleService.ts index b059dc6..19fb34b 100644 --- a/app/service/Interaction/RoleService.ts +++ b/app/service/Interaction/RoleService.ts @@ -158,6 +158,7 @@ export const useRoleServiceHook = (): UseRoleService => { content: keyword, loadingProgress: 100, disableEdit: false, + updatedAt: Date.now(), })), } : role @@ -177,6 +178,7 @@ export const useRoleServiceHook = (): UseRoleService => { content: keyword, loadingProgress: 100, disableEdit: false, + updatedAt: Date.now(), })), }); } @@ -383,6 +385,7 @@ export const useRoleServiceHook = (): UseRoleService => { content: highlight, loadingProgress: 100, disableEdit: false, + updatedAt: Date.now(), })), }; diff --git a/app/service/domain/Item.ts b/app/service/domain/Item.ts index 97c9037..cb00b7b 100644 --- a/app/service/domain/Item.ts +++ b/app/service/domain/Item.ts @@ -83,3 +83,17 @@ export class SceneItem extends EditItem { super(entity, metadata); } } + +/** + * 标签可编辑项 + */ +export class TagItem extends EditItem { + type: ItemType.TEXT = ItemType.TEXT; + + constructor( + entity: TagValueObject, + metadata: Record = {} + ) { + super(entity, metadata); + } +} diff --git a/app/service/domain/valueObject.ts b/app/service/domain/valueObject.ts index 25b5be2..a18656e 100644 --- a/app/service/domain/valueObject.ts +++ b/app/service/domain/valueObject.ts @@ -95,6 +95,8 @@ export interface TagValueObject { disableEdit: boolean; /** 颜色 */ color?: string; + /** 更新时间 */ + readonly updatedAt: number; } diff --git a/app/service/usecase/SceneEditUseCase.ts b/app/service/usecase/SceneEditUseCase.ts index 78a05da..e69de29 100644 --- a/app/service/usecase/SceneEditUseCase.ts +++ b/app/service/usecase/SceneEditUseCase.ts @@ -1,90 +0,0 @@ -import { SceneEntity, AITextEntity } from '../domain/Entities'; -import { SceneItem, TagItem, TextItem } from '../domain/Item'; -import { regenerateScene, applySceneToShots, getSceneData } from '@/api/video_flow'; - -/** - * 场景编辑用例 - * 负责场景内容的初始化、修改和优化 - */ -export class SceneEditUseCase { - constructor(private sceneItem: SceneItem) { - } - - /** - * @description: 重新生成场景 - * @param {TextItem} prompt - * @param {TagItem[]} tags - * @return {*} - */ - async AIgenerateScene(prompt: TextItem, tags: TagItem[]): Promise { - const promptText = prompt.entity.content; - const tagList = tags.map((tag) => tag.entity.content); - - // 调用重新生成场景接口 - const response = await regenerateScene({ - sceneId: this.sceneItem.entity.id || '', - prompt: promptText, - tagTypes: tagList, - }); - - if (response.successful) { - const sceneEntity = response.data; - this.sceneItem.setEntity(sceneEntity); - return sceneEntity; - } else { - throw new Error(`重新生成场景失败: ${response.message}`); - } - } - - /** - * 应用此场景到指定分镜 - * @param shotIds 分镜ID列表 - * @returns 应用结果 - */ - async applyScene(shotIds: string[]) { - const sceneId = this.sceneItem.entity.id; - - return await applySceneToShots({ - sceneId, - shotIds - }); - } - - /** - * 重新获取当前场景的数据 - * @description 从服务器重新获取当前场景的AI文本和标签数据,并更新当前实体 - * @returns Promise<{ text: AITextEntity; tags: TagValueObject[] }> 场景相关的AI文本和标签数据 - * @throws {Error} 当API调用失败时抛出错误 - */ - async refreshSceneData(): Promise<{ text: AITextEntity; tags: TagValueObject[] }> { - const sceneId = this.sceneItem.entity.id; - - if (!sceneId) { - throw new Error('场景ID不存在,无法获取场景数据'); - } - - const response = await getSceneData({ - sceneId: sceneId - }); - - if (response.successful) { - // 更新当前场景的实体数据 - const { text, tags } = response.data; - - // 更新场景实体中的相关字段 - const updatedSceneEntity = { - ...this.sceneItem.entity, - generateTextId: text.id, // 更新AI文本ID - tagIds: tags.map(tag => tag.id), // 更新标签ID列表 - updatedAt: Date.now(), // 更新时间戳 - }; - - // 更新当前UseCase中的实体 - this.sceneItem.setEntity(updatedSceneEntity); - - return response.data; - } else { - throw new Error(`获取场景数据失败: ${response.message}`); - } - } -} diff --git a/compile_test.sh b/compile_test.sh new file mode 100755 index 0000000..ea743fa --- /dev/null +++ b/compile_test.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +BRANCH_NAME="dev" + +# 修改项目名称 +PROJECT_NAME="video-flow-frontend-test" + +# 设置日志文件路径 +LOGFILE="build_and_copy.log" + +# 记录开始时间 +echo "Build process started at $(date)" | tee $LOGFILE + +# 获取当前分支名 +current_branch=$(git rev-parse --abbrev-ref HEAD) + +# 打包之前,需要检查是否在 dev 分支,工作区是否干净,是否和远程分支一致 +if [ "$(git branch --show-current)" != "$BRANCH_NAME" ]; then + echo "当前分支不是 dev 分支" + exit 1 +fi + +# 检查工作区是否干净 +if [ -n "$(git status --porcelain)" ]; then + echo "工作区不干净" + exit 1 +fi + +# 检查远程分支是否和本地分支一致 +if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/$BRANCH_NAME)" ]; then + echo "本地分支和远程分支不一致" + exit 1 +fi + + +# 检查当前分支并运行相应的 npm 命令 +if [ "$current_branch" = "$BRANCH_NAME" ]; then + echo "On dev branch, building project..." | tee -a $LOGFILE + PROFILE_ENV=$BRANCH_NAME + + # 安装依赖并构建 + yarn install + yarn build + + # 准备dist目录 + mkdir -p dist + cp -r .next dist/ + cp -r public dist/ + cp package.json dist/ + cp package-lock.json dist/ + +else + echo "On non-dev branch ($current_branch), exiting" + exit 1 +fi + +# 创建tar包 +tar -czvf $PROJECT_NAME-$PROFILE_ENV.tar.gz dist + +# 记录结束时间 +echo "Build process completed at $(date)" | tee -a $LOGFILE + +# 上传到 nexus +echo "upload to nexus at $(date)" | tee -a $LOGFILE +curl -u 'admin':'YZ9Gq6=8\*G|?:,' --upload-file $PROJECT_NAME-$PROFILE_ENV.tar.gz https://repo.qikongjian.com/repository/frontend-tar-files/ + +# 清理构建文件 +rm -rf dist +rm $PROJECT_NAME-$PROFILE_ENV.tar.gz diff --git a/components/pages/work-flow/use-api-data.ts b/components/pages/work-flow/use-api-data.ts index f726e56..15337c3 100644 --- a/components/pages/work-flow/use-api-data.ts +++ b/components/pages/work-flow/use-api-data.ts @@ -2,6 +2,7 @@ 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 = { @@ -15,34 +16,35 @@ const STEP_MAP = { type ApiStep = keyof typeof STEP_MAP; interface TaskData { - sketch?: Array<{ + sketch?: { url: string; script: string; bg_rgb: string[]; - }>; - roles?: Array<{ + }[]; + roles?: { name: string; url: string; sound: string; soundDescription: string; roleDescription: string; - }>; - videos?: Array<{ + }[]; + videos?: { url: string; script: string; audio: string; - }>; - music?: Array<{ + }[]; + music?: { url: string; script: string; name: string; duration: string; totalDuration: string; isLooped: boolean; - }>; + }[]; final?: { url: string; }; + [key: string]: any; // 允许其他可能的字段 } export const useApiData = () => { @@ -192,7 +194,38 @@ export const useApiData = () => { // 设置任务数据 if (data) { - setTaskData(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; diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index b878c00..6ca6792 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -424,7 +424,7 @@ export function useWorkflowData() { } const { name, status, data, tags, mode, original_text } = response.data; - setMode(mode); + setMode(mode as 'automatic' | 'manual' | 'auto'); setOriginalText(original_text); setIsLoading(false); @@ -569,9 +569,9 @@ export function useWorkflowData() { } // 粗剪 - if (data.final_simple_video && data.final_simple_video.video) { + if ((data as any).final_simple_video && (data as any).final_simple_video.video) { setFinal({ - url: data.final_simple_video.video + url: (data as any).final_simple_video.video }); taskData.status = '5.5'; loadingText = LOADING_TEXT_MAP.postProduction('generating fine-grained video clips...'); diff --git a/components/script-renderer/ScriptRenderer.tsx b/components/script-renderer/ScriptRenderer.tsx index 161e8c0..a369129 100644 --- a/components/script-renderer/ScriptRenderer.tsx +++ b/components/script-renderer/ScriptRenderer.tsx @@ -14,7 +14,7 @@ interface ScriptRendererProps { isPauseWorkFlow: boolean; applyScript: any; mode: string; - from: string; + from?: string; setIsUpdate?: (isUpdate: boolean) => void; } @@ -32,7 +32,7 @@ export const ScriptRenderer: React.FC = ({ data, setIsPause useEffect(() => { const themeBlock = data.find(block => block.id === 'categories'); if (themeBlock && themeBlock.content.length > 0) { - const themeTag = themeBlock.content[0].text.split(',').map(item => item.trim()); + const themeTag = themeBlock.content[0].text.split(',').map((item: string) => item.trim()); console.log('themeTag', themeTag); setAddThemeTag(themeTag); } @@ -110,13 +110,13 @@ export const ScriptRenderer: React.FC = ({ data, setIsPause // console.log(e.target.value); }; - const handleBlockTextBlur = (block: ScriptBlock) => (e: ContentEditableEvent) => { + const handleBlockTextBlur = (block: ScriptBlock) => () => { setEditBlockId(null); if (contentEditableRef.current) { const text = contentEditableRef.current.innerText; console.log('contentEditableRef---text', text); console.log('contentEditableRef---block', block.id, block); - if (block.content[0].text !== text) { + if (block.content[0].text !== text && setIsUpdate) { setIsUpdate(true); } setAnyAttribute(block.id, text,from !== 'tab',(old: string)=>{ @@ -140,7 +140,9 @@ export const ScriptRenderer: React.FC = ({ data, setIsPause return; } setAddThemeTag(value); - setIsUpdate(true); + if (setIsUpdate) { + setIsUpdate(true); + } from !== 'tab' && setIsPauseWorkFlow(true); setAnyAttribute('categories', value.join(','),from !== 'tab',(old: string)=>{ if(old!==value.join(',')){ diff --git a/components/ui/CharacterToken.tsx b/components/ui/CharacterToken.tsx index 3ddd8c8..d0d9c26 100644 --- a/components/ui/CharacterToken.tsx +++ b/components/ui/CharacterToken.tsx @@ -1,5 +1,5 @@ import { Node, mergeAttributes } from '@tiptap/core' -import { ReactNodeViewRenderer, NodeViewWrapper } from '@tiptap/react' +import { ReactNodeViewRenderer, NodeViewWrapper, ReactNodeViewProps } from '@tiptap/react' import { motion, AnimatePresence } from 'framer-motion' import { useState } from 'react' @@ -33,7 +33,8 @@ export const CharacterToken = Node.create({ }, }) -function CharacterView({ node }) { +function CharacterView(props: ReactNodeViewProps) { + const { node } = props; const [showCard, setShowCard] = useState(false) const { name, avatar, gender, age } = node.attrs diff --git a/components/ui/replace-scene-panel.tsx b/components/ui/replace-scene-panel.tsx index 266ca36..4046120 100644 --- a/components/ui/replace-scene-panel.tsx +++ b/components/ui/replace-scene-panel.tsx @@ -71,6 +71,7 @@ export function ReplaceScenePanel({ showAddToLibrary={false} onClose={onClose} onConfirm={onConfirm} + isLoading={false} /> ); } \ No newline at end of file