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);
|
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 { useState, useCallback, useMemo } from 'react';
|
||||||
import { VideoSegmentEntity } from '../domain/Entities';
|
import { VideoSegmentEntity } from '../domain/Entities';
|
||||||
import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
|
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 {
|
interface ExtendedVideoSegmentEntity extends VideoSegmentEntity {
|
||||||
/** 是否已选中 */
|
/** 是否已选中 */
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
/** 是否已应用角色 */
|
/** 是否已应用 */
|
||||||
applied?: boolean;
|
applied?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ interface UseRoleShotService {
|
|||||||
* 角色视频片段服务Hook
|
* 角色视频片段服务Hook
|
||||||
* 提供角色与视频片段交互的所有响应式功能和业务逻辑
|
* 提供角色与视频片段交互的所有响应式功能和业务逻辑
|
||||||
*/
|
*/
|
||||||
export const useRoleShotServiceHook = (): UseRoleShotService => {
|
export const useRoleShotServiceHook = (projectId: string): UseRoleShotService => {
|
||||||
// 响应式状态
|
// 响应式状态
|
||||||
const [shotSelectionList, setShotSelectionList] = useState<ExtendedVideoSegmentEntity[]>([]);
|
const [shotSelectionList, setShotSelectionList] = useState<ExtendedVideoSegmentEntity[]>([]);
|
||||||
const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null);
|
const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null);
|
||||||
@ -77,14 +77,28 @@ export const useRoleShotServiceHook = (): UseRoleShotService => {
|
|||||||
*/
|
*/
|
||||||
const fetchRoleShots = useCallback(async (roleId: string) => {
|
const fetchRoleShots = useCallback(async (roleId: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await getRoleShots({ roleId });
|
// 调用新的真实接口获取角色在项目中的场景列表
|
||||||
if (response.successful) {
|
const response = await getCharacterScenes({
|
||||||
const { shots, appliedShotIds } = response.data;
|
project_id: projectId,
|
||||||
|
character_name: roleId
|
||||||
|
});
|
||||||
|
|
||||||
const extendedShots: ExtendedVideoSegmentEntity[] = shots.map(shot => ({
|
if (response.successful) {
|
||||||
...shot,
|
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,
|
selected: false,
|
||||||
applied: appliedShotIds.includes(shot.id)
|
applied: true // 由于是通过角色查询到的,所以都是已应用的
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setShotSelectionList(extendedShots);
|
setShotSelectionList(extendedShots);
|
||||||
@ -94,13 +108,13 @@ export const useRoleShotServiceHook = (): UseRoleShotService => {
|
|||||||
const newRoleEditUseCase = new RoleEditUseCase();
|
const newRoleEditUseCase = new RoleEditUseCase();
|
||||||
setRoleEditUseCase(newRoleEditUseCase);
|
setRoleEditUseCase(newRoleEditUseCase);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`获取角色分镜列表失败: ${response.message}`);
|
throw new Error(`获取角色场景列表失败: ${response.message}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取角色分镜列表失败:', error);
|
console.error('获取角色场景列表失败:', error);
|
||||||
throw 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 { VideoSegmentEditUseCase } from "../usecase/ShotEditUsecase";
|
||||||
import { VideoSegmentEntity } from "../domain/Entities";
|
import { VideoSegmentEntity } from "../domain/Entities";
|
||||||
import { LensType, SimpleCharacter } from "../domain/valueObject";
|
import { LensType, SimpleCharacter } from "../domain/valueObject";
|
||||||
@ -57,6 +57,8 @@ export const useShotService = (): UseShotService => {
|
|||||||
const [selectedSegment, setSelectedSegment] = useState<VideoSegmentEntity | null>(null);
|
const [selectedSegment, setSelectedSegment] = useState<VideoSegmentEntity | null>(null);
|
||||||
const [projectId, setProjectId] = useState<string>("");
|
const [projectId, setProjectId] = useState<string>("");
|
||||||
const [simpleCharacter, setSimpleCharacter] = useState<SimpleCharacter[]>([]);
|
const [simpleCharacter, setSimpleCharacter] = useState<SimpleCharacter[]>([]);
|
||||||
|
// 轮询任务ID
|
||||||
|
const [intervalId, setIntervalId] = useState<NodeJS.Timeout | null>(null);
|
||||||
// UseCase实例
|
// UseCase实例
|
||||||
const [vidoEditUseCase] = useState<VideoSegmentEditUseCase>(
|
const [vidoEditUseCase] = useState<VideoSegmentEditUseCase>(
|
||||||
new VideoSegmentEditUseCase()
|
new VideoSegmentEditUseCase()
|
||||||
@ -76,6 +78,7 @@ export const useShotService = (): UseShotService => {
|
|||||||
console.log('segments', segments);
|
console.log('segments', segments);
|
||||||
|
|
||||||
setVideoSegments(segments);
|
setVideoSegments(segments);
|
||||||
|
setIntervalIdHandler();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取视频片段列表失败:", error);
|
console.error("获取视频片段列表失败:", error);
|
||||||
} finally {
|
} finally {
|
||||||
@ -85,6 +88,59 @@ export const useShotService = (): UseShotService => {
|
|||||||
[vidoEditUseCase]
|
[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 镜头描述数据
|
* @param shotPrompt 镜头描述数据
|
||||||
@ -324,6 +380,6 @@ export const useShotService = (): UseShotService => {
|
|||||||
addNewLens,
|
addNewLens,
|
||||||
deleteLens,
|
deleteLens,
|
||||||
filterRole,
|
filterRole,
|
||||||
setSimpleCharacter
|
setSimpleCharacter,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -136,15 +136,18 @@ export class VideoSegmentEntityAdapter {
|
|||||||
videoUrls.push(...result.videos.map(video => video.video_url));
|
videoUrls.push(...result.videos.map(video => video.video_url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 根据task_status确定状态
|
// 根据task_status和videoUrls内容确定状态
|
||||||
let status: 0 | 1 | 2 = 1; // 默认为已完成状态
|
let status: 0 | 1 | 2 = 1; // 默认为已完成状态
|
||||||
// if (data.task_status === "INIT" || data.task_status === "IN_PROGRESS") {
|
if (data.task_status === "INIT" || data.task_status === "IN_PROGRESS") {
|
||||||
// status = 0; // 视频加载中(INIT和IN_PROGRESS都当成进行中)
|
// 进行中:videoUrls[0]有内容为已完成,否则为处理中
|
||||||
// } else if (data.task_status === "COMPLETED") {
|
status = videoUrls[0] ? 1 : 0;
|
||||||
// status = 1; // 任务已完成
|
} else if (data.task_status === "COMPLETED") {
|
||||||
// } else if (data.task_status === "FAILED") {
|
// 已完成:videoUrls[0]有内容为已完成,否则为失败
|
||||||
// status = 2; // 任务失败
|
status = videoUrls[0] ? 1 : 2;
|
||||||
// }
|
} else if (data.task_status === "FAILED") {
|
||||||
|
// 失败:全部为失败
|
||||||
|
status = 2;
|
||||||
|
}
|
||||||
|
|
||||||
// 创建VideoSegmentEntity
|
// 创建VideoSegmentEntity
|
||||||
const entity: VideoSegmentEntity = {
|
const entity: VideoSegmentEntity = {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
updateShotPrompt,
|
updateShotPrompt,
|
||||||
detailScriptEpisodeNew,
|
detailScriptEpisodeNew,
|
||||||
faceRecognition,
|
faceRecognition,
|
||||||
|
batchUpdateVideoSegments,
|
||||||
} from "@/api/video_flow";
|
} 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 获取加载状态
|
* @description 获取加载状态
|
||||||
* @returns boolean 是否正在加载
|
* @returns boolean 是否正在加载
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user