新增批量更新视频片段状态和视频地址接口,更新获取角色场景列表接口,重构角色视频片段服务逻辑以支持新接口,优化视频片段状态处理和定时获取逻辑。

This commit is contained in:
海龙 2025-08-10 20:22:06 +08:00
parent 4fccce896e
commit 99ea9a2c0a
5 changed files with 218 additions and 22 deletions

View File

@ -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);
};

View File

@ -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]);
/**
*

View File

@ -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,
};
};

View File

@ -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 = {

View File

@ -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