forked from 77media/video-flow
优化 ScriptService 和 ShotService,移除调试信息,更新接口以支持新功能,重构代码以提高可读性。
This commit is contained in:
parent
817c5d5144
commit
1ef7d03830
@ -99,7 +99,6 @@ export interface UseScriptService {
|
|||||||
* 包括剧本生成、项目创建、剧本保存等功能
|
* 包括剧本生成、项目创建、剧本保存等功能
|
||||||
*/
|
*/
|
||||||
export const useScriptService = (): UseScriptService => {
|
export const useScriptService = (): UseScriptService => {
|
||||||
console.log('useScriptService----9((@@@@@@@@@@@@@@@@@@');
|
|
||||||
// 响应式状态
|
// 响应式状态
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [synopsis, setSynopsis] = useState<string>("");
|
const [synopsis, setSynopsis] = useState<string>("");
|
||||||
@ -215,7 +214,6 @@ export const useScriptService = (): UseScriptService => {
|
|||||||
|
|
||||||
// 获取解析后的故事详情
|
// 获取解析后的故事详情
|
||||||
const storyDetails = newScriptEditUseCase.getStoryDetails();
|
const storyDetails = newScriptEditUseCase.getStoryDetails();
|
||||||
|
|
||||||
setSynopsis(storyDetails.synopsis || "");
|
setSynopsis(storyDetails.synopsis || "");
|
||||||
setCategories(storyDetails.categories || []);
|
setCategories(storyDetails.categories || []);
|
||||||
setProtagonist(storyDetails.protagonist || "");
|
setProtagonist(storyDetails.protagonist || "");
|
||||||
@ -231,7 +229,7 @@ export const useScriptService = (): UseScriptService => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[projectId, scriptEditUseCase]
|
[generateScriptFromIdea]
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -392,7 +390,6 @@ export const useScriptService = (): UseScriptService => {
|
|||||||
|
|
||||||
const setAnyAttributeWrapper = useCallback(
|
const setAnyAttributeWrapper = useCallback(
|
||||||
(type: string, value: SetStateAction<string>, tags?: string[]) => {
|
(type: string, value: SetStateAction<string>, tags?: string[]) => {
|
||||||
console.log('setAnyAttributeWrapper', type);
|
|
||||||
if (type === 'synopsis') {
|
if (type === 'synopsis') {
|
||||||
setFieldOld(synopsis)
|
setFieldOld(synopsis)
|
||||||
scriptEditUseCase.replaceScript(fieldOld,synopsis)
|
scriptEditUseCase.replaceScript(fieldOld,synopsis)
|
||||||
@ -492,8 +489,7 @@ export const useScriptService = (): UseScriptService => {
|
|||||||
];
|
];
|
||||||
// 筛选出有内容的block
|
// 筛选出有内容的block
|
||||||
const filteredArr = arr.filter(item => (item.content.length > 0 && item.content[0].text !== ''));
|
const filteredArr = arr.filter(item => (item.content.length > 0 && item.content[0].text !== ''));
|
||||||
console.log('scriptBlocksMemo 所有关联数据', synopsis, categories, protagonist, incitingIncident, problem, conflict, stakes, characterArc);
|
console.log('scriptBlocksMemo', JSON.parse(JSON.stringify(filteredArr)));
|
||||||
console.log('scriptBlocksMemo', filteredArr);
|
|
||||||
return filteredArr;
|
return filteredArr;
|
||||||
}, [
|
}, [
|
||||||
synopsis,
|
synopsis,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useState, useCallback } from "react";
|
import { useState, useCallback } from "react";
|
||||||
import { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase";
|
import { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase";
|
||||||
import { VideoSegmentEntity } from "../domain/Entities";
|
import { VideoSegmentEntity } from "../domain/Entities";
|
||||||
import { ContentItem, LensType } from "../domain/valueObject";
|
import { LensType } from "../domain/valueObject";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 视频片段服务Hook接口
|
* 视频片段服务Hook接口
|
||||||
@ -15,8 +15,6 @@ export interface UseShotService {
|
|||||||
videoSegments: VideoSegmentEntity[];
|
videoSegments: VideoSegmentEntity[];
|
||||||
/** 当前选中的视频片段 */
|
/** 当前选中的视频片段 */
|
||||||
selectedSegment: VideoSegmentEntity | null;
|
selectedSegment: VideoSegmentEntity | null;
|
||||||
/** 错误信息 */
|
|
||||||
error: string | null;
|
|
||||||
|
|
||||||
// 操作方法
|
// 操作方法
|
||||||
/** 获取视频片段列表 */
|
/** 获取视频片段列表 */
|
||||||
@ -38,8 +36,10 @@ export interface UseShotService {
|
|||||||
abortOperation: () => void;
|
abortOperation: () => void;
|
||||||
/** 设置选中的视频片段 */
|
/** 设置选中的视频片段 */
|
||||||
setSelectedSegment: (segment: VideoSegmentEntity | null) => void;
|
setSelectedSegment: (segment: VideoSegmentEntity | null) => void;
|
||||||
/** 清除错误信息 */
|
/** 添加新镜头到选中的视频片段 */
|
||||||
clearError: () => void;
|
addNewLens: () => void;
|
||||||
|
/** 删除指定镜头 */
|
||||||
|
deleteLens: (lensName: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,10 +52,9 @@ export const useShotService = (): UseShotService => {
|
|||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [videoSegments, setVideoSegments] = useState<VideoSegmentEntity[]>([]);
|
const [videoSegments, setVideoSegments] = useState<VideoSegmentEntity[]>([]);
|
||||||
const [selectedSegment, setSelectedSegment] = useState<VideoSegmentEntity | null>(null);
|
const [selectedSegment, setSelectedSegment] = useState<VideoSegmentEntity | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
// UseCase实例
|
// UseCase实例
|
||||||
const [shotEditUseCase] = useState<VideoSegmentEditUseCase>(
|
const [vidoEditUseCase] = useState<VideoSegmentEditUseCase>(
|
||||||
new VideoSegmentEditUseCase()
|
new VideoSegmentEditUseCase()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -67,19 +66,16 @@ export const useShotService = (): UseShotService => {
|
|||||||
async (projectId: string): Promise<void> => {
|
async (projectId: string): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
|
||||||
|
|
||||||
const segments = await shotEditUseCase.getVideoSegmentList(projectId);
|
const segments = await vidoEditUseCase.getVideoSegmentList(projectId);
|
||||||
setVideoSegments(segments);
|
setVideoSegments(segments);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : "获取视频片段列表失败";
|
|
||||||
setError(errorMessage);
|
|
||||||
console.error("获取视频片段列表失败:", error);
|
console.error("获取视频片段列表失败:", error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[shotEditUseCase]
|
[vidoEditUseCase]
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,9 +95,8 @@ export const useShotService = (): UseShotService => {
|
|||||||
): Promise<VideoSegmentEntity> => {
|
): Promise<VideoSegmentEntity> => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
|
||||||
|
|
||||||
const regeneratedSegment = await shotEditUseCase.regenerateVideoSegment(
|
const regeneratedSegment = await vidoEditUseCase.regenerateVideoSegment(
|
||||||
shotPrompt,
|
shotPrompt,
|
||||||
shotId,
|
shotId,
|
||||||
roleReplaceParams,
|
roleReplaceParams,
|
||||||
@ -122,15 +117,13 @@ export const useShotService = (): UseShotService => {
|
|||||||
|
|
||||||
return regeneratedSegment;
|
return regeneratedSegment;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : "重新生成视频片段失败";
|
|
||||||
setError(errorMessage);
|
|
||||||
console.error("重新生成视频片段失败:", error);
|
console.error("重新生成视频片段失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[shotEditUseCase]
|
[vidoEditUseCase]
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,9 +141,8 @@ export const useShotService = (): UseShotService => {
|
|||||||
): Promise<LensType[]> => {
|
): Promise<LensType[]> => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
|
||||||
|
|
||||||
const optimizedLensData = await shotEditUseCase.optimizeVideoContent(
|
const optimizedLensData = await vidoEditUseCase.optimizeVideoContent(
|
||||||
shotId,
|
shotId,
|
||||||
userRequirement,
|
userRequirement,
|
||||||
lensData
|
lensData
|
||||||
@ -161,23 +153,21 @@ export const useShotService = (): UseShotService => {
|
|||||||
|
|
||||||
return optimizedLensData;
|
return optimizedLensData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : "AI优化视频内容失败";
|
|
||||||
setError(errorMessage);
|
|
||||||
console.error("AI优化视频内容失败:", error);
|
console.error("AI优化视频内容失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[shotEditUseCase]
|
[vidoEditUseCase]
|
||||||
);
|
);
|
||||||
/**
|
/**
|
||||||
* 中断当前操作
|
* 中断当前操作
|
||||||
*/
|
*/
|
||||||
const abortOperation = useCallback((): void => {
|
const abortOperation = useCallback((): void => {
|
||||||
shotEditUseCase.abortOperation();
|
vidoEditUseCase.abortOperation();
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [shotEditUseCase]);
|
}, [vidoEditUseCase]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置选中的视频片段
|
* 设置选中的视频片段
|
||||||
@ -187,24 +177,85 @@ export const useShotService = (): UseShotService => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除错误信息
|
* 添加新镜头到选中的视频片段
|
||||||
|
* @description 在selectedSegment的lens数组中添加一个新的空镜头,镜头名称按顺序命名
|
||||||
*/
|
*/
|
||||||
const clearError = useCallback((): void => {
|
const addNewLens = useCallback((): void => {
|
||||||
setError(null);
|
if (!selectedSegment) {
|
||||||
}, []);
|
console.warn("没有选中的视频片段,无法添加镜头");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算下一个镜头编号
|
||||||
|
const currentLensCount = selectedSegment.lens.length;
|
||||||
|
const newLensName = `镜头${currentLensCount + 1}`;
|
||||||
|
|
||||||
|
// 创建新的空镜头
|
||||||
|
const newLens = new LensType(newLensName, "", []);
|
||||||
|
|
||||||
|
// 创建更新后的片段
|
||||||
|
const updatedSegment: VideoSegmentEntity = {
|
||||||
|
...selectedSegment,
|
||||||
|
lens: [...selectedSegment.lens, newLens]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量更新状态,避免多次重渲染
|
||||||
|
setSelectedSegment(updatedSegment);
|
||||||
|
setVideoSegments(prev => {
|
||||||
|
const segmentIndex = prev.findIndex(segment => segment.id === selectedSegment.id);
|
||||||
|
if (segmentIndex === -1) return prev;
|
||||||
|
|
||||||
|
const newSegments = [...prev];
|
||||||
|
newSegments[segmentIndex] = updatedSegment;
|
||||||
|
return newSegments;
|
||||||
|
});
|
||||||
|
}, [selectedSegment]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定镜头
|
||||||
|
* @param lensName 要删除的镜头名称
|
||||||
|
*/
|
||||||
|
const deleteLens = useCallback((lensName: string): void => {
|
||||||
|
if (!selectedSegment) {
|
||||||
|
console.warn("没有选中的视频片段,无法删除镜头");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤掉指定名称的镜头并重新排序
|
||||||
|
const updatedLens = selectedSegment.lens
|
||||||
|
.filter(lens => lens.name !== lensName)
|
||||||
|
.map((lens, index) => new LensType(`镜头${index + 1}`, lens.script, lens.content));
|
||||||
|
|
||||||
|
// 创建更新后的片段
|
||||||
|
const updatedSegment: VideoSegmentEntity = {
|
||||||
|
...selectedSegment,
|
||||||
|
lens: updatedLens
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量更新状态,避免多次重渲染
|
||||||
|
setSelectedSegment(updatedSegment);
|
||||||
|
setVideoSegments(prev => {
|
||||||
|
const segmentIndex = prev.findIndex(segment => segment.id === selectedSegment.id);
|
||||||
|
if (segmentIndex === -1) return prev;
|
||||||
|
|
||||||
|
const newSegments = [...prev];
|
||||||
|
newSegments[segmentIndex] = updatedSegment;
|
||||||
|
return newSegments;
|
||||||
|
});
|
||||||
|
}, [selectedSegment]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// 响应式状态
|
// 响应式状态
|
||||||
loading,
|
loading,
|
||||||
videoSegments,
|
videoSegments,
|
||||||
selectedSegment,
|
selectedSegment,
|
||||||
error,
|
|
||||||
// 操作方法
|
// 操作方法
|
||||||
getVideoSegmentList,
|
getVideoSegmentList,
|
||||||
regenerateVideoSegment,
|
regenerateVideoSegment,
|
||||||
optimizeVideoContent,
|
optimizeVideoContent,
|
||||||
abortOperation,
|
abortOperation,
|
||||||
setSelectedSegment: setSelectedSegmentHandler,
|
setSelectedSegment: setSelectedSegmentHandler,
|
||||||
clearError,
|
addNewLens,
|
||||||
|
deleteLens,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -115,7 +115,9 @@ export class StoryDetails {
|
|||||||
* @param text 剧本文本
|
* @param text 剧本文本
|
||||||
*/
|
*/
|
||||||
updateScript(text: string) {
|
updateScript(text: string) {
|
||||||
|
console.log('text.length', text.length)
|
||||||
const scriptObject = this.createFromText(text);
|
const scriptObject = this.createFromText(text);
|
||||||
|
console.log('scriptObject', scriptObject)
|
||||||
this.setProperties(scriptObject);
|
this.setProperties(scriptObject);
|
||||||
return scriptObject;
|
return scriptObject;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1,13 +0,0 @@
|
|||||||
import { useVideoSegmentService } from '../Interaction/ShotService';
|
|
||||||
|
|
||||||
describe('VideoSegmentService 测试', () => {
|
|
||||||
it('初始化服务实例', () => {
|
|
||||||
const videoSegmentService = useVideoSegmentService();
|
|
||||||
expect(videoSegmentService).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('获取视频片段列表', () => {
|
|
||||||
const videoSegmentService = useVideoSegmentService();
|
|
||||||
expect(typeof videoSegmentService.fetchVideoSegmentList).toBe('function');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -150,7 +150,7 @@ export const ScriptRenderer: React.FC<ScriptRendererProps> = ({ data, setIsPause
|
|||||||
onChange={handleBlockTextChange(block)}
|
onChange={handleBlockTextChange(block)}
|
||||||
onBlur={handleBlockTextBlur(block)}
|
onBlur={handleBlockTextBlur(block)}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
className="block w-full min-h-[120px] bg-white/5 backdrop-blur-md p-4 text-white/90
|
className="block w-full min-h-[120px] bg-white/5 backdrop-blur-md p-4 text-white/90
|
||||||
rounded-lg border-unset outline-none pb-12
|
rounded-lg border-unset outline-none pb-12
|
||||||
whitespace-pre-wrap break-words"
|
whitespace-pre-wrap break-words"
|
||||||
placeholder=""
|
placeholder=""
|
||||||
@ -166,7 +166,7 @@ export const ScriptRenderer: React.FC<ScriptRendererProps> = ({ data, setIsPause
|
|||||||
{addThemeTag.map((item, index) => (
|
{addThemeTag.map((item, index) => (
|
||||||
<div key={index} className={`flex items-center gap-1 px-2 rounded-full ${Object.values(ThemeTagBgColor)[index]}`}>
|
<div key={index} className={`flex items-center gap-1 px-2 rounded-full ${Object.values(ThemeTagBgColor)[index]}`}>
|
||||||
<span className={`text-sm px-2 py-1 rounded-md`}>{item}</span>
|
<span className={`text-sm px-2 py-1 rounded-md`}>{item}</span>
|
||||||
<X className="w-4 h-4 cursor-pointer text-blue-500/80" onClick={() =>
|
<X className="w-4 h-4 cursor-pointer text-blue-500/80" onClick={() =>
|
||||||
handleThemeTagChange(addThemeTag.filter(v => v !== item))
|
handleThemeTagChange(addThemeTag.filter(v => v !== item))
|
||||||
} />
|
} />
|
||||||
</div>
|
</div>
|
||||||
@ -234,11 +234,11 @@ export const ScriptRenderer: React.FC<ScriptRendererProps> = ({ data, setIsPause
|
|||||||
const isHovered = hoveredBlockId === block.id;
|
const isHovered = hoveredBlockId === block.id;
|
||||||
const isActive = activeBlockId === block.id;
|
const isActive = activeBlockId === block.id;
|
||||||
const isEditing = editBlockId === block.id;
|
const isEditing = editBlockId === block.id;
|
||||||
|
console.log('block', block)
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={block.id}
|
key={block.id}
|
||||||
className={`relative p-4 mb-1 rounded-lg shadow-md transition-colors duration-300
|
className={`relative p-4 mb-1 rounded-lg shadow-md transition-colors duration-300
|
||||||
${isActive ? 'bg-slate-700/50' : ''} hover:bg-slate-700/30`}
|
${isActive ? 'bg-slate-700/50' : ''} hover:bg-slate-700/30`}
|
||||||
ref={(el) => (contentRefs.current[block.id] = el)}
|
ref={(el) => (contentRefs.current[block.id] = el)}
|
||||||
onMouseEnter={() => setHoveredBlockId(block.id)}
|
onMouseEnter={() => setHoveredBlockId(block.id)}
|
||||||
@ -303,4 +303,4 @@ export const ScriptRenderer: React.FC<ScriptRendererProps> = ({ data, setIsPause
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user