forked from 77media/video-flow
新增批量更新视频片段状态和视频地址接口,更新获取角色场景列表接口,重构角色视频片段服务逻辑以支持新接口,优化视频片段状态处理和定时获取逻辑。
This commit is contained in:
parent
4fccce896e
commit
99ea9a2c0a
@ -880,3 +880,67 @@ export const generateCharacterDescription = async (request: {
|
||||
}>> => {
|
||||
return post("/character/generate_description", request);
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量更新视频片段状态和视频地址接口
|
||||
* @param request - 批量更新视频片段请求参数
|
||||
* @returns Promise<ApiResponse<更新结果>>
|
||||
*/
|
||||
export const batchUpdateVideoSegments = async (request: {
|
||||
/** 项目ID */
|
||||
project_id: string;
|
||||
/** 要更新的视频片段列表 */
|
||||
segments: Array<{
|
||||
/** 视频片段ID */
|
||||
shot_id: string;
|
||||
/** 新的视频地址列表 */
|
||||
video_urls: string[];
|
||||
/** 新的状态 0:视频加载中 1:任务已完成 2:任务失败 */
|
||||
status: 0 | 1 | 2;
|
||||
/** 优化后的描述文本 */
|
||||
optimized_description?: string;
|
||||
/** 关键词列表 */
|
||||
keywords?: string[];
|
||||
}>;
|
||||
}): Promise<ApiResponse<{
|
||||
/** 更新成功的片段数量 */
|
||||
success_count: number;
|
||||
/** 更新失败的片段数量 */
|
||||
failed_count: number;
|
||||
/** 更新结果详情 */
|
||||
results: Array<{
|
||||
shot_id: string;
|
||||
success: boolean;
|
||||
message?: string;
|
||||
}>;
|
||||
}>> => {
|
||||
return post("/movie/batch_update_video_segments", request);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取角色在项目中的场景列表接口
|
||||
* @param request - 获取角色场景请求参数
|
||||
* @returns Promise<ApiResponse<角色场景列表>>
|
||||
*/
|
||||
export const getCharacterScenes = async (request: {
|
||||
/** 项目ID */
|
||||
project_id: string;
|
||||
/** 角色名称或ID */
|
||||
character_name: string;
|
||||
}): Promise<ApiResponse<{
|
||||
/** 角色名称 */
|
||||
character_name: string;
|
||||
/** 项目ID */
|
||||
project_id: string;
|
||||
/** 场景列表 */
|
||||
scenes: Array<{
|
||||
/** 视频ID */
|
||||
video_id: string;
|
||||
/** 视频URL列表 */
|
||||
video_urls: string[];
|
||||
}>;
|
||||
/** 总数量 */
|
||||
total_count: number;
|
||||
}>> => {
|
||||
return post("/character/get_character_scenes", request);
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useState, useCallback, useMemo } from 'react';
|
||||
import { VideoSegmentEntity } from '../domain/Entities';
|
||||
import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
|
||||
import { getRoleShots, applyRoleToShots } from '@/api/video_flow';
|
||||
import { getCharacterScenes } from '@/api/video_flow';
|
||||
|
||||
/**
|
||||
* 扩展的视频片段实体,包含选择状态
|
||||
@ -9,7 +9,7 @@ import { getRoleShots, applyRoleToShots } from '@/api/video_flow';
|
||||
interface ExtendedVideoSegmentEntity extends VideoSegmentEntity {
|
||||
/** 是否已选中 */
|
||||
selected?: boolean;
|
||||
/** 是否已应用角色 */
|
||||
/** 是否已应用 */
|
||||
applied?: boolean;
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ interface UseRoleShotService {
|
||||
* 角色视频片段服务Hook
|
||||
* 提供角色与视频片段交互的所有响应式功能和业务逻辑
|
||||
*/
|
||||
export const useRoleShotServiceHook = (): UseRoleShotService => {
|
||||
export const useRoleShotServiceHook = (projectId: string): UseRoleShotService => {
|
||||
// 响应式状态
|
||||
const [shotSelectionList, setShotSelectionList] = useState<ExtendedVideoSegmentEntity[]>([]);
|
||||
const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null);
|
||||
@ -77,14 +77,28 @@ export const useRoleShotServiceHook = (): UseRoleShotService => {
|
||||
*/
|
||||
const fetchRoleShots = useCallback(async (roleId: string) => {
|
||||
try {
|
||||
const response = await getRoleShots({ roleId });
|
||||
if (response.successful) {
|
||||
const { shots, appliedShotIds } = response.data;
|
||||
// 调用新的真实接口获取角色在项目中的场景列表
|
||||
const response = await getCharacterScenes({
|
||||
project_id: projectId,
|
||||
character_name: roleId
|
||||
});
|
||||
|
||||
const extendedShots: ExtendedVideoSegmentEntity[] = shots.map(shot => ({
|
||||
...shot,
|
||||
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: appliedShotIds.includes(shot.id)
|
||||
applied: true // 由于是通过角色查询到的,所以都是已应用的
|
||||
}));
|
||||
|
||||
setShotSelectionList(extendedShots);
|
||||
@ -94,13 +108,13 @@ export const useRoleShotServiceHook = (): UseRoleShotService => {
|
||||
const newRoleEditUseCase = new RoleEditUseCase();
|
||||
setRoleEditUseCase(newRoleEditUseCase);
|
||||
} else {
|
||||
throw new Error(`获取角色分镜列表失败: ${response.message}`);
|
||||
throw new Error(`获取角色场景列表失败: ${response.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取角色分镜列表失败:', error);
|
||||
console.error('获取角色场景列表失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}, []);
|
||||
}, [projectId]);
|
||||
|
||||
/**
|
||||
* 切换全选与全不选
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { useState, useCallback, useEffect } from "react";
|
||||
import { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase";
|
||||
import { VideoSegmentEntity } from "../domain/Entities";
|
||||
import { LensType, SimpleCharacter } from "../domain/valueObject";
|
||||
@ -57,6 +57,8 @@ export const useShotService = (): UseShotService => {
|
||||
const [selectedSegment, setSelectedSegment] = useState<VideoSegmentEntity | null>(null);
|
||||
const [projectId, setProjectId] = useState<string>("");
|
||||
const [simpleCharacter, setSimpleCharacter] = useState<SimpleCharacter[]>([]);
|
||||
// 轮询任务ID
|
||||
const [intervalId, setIntervalId] = useState<NodeJS.Timeout | null>(null);
|
||||
// UseCase实例
|
||||
const [vidoEditUseCase] = useState<VideoSegmentEditUseCase>(
|
||||
new VideoSegmentEditUseCase()
|
||||
@ -76,6 +78,7 @@ export const useShotService = (): UseShotService => {
|
||||
console.log('segments', segments);
|
||||
|
||||
setVideoSegments(segments);
|
||||
setIntervalIdHandler();
|
||||
} catch (error) {
|
||||
console.error("获取视频片段列表失败:", error);
|
||||
} finally {
|
||||
@ -85,6 +88,59 @@ export const useShotService = (): UseShotService => {
|
||||
[vidoEditUseCase]
|
||||
);
|
||||
|
||||
const setIntervalIdHandler = async (): Promise<void> => {
|
||||
// 每次执行前先清除之前的定时器,确保只存在一个定时器
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
setIntervalId(null);
|
||||
}
|
||||
// 定义定时任务,每5秒执行一次
|
||||
const newIntervalId = setInterval(async () => {
|
||||
try {
|
||||
const segments = await vidoEditUseCase.getVideoSegmentList(projectId);
|
||||
|
||||
setVideoSegments(prevSegments => {
|
||||
const existingSegmentsMap = new Map(prevSegments.map(segment => [segment.id, segment]));
|
||||
const segmentsToUpdate = segments.filter(segment => segment.id !== selectedSegment?.id);
|
||||
|
||||
segmentsToUpdate.forEach(newSegment => {
|
||||
const existingSegment = existingSegmentsMap.get(newSegment.id);
|
||||
|
||||
if (existingSegment) {
|
||||
existingSegmentsMap.set(newSegment.id, {
|
||||
...existingSegment,
|
||||
videoUrl: newSegment.videoUrl,
|
||||
status: newSegment.status,
|
||||
sketchUrl: newSegment.sketchUrl,
|
||||
lens: newSegment.lens,
|
||||
updatedAt: newSegment.updatedAt,
|
||||
loadingProgress: newSegment.loadingProgress
|
||||
});
|
||||
} else {
|
||||
existingSegmentsMap.set(newSegment.id, newSegment);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(existingSegmentsMap.values());
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('定时获取视频片段列表失败:', error);
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
setIntervalId(newIntervalId);
|
||||
};
|
||||
|
||||
// 组件卸载时清理定时器
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
setIntervalId(null);
|
||||
}
|
||||
};
|
||||
}, [intervalId]);
|
||||
|
||||
/**
|
||||
* 重新生成视频片段
|
||||
* @param shotPrompt 镜头描述数据
|
||||
@ -324,6 +380,6 @@ export const useShotService = (): UseShotService => {
|
||||
addNewLens,
|
||||
deleteLens,
|
||||
filterRole,
|
||||
setSimpleCharacter
|
||||
setSimpleCharacter,
|
||||
};
|
||||
};
|
||||
|
||||
@ -136,15 +136,18 @@ export class VideoSegmentEntityAdapter {
|
||||
videoUrls.push(...result.videos.map(video => video.video_url));
|
||||
}
|
||||
|
||||
// // 根据task_status确定状态
|
||||
// 根据task_status和videoUrls内容确定状态
|
||||
let status: 0 | 1 | 2 = 1; // 默认为已完成状态
|
||||
// if (data.task_status === "INIT" || data.task_status === "IN_PROGRESS") {
|
||||
// status = 0; // 视频加载中(INIT和IN_PROGRESS都当成进行中)
|
||||
// } else if (data.task_status === "COMPLETED") {
|
||||
// status = 1; // 任务已完成
|
||||
// } else if (data.task_status === "FAILED") {
|
||||
// status = 2; // 任务失败
|
||||
// }
|
||||
if (data.task_status === "INIT" || data.task_status === "IN_PROGRESS") {
|
||||
// 进行中:videoUrls[0]有内容为已完成,否则为处理中
|
||||
status = videoUrls[0] ? 1 : 0;
|
||||
} else if (data.task_status === "COMPLETED") {
|
||||
// 已完成:videoUrls[0]有内容为已完成,否则为失败
|
||||
status = videoUrls[0] ? 1 : 2;
|
||||
} else if (data.task_status === "FAILED") {
|
||||
// 失败:全部为失败
|
||||
status = 2;
|
||||
}
|
||||
|
||||
// 创建VideoSegmentEntity
|
||||
const entity: VideoSegmentEntity = {
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
updateShotPrompt,
|
||||
detailScriptEpisodeNew,
|
||||
faceRecognition,
|
||||
batchUpdateVideoSegments,
|
||||
} from "@/api/video_flow";
|
||||
|
||||
/**
|
||||
@ -228,6 +229,64 @@ export class VideoSegmentEditUseCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 批量更新除当前选中片段外的所有视频片段
|
||||
* @param projectId 项目ID
|
||||
* @param currentSegmentId 当前选中的视频片段ID
|
||||
* @param optimizedDescription 优化后的描述文本
|
||||
* @param keywords 关键词列表
|
||||
* @returns Promise<boolean> 更新是否成功
|
||||
*/
|
||||
async batchUpdateOtherVideoSegments(
|
||||
projectId: string,
|
||||
currentSegmentId: string,
|
||||
optimizedDescription: string,
|
||||
keywords: string[]
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
this.loading = true;
|
||||
|
||||
// 获取当前项目的视频片段列表
|
||||
const segments = await this.getVideoSegmentList(projectId);
|
||||
|
||||
// 过滤出除当前选中片段外的所有片段
|
||||
const otherSegments = segments.filter(segment => segment.id !== currentSegmentId);
|
||||
|
||||
if (otherSegments.length === 0) {
|
||||
console.log('没有其他视频片段需要更新');
|
||||
return true;
|
||||
}
|
||||
|
||||
// 准备更新数据
|
||||
const updateData = otherSegments.map(segment => ({
|
||||
shot_id: segment.id,
|
||||
video_urls: segment.videoUrl || [],
|
||||
status: segment.status,
|
||||
optimized_description: optimizedDescription,
|
||||
keywords: keywords
|
||||
}));
|
||||
|
||||
// 调用批量更新API
|
||||
const response = await batchUpdateVideoSegments({
|
||||
project_id: projectId,
|
||||
segments: updateData
|
||||
});
|
||||
|
||||
if (!response.successful) {
|
||||
throw new Error(response.message || '批量更新视频片段失败');
|
||||
}
|
||||
|
||||
console.log(`成功更新 ${response.data.success_count} 个视频片段,失败 ${response.data.failed_count} 个`);
|
||||
|
||||
return response.data.failed_count === 0;
|
||||
} catch (error) {
|
||||
console.error('批量更新视频片段失败:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取加载状态
|
||||
* @returns boolean 是否正在加载
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user