forked from 77media/video-flow
326 lines
9.0 KiB
TypeScript
326 lines
9.0 KiB
TypeScript
import { useState, useCallback } from "react";
|
||
import { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase";
|
||
import { VideoSegmentEntity } from "../domain/Entities";
|
||
import { ContentItem, LensType } from "../domain/valueObject";
|
||
|
||
/**
|
||
* 视频片段服务Hook接口
|
||
* 定义视频片段服务Hook的所有状态和操作方法
|
||
*/
|
||
export interface UseShotService {
|
||
// 响应式状态
|
||
/** 加载状态 */
|
||
loading: boolean;
|
||
/** 视频片段列表 */
|
||
videoSegments: VideoSegmentEntity[];
|
||
/** 当前选中的视频片段 */
|
||
selectedSegment: VideoSegmentEntity | null;
|
||
/** 错误信息 */
|
||
error: string | null;
|
||
|
||
// 操作方法
|
||
/** 获取视频片段列表 */
|
||
getVideoSegmentList: (projectId: string) => Promise<void>;
|
||
/** 重新生成视频片段 */
|
||
regenerateVideoSegment: (
|
||
shotPrompt: LensType[],
|
||
shotId?: string,
|
||
roleReplaceParams?: { oldId: string; newId: string }[],
|
||
sceneReplaceParams?: { oldId: string; newId: string }[]
|
||
) => Promise<VideoSegmentEntity>;
|
||
/** AI优化视频内容 */
|
||
optimizeVideoContent: (
|
||
shotId: string,
|
||
userRequirement: string,
|
||
lensData: LensType[]
|
||
) => Promise<LensType[]>;
|
||
/** 更新视频内容 */
|
||
updateVideoContent: (
|
||
shotId: string,
|
||
content: Array<{ roleId: string; content: string }>
|
||
) => Promise<VideoSegmentEntity>;
|
||
/** 中断当前操作 */
|
||
abortOperation: () => void;
|
||
/** 设置选中的视频片段 */
|
||
setSelectedSegment: (segment: VideoSegmentEntity | null) => void;
|
||
/** 清除错误信息 */
|
||
clearError: () => void;
|
||
}
|
||
|
||
/**
|
||
* 视频片段服务Hook
|
||
* 提供视频片段相关的所有状态管理和操作方法
|
||
* 包括获取视频列表、重新生成视频、AI优化等功能
|
||
*/
|
||
export const useShotService = (): UseShotService => {
|
||
// 响应式状态
|
||
const [loading, setLoading] = useState<boolean>(false);
|
||
const [videoSegments, setVideoSegments] = useState<VideoSegmentEntity[]>([]);
|
||
const [selectedSegment, setSelectedSegment] = useState<VideoSegmentEntity | null>(null);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
// UseCase实例
|
||
const [shotEditUseCase] = useState<VideoSegmentEditUseCase>(
|
||
new VideoSegmentEditUseCase()
|
||
);
|
||
|
||
/**
|
||
* 获取视频片段列表
|
||
* @param projectId 项目ID
|
||
*/
|
||
const getVideoSegmentList = useCallback(
|
||
async (projectId: string): Promise<void> => {
|
||
try {
|
||
setLoading(true);
|
||
setError(null);
|
||
|
||
const segments = await shotEditUseCase.getVideoSegmentList(projectId);
|
||
setVideoSegments(segments);
|
||
} catch (error) {
|
||
const errorMessage = error instanceof Error ? error.message : "获取视频片段列表失败";
|
||
setError(errorMessage);
|
||
console.error("获取视频片段列表失败:", error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
},
|
||
[shotEditUseCase]
|
||
);
|
||
|
||
/**
|
||
* 重新生成视频片段
|
||
* @param shotPrompt 镜头描述数据
|
||
* @param shotId 视频片段ID(可选)
|
||
* @param roleReplaceParams 角色替换参数(可选)
|
||
* @param sceneReplaceParams 场景替换参数(可选)
|
||
* @returns Promise<VideoSegmentEntity> 重新生成的视频片段
|
||
*/
|
||
const regenerateVideoSegment = useCallback(
|
||
async (
|
||
shotPrompt: LensType[],
|
||
shotId?: string,
|
||
roleReplaceParams?: { oldId: string; newId: string }[],
|
||
sceneReplaceParams?: { oldId: string; newId: string }[]
|
||
): Promise<VideoSegmentEntity> => {
|
||
try {
|
||
setLoading(true);
|
||
setError(null);
|
||
|
||
const regeneratedSegment = await shotEditUseCase.regenerateVideoSegment(
|
||
shotPrompt,
|
||
shotId,
|
||
roleReplaceParams,
|
||
sceneReplaceParams
|
||
);
|
||
|
||
// 如果重新生成的是现有片段,更新列表中的对应项
|
||
if (shotId) {
|
||
setVideoSegments(prev =>
|
||
prev.map(segment =>
|
||
segment.id === shotId ? regeneratedSegment : segment
|
||
)
|
||
);
|
||
} else {
|
||
// 如果是新生成的片段,添加到列表中
|
||
setVideoSegments(prev => [...prev, regeneratedSegment]);
|
||
}
|
||
|
||
return regeneratedSegment;
|
||
} catch (error) {
|
||
const errorMessage = error instanceof Error ? error.message : "重新生成视频片段失败";
|
||
setError(errorMessage);
|
||
console.error("重新生成视频片段失败:", error);
|
||
throw error;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
},
|
||
[shotEditUseCase]
|
||
);
|
||
|
||
/**
|
||
* AI优化视频内容
|
||
* @param shotId 视频片段ID
|
||
* @param userRequirement 用户优化需求
|
||
* @param lensData 镜头数据数组
|
||
* @returns Promise<LensType[]> 优化后的镜头数据
|
||
*/
|
||
const optimizeVideoContent = useCallback(
|
||
async (
|
||
shotId: string,
|
||
userRequirement: string,
|
||
lensData: LensType[]
|
||
): Promise<LensType[]> => {
|
||
try {
|
||
setLoading(true);
|
||
setError(null);
|
||
|
||
const optimizedLensData = await shotEditUseCase.optimizeVideoContent(
|
||
shotId,
|
||
userRequirement,
|
||
lensData
|
||
);
|
||
|
||
// 注意:这里不再更新videoSegments状态,因为返回的是LensType[]而不是VideoSegmentEntity
|
||
// 调用者需要自己处理优化后的镜头数据
|
||
|
||
return optimizedLensData;
|
||
} catch (error) {
|
||
const errorMessage = error instanceof Error ? error.message : "AI优化视频内容失败";
|
||
setError(errorMessage);
|
||
console.error("AI优化视频内容失败:", error);
|
||
throw error;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
},
|
||
[shotEditUseCase]
|
||
);
|
||
|
||
/**
|
||
* 更新视频内容
|
||
* @param shotId 视频片段ID
|
||
* @param content 新的对话内容
|
||
* @returns Promise<VideoSegmentEntity> 更新后的视频片段
|
||
*/
|
||
const updateVideoContent = useCallback(
|
||
async (
|
||
shotId: string,
|
||
content: Array<{ roleId: string; content: string }>
|
||
): Promise<VideoSegmentEntity> => {
|
||
try {
|
||
setLoading(true);
|
||
setError(null);
|
||
|
||
const updatedSegment = await shotEditUseCase.updateVideoContent(
|
||
shotId,
|
||
content
|
||
);
|
||
|
||
// 更新列表中的对应项
|
||
setVideoSegments(prev =>
|
||
prev.map(segment =>
|
||
segment.id === shotId ? updatedSegment : segment
|
||
)
|
||
);
|
||
|
||
return updatedSegment;
|
||
} catch (error) {
|
||
const errorMessage = error instanceof Error ? error.message : "更新视频内容失败";
|
||
setError(errorMessage);
|
||
console.error("更新视频内容失败:", error);
|
||
throw error;
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
},
|
||
[shotEditUseCase]
|
||
);
|
||
|
||
/**
|
||
* 中断当前操作
|
||
*/
|
||
const abortOperation = useCallback((): void => {
|
||
shotEditUseCase.abortOperation();
|
||
setLoading(false);
|
||
}, [shotEditUseCase]);
|
||
|
||
/**
|
||
* 设置选中的视频片段
|
||
*/
|
||
const setSelectedSegmentHandler = useCallback((segment: VideoSegmentEntity | null): void => {
|
||
setSelectedSegment(segment);
|
||
}, []);
|
||
|
||
/**
|
||
* 清除错误信息
|
||
*/
|
||
const clearError = useCallback((): void => {
|
||
setError(null);
|
||
}, []);
|
||
|
||
return {
|
||
// 响应式状态
|
||
loading,
|
||
videoSegments,
|
||
selectedSegment,
|
||
error,
|
||
// 操作方法
|
||
getVideoSegmentList,
|
||
regenerateVideoSegment,
|
||
optimizeVideoContent,
|
||
updateVideoContent,
|
||
abortOperation,
|
||
setSelectedSegment: setSelectedSegmentHandler,
|
||
clearError,
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 使用示例:
|
||
*
|
||
* ```tsx
|
||
* import { useShotService } from '@/app/service/Interaction/ShotService';
|
||
*
|
||
* const VideoSegmentComponent = () => {
|
||
* const {
|
||
* loading,
|
||
* videoSegments,
|
||
* selectedSegment,
|
||
* error,
|
||
* getVideoSegmentList,
|
||
* regenerateVideoSegment,
|
||
* optimizeVideoContent,
|
||
* updateVideoContent,
|
||
* setSelectedSegment,
|
||
* clearError
|
||
* } = useShotService();
|
||
*
|
||
* // 获取视频片段列表
|
||
* useEffect(() => {
|
||
* getVideoSegmentList('project-id');
|
||
* }, [getVideoSegmentList]);
|
||
*
|
||
* // 重新生成视频片段
|
||
* const handleRegenerate = async () => {
|
||
* try {
|
||
* await regenerateVideoSegment([
|
||
* new LensType('特写', '镜头描述', '运镜')
|
||
* ]);
|
||
* } catch (error) {
|
||
* console.error('重新生成失败:', error);
|
||
* }
|
||
* };
|
||
*
|
||
* // AI优化镜头数据
|
||
* const handleOptimize = async () => {
|
||
* try {
|
||
* const optimizedLensData = await optimizeVideoContent(
|
||
* 'shot-id',
|
||
* '让镜头更加动态',
|
||
* [new LensType('特写', '当前镜头描述', '运镜')]
|
||
* );
|
||
*
|
||
* // 处理优化后的镜头数据
|
||
* console.log('优化后的镜头数据:', optimizedLensData);
|
||
* } catch (error) {
|
||
* console.error('优化失败:', error);
|
||
* }
|
||
* };
|
||
*
|
||
* return (
|
||
* <div>
|
||
* {loading && <div>加载中...</div>}
|
||
* {error && <div>错误: {error}</div>}
|
||
* {videoSegments.map(segment => (
|
||
* <div key={segment.id}>
|
||
* <h3>{segment.name}</h3>
|
||
* <p>状态: {segment.status}</p>
|
||
* </div>
|
||
* ))}
|
||
* </div>
|
||
* );
|
||
* };
|
||
* ```
|
||
*/
|