forked from 77media/video-flow
207 lines
6.3 KiB
TypeScript
207 lines
6.3 KiB
TypeScript
import { useState, useCallback, useMemo } from 'react';
|
|
import { VideoSegmentEntity } from '../domain/Entities';
|
|
import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
|
|
import { getCharacterScenes } from '@/api/video_flow';
|
|
|
|
/**
|
|
* 扩展的视频片段实体,包含选择状态
|
|
*/
|
|
interface ExtendedVideoSegmentEntity extends VideoSegmentEntity {
|
|
/** 是否已选中 */
|
|
selected?: boolean;
|
|
/** 是否已应用 */
|
|
applied?: boolean;
|
|
}
|
|
|
|
/**
|
|
* 角色视频片段服务Hook返回值接口
|
|
*/
|
|
interface UseRoleShotService {
|
|
// 响应式数据
|
|
/** 分镜选择列表 */
|
|
shotSelectionList: ExtendedVideoSegmentEntity[];
|
|
/** 是否全选分镜 */
|
|
isAllVideoSegmentSelected: boolean;
|
|
/** 已选中的分镜数量 */
|
|
selectedVideoSegmentCount: number;
|
|
/** 当前选中的角色ID */
|
|
selectedRoleId: string | null;
|
|
// 操作方法
|
|
/** 获取角色出现的分镜列表 */
|
|
fetchRoleShots: (roleId: string) => Promise<void>;
|
|
/** 切换全选与全不选 */
|
|
toggleSelectAllShots: () => void;
|
|
/** 选择/取消选择单个分镜 */
|
|
toggleShotSelection: (shotId: string) => void;
|
|
/** 应用角色到选中的分镜 */
|
|
applyRoleToSelectedShots: (roleId: string) => Promise<void>;
|
|
/** 清空选择列表 */
|
|
clearShotSelection: () => void;
|
|
}
|
|
|
|
/**
|
|
* 角色视频片段服务Hook
|
|
* 提供角色与视频片段交互的所有响应式功能和业务逻辑
|
|
*/
|
|
export const useRoleShotServiceHook = (projectId: string): UseRoleShotService => {
|
|
// 响应式状态
|
|
const [shotSelectionList, setShotSelectionList] = useState<ExtendedVideoSegmentEntity[]>([]);
|
|
const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null);
|
|
|
|
// UseCase实例
|
|
const [roleEditUseCase, setRoleEditUseCase] = useState<RoleEditUseCase | null>(null);
|
|
|
|
// 计算属性
|
|
/**
|
|
* 是否全选分镜
|
|
* @description 判断是否所有分镜都被选中
|
|
*/
|
|
const isAllVideoSegmentSelected = useMemo(() => {
|
|
return shotSelectionList.length > 0 && shotSelectionList.every(shot => shot.selected);
|
|
}, [shotSelectionList]);
|
|
|
|
/**
|
|
* 已选中的分镜数量
|
|
* @description 获取当前选中的分镜数量
|
|
*/
|
|
const selectedVideoSegmentCount = useMemo(() => {
|
|
return shotSelectionList.filter(shot => shot.selected).length;
|
|
}, [shotSelectionList]);
|
|
|
|
/**
|
|
* 获取角色出现的分镜列表
|
|
* @description 获取当前角色应用到的分镜列表,包括已应用状态
|
|
* @param roleId 角色ID
|
|
* @throws {Error} 当API调用失败时抛出错误
|
|
* @returns {Promise<void>} 获取完成后的Promise
|
|
*/
|
|
const fetchRoleShots = useCallback(async (roleId: string) => {
|
|
try {
|
|
// 调用新的真实接口获取角色在项目中的场景列表
|
|
const response = await getCharacterScenes({
|
|
project_id: projectId,
|
|
character_name: roleId
|
|
});
|
|
|
|
if (response.successful) {
|
|
const { scenes, total_count } = response.data;
|
|
|
|
// 将scenes数据转换为ExtendedVideoSegmentEntity[]数据结构
|
|
const extendedShots: ExtendedVideoSegmentEntity[] = scenes.map(scene => ({
|
|
id: scene.video_id,
|
|
name: `视频片段_${scene.video_id}`,
|
|
sketchUrl: "",
|
|
videoUrl: scene.video_urls, // 保持为string[]类型
|
|
status: 1, // 默认为已完成状态
|
|
lens: [],
|
|
updatedAt: Date.now(),
|
|
loadingProgress: 100,
|
|
disableEdit: false,
|
|
selected: false,
|
|
applied: true // 由于是通过角色查询到的,所以都是已应用的
|
|
}));
|
|
|
|
setShotSelectionList(extendedShots);
|
|
setSelectedRoleId(roleId);
|
|
|
|
// 初始化角色编辑UseCase实例
|
|
const newRoleEditUseCase = new RoleEditUseCase();
|
|
setRoleEditUseCase(newRoleEditUseCase);
|
|
} else {
|
|
throw new Error(`获取角色场景列表失败: ${response.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('获取角色场景列表失败:', error);
|
|
throw error;
|
|
}
|
|
}, [projectId]);
|
|
|
|
/**
|
|
* 切换全选与全不选
|
|
* @description 如果当前是全选状态则全不选,否则全选
|
|
*/
|
|
const toggleSelectAllShots = useCallback(() => {
|
|
setShotSelectionList(prev => {
|
|
const isAllSelected = prev.length > 0 && prev.every(shot => shot.selected);
|
|
return prev.map(shot => ({ ...shot, selected: !isAllSelected }));
|
|
});
|
|
}, []);
|
|
|
|
/**
|
|
* 选择/取消选择单个分镜
|
|
* @description 切换指定分镜的选择状态
|
|
* @param shotId 分镜ID
|
|
*/
|
|
const toggleShotSelection = useCallback((shotId: string) => {
|
|
setShotSelectionList(prev =>
|
|
prev.map(shot =>
|
|
shot.id === shotId
|
|
? { ...shot, selected: !shot.selected }
|
|
: shot
|
|
)
|
|
);
|
|
}, []);
|
|
|
|
/**
|
|
* 应用角色到选中的分镜
|
|
* @description 将当前角色应用到选中的分镜,并更新应用状态
|
|
* @param roleId 角色ID
|
|
* @throws {Error} 当未选择分镜或UseCase未初始化时抛出错误
|
|
* @returns {Promise<void>} 应用完成后的Promise
|
|
*/
|
|
const applyRoleToSelectedShots = useCallback(async (roleId: string) => {
|
|
if (!roleEditUseCase) {
|
|
throw new Error('角色编辑UseCase未初始化');
|
|
}
|
|
|
|
const selectedShotIds = shotSelectionList
|
|
.filter(shot => shot.selected)
|
|
.map(shot => shot.id);
|
|
|
|
if (selectedShotIds.length === 0) {
|
|
throw new Error('请先选择要应用的分镜');
|
|
}
|
|
|
|
try {
|
|
await roleEditUseCase.applyRole(selectedShotIds, roleId);
|
|
|
|
// 更新应用状态
|
|
setShotSelectionList(prev =>
|
|
prev.map(shot =>
|
|
selectedShotIds.includes(shot.id)
|
|
? { ...shot, applied: true, selected: false }
|
|
: shot
|
|
)
|
|
);
|
|
} catch (error) {
|
|
console.error('应用角色到分镜失败:', error);
|
|
throw error;
|
|
}
|
|
}, [roleEditUseCase, shotSelectionList]);
|
|
|
|
/**
|
|
* 清空选择列表
|
|
* @description 清空所有分镜的选择状态
|
|
*/
|
|
const clearShotSelection = useCallback(() => {
|
|
setShotSelectionList(prev =>
|
|
prev.map(shot => ({ ...shot, selected: false }))
|
|
);
|
|
}, []);
|
|
|
|
return {
|
|
// 响应式数据
|
|
shotSelectionList,
|
|
isAllVideoSegmentSelected,
|
|
selectedVideoSegmentCount,
|
|
selectedRoleId,
|
|
|
|
// 操作方法
|
|
fetchRoleShots,
|
|
toggleSelectAllShots,
|
|
toggleShotSelection,
|
|
applyRoleToSelectedShots,
|
|
clearShotSelection,
|
|
};
|
|
};
|