video-flow-b/api/video_flow.ts

745 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { post, streamJsonPost } from "./request";
import { ProjectTypeEnum } from "@/app/model/enums";
import { ApiResponse } from "@/api/common";
import { BASE_URL } from "./constants";
import {
AITextEntity,
RoleEntity,
SceneEntity,
VideoSegmentEntity,
} from "@/app/service/domain/Entities";
import { TagValueObject } from "@/app/service/domain/valueObject";
import {
ContentItem,
LensType,
ScriptSlice,
} from "@/app/service/domain/valueObject";
// API 响应类型
interface BaseApiResponse<T> {
code: number;
successful: boolean;
message: string;
data: T;
}
// 剧集详情数据类型
interface EpisodeDetail {
project_id: string;
name: string;
status: "running" | "completed";
step: "sketch" | "character" | "video" | "music" | "final_video";
last_message: string;
data: TaskData | null;
mode: "auto" | "manual";
resolution: "1080p" | "4k";
}
// 任务数据类型
interface TaskData {
sketch?: Array<{
url: string;
script: string;
bg_rgb: string[];
}>;
roles?: Array<{
name: string;
url: string;
sound: string;
soundDescription: string;
roleDescription: string;
}>;
videos?: Array<{
url: string;
script: string;
audio: string;
}>;
music?: Array<{
url: string;
script: string;
name: string;
duration: string;
totalDuration: string;
isLooped: boolean;
}>;
final?: {
url: string;
};
}
// 流式数据类型
export interface StreamData<T = any> {
category: "sketch" | "character" | "video" | "music" | "final_video";
message: string;
data: T;
status: "running" | "completed";
total?: number;
completed?: number;
all_completed?: boolean;
}
// 场景/分镜头数据结构
export interface Scene {
id: string;
name: string;
description: string;
plot: string;
dialogue: string;
narration: string;
imageUrl?: string;
}
// 角色数据结构
export interface Character {
name: string;
desc: string;
}
// 剧本到分镜头提示词模型
export interface ScenePrompts {
scenes: Scene[]; // 分场景列表
characters?: Character[]; // 角色列表
summary?: string; // 剧情概要
scene?: string; // 场景描述
atmosphere?: string; // 氛围描述
episode_id?: number; // 剧集ID
total_shots?: string; // 总镜头数
}
// 剧本转分镜头请求接口
export interface ScriptToSceneRequest {
script: string;
episode_id: number;
script_id: number;
project_type: ProjectTypeEnum.SCRIPT_TO_VIDEO;
}
// 视频转分镜头请求接口
export interface VideoToSceneRequest {
video_url: string;
episode_id: number;
script_id: number;
project_type: ProjectTypeEnum.VIDEO_TO_VIDEO;
}
// 转换分镜头请求类型
export type ConvertScenePromptRequest =
| ScriptToSceneRequest
| VideoToSceneRequest;
// 转换分镜头响应接口
export type ConvertScenePromptResponse = BaseApiResponse<ScenePrompts>;
/**
* 将剧本或视频转换为分镜头提示词
* @param request - 请求参数,根据 project_type 自动判断是剧本还是视频模式
* @returns Promise<ConvertScenePromptResponse>
*/
export const convertScenePrompt = async (
request: ConvertScenePromptRequest
): Promise<ConvertScenePromptResponse> => {
// return post<ConvertScenePromptResponse>('/video_flow/convert-scene-prompts', request);
return new Promise((resolve) => {
setTimeout(() => {
resolve({
code: 0,
message: "success",
data: {
scenes: [],
characters: [],
summary: "",
scene: "",
atmosphere: "",
episode_id: 0,
total_shots: "",
},
successful: true,
});
}, 0);
});
};
/**
* 剧本转分镜头提示词
* @param script - 剧本内容
* @returns Promise<ConvertScenePromptResponse>
*/
export const convertScriptToScene = async (
script: string,
episode_id: number,
script_id: number
): Promise<ConvertScenePromptResponse> => {
return convertScenePrompt({
script,
episode_id,
script_id,
project_type: ProjectTypeEnum.SCRIPT_TO_VIDEO,
});
};
/**
* 视频转分镜头提示词
* @param video_url - 视频链接
* @returns Promise<ConvertScenePromptResponse>
*/
export const convertVideoToScene = async (
video_url: string,
episode_id: number,
script_id: number
): Promise<ConvertScenePromptResponse> => {
return convertScenePrompt({
video_url,
episode_id,
script_id,
project_type: ProjectTypeEnum.VIDEO_TO_VIDEO,
});
};
// 新-获取剧集详情
export const detailScriptEpisodeNew = async (data: {
project_id: string;
}): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>("/movie/get_movie_project_detail", data);
};
// 获取 title 接口
export const getScriptTitle = async (data: {
project_id: string;
}): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>("/movie/get_movie_project_description", data);
};
// 获取 数据 全量(需轮询)
export const getRunningStreamData = async (data: {
project_id: string;
}): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>("/movie/get_status", data);
};
// 获取 脚本 接口
export const getScriptTags = async (data: {
project_id: string;
}): Promise<any> => {
return post<any>("/movie/text_to_script_tags", data);
};
// 获取 loading-场景 接口
export const getSceneJson = async (data: {
project_id: string;
}): Promise<ApiResponse<any>> => {
return post<any>("/movie/scene_json", data);
};
// 获取 loading-分镜 接口
export const getShotSketchJson = async (data: {
project_id: string;
}): Promise<ApiResponse<any>> => {
return post<any>("/movie/shot_sketch_json", data);
};
// 获取 loading-视频 接口
export const getVideoJson = async (data: {
project_id: string;
}): Promise<ApiResponse<any>> => {
return post<any>("/movie/video_json", data);
};
/**
* 重新生成角色
* @param request - 重新生成角色请求参数
* @returns Promise<ApiResponse<RoleEntity>>
*/
export const regenerateRole = async (request: {
/** 角色提示词 */
prompt: string;
/** 标签列表 */
tagTypes: TagValueObject[];
/** 角色ID可选如果重新生成现有角色 */
roleId?: string;
}): Promise<ApiResponse<RoleEntity>> => {
return post<ApiResponse<any>>("/movie/regenerate_role", request);
};
/**
* 应用角色到分镜
* @param request - 应用角色请求参数
* @returns Promise<ApiResponse<应用结果>>
*/
export const applyRoleToShots = async (request: {
/** 角色ID */
roleId: string;
/** 分镜ID列表 */
shotIds: string[];
}): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>("/movie/apply_role_to_shots", request);
};
/**
* 获取角色应用到的分镜列表
* @param request - 获取角色分镜列表请求参数
* @returns Promise<ApiResponse<分镜列表>>
*/
export const getRoleShots = async (request: {
/** 角色ID */
roleId: string;
}): Promise<
ApiResponse<{
/** 分镜列表 */
shots: VideoSegmentEntity[];
/** 已应用的分镜ID列表 */
appliedShotIds: string[];
}>
> => {
return post<ApiResponse<any>>("/movie/get_role_shots", request);
};
/**
* 获取角色列表
* @param request - 获取角色列表请求参数
* @returns Promise<ApiResponse<角色实体列表>>
*/
export const getRoleList = async (request: {
/** 项目ID */
projectId: string;
}): Promise<ApiResponse<RoleEntity[]>> => {
return post<ApiResponse<any>>("/movie/get_role_list", request);
};
/**
* 获取角色数据
* @param request - 获取角色数据请求参数
* @returns Promise<ApiResponse<{ AI文本数据, 标签列表 }>>
*/
export const getRoleData = async (request: {
/** 角色ID */
roleId: string;
}): Promise<
ApiResponse<{
/** AI文本数据 */
text: AITextEntity;
/** 标签列表 */
tags: TagValueObject[];
}>
> => {
return post<ApiResponse<any>>("/movie/get_role_data", request);
};
/**
* 获取用户角色库
* @returns Promise<ApiResponse<角色实体列表>>
*/
export const getUserRoleLibrary = async (): Promise<
ApiResponse<RoleEntity[]>
> => {
return post<ApiResponse<any>>("/movie/get_user_role_library", {});
};
/**
* 替换角色
* @param request - 替换角色请求参数
* @returns Promise<ApiResponse<替换结果>>
*/
export const replaceRole = async (request: {
/** 当前角色ID */
currentRoleId: string;
/** 替换的角色ID */
replaceRoleId: string;
}): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>("/movie/replace_role", request);
};
/**
* 修改标签
* @param request - 修改标签请求参数
* @returns Promise<ApiResponse<修改后的标签>>
*/
export const updateTag = async (request: {
/** 标签ID */
tagId: string;
/** 新的标签内容 */
content: string | number;
}): Promise<ApiResponse<TagValueObject>> => {
return post<ApiResponse<any>>("/movie/update_tag", request);
};
/**
* 修改文案
* @param request - 修改文案请求参数
* @returns Promise<ApiResponse<修改后的文案>>
*/
export const updateText = async (request: {
/** 文案ID */
textId: string;
/** 新的文案内容 */
content: string;
}): Promise<ApiResponse<AITextEntity>> => {
return post<ApiResponse<any>>("/movie/update_text", request);
};
/**
* 重新生成场景
* @param request - 重新生成场景请求参数
* @returns Promise<ApiResponse<重新生成的场景>>
*/
export const regenerateScene = async (request: {
/** 场景提示词 */
prompt: string;
/** 标签列表 */
tagTypes: TagValueObject[];
/** 场景ID可选如果重新生成现有场景 */
sceneId?: string;
}): Promise<ApiResponse<SceneEntity>> => {
return post<ApiResponse<any>>("/movie/regenerate_scene", request);
};
/**
* 应用场景到分镜
* @param request - 应用场景请求参数
* @returns Promise<ApiResponse<应用结果>>
*/
export const applySceneToShots = async (request: {
/** 场景ID */
sceneId: string;
/** 分镜ID列表 */
shotIds: string[];
}): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>("/movie/apply_scene_to_shots", request);
};
/**
* 获取场景数据
* @param request - 获取场景数据请求参数
* @returns Promise<ApiResponse<{ AI文本数据, 标签列表 }>>
*/
export const getSceneData = async (request: {
/** 场景ID */
sceneId: string;
}): Promise<
ApiResponse<{
/** AI文本数据 */
text: AITextEntity;
/** 标签列表 */
tags: TagValueObject[];
}>
> => {
return post<ApiResponse<any>>("/movie/get_scene_data", request);
};
/**
* 获取场景列表
* @param request - 获取场景列表请求参数
* @returns Promise<ApiResponse<场景实体列表>>
*/
export const getSceneList = async (request: {
/** 项目ID */
projectId: string;
}): Promise<ApiResponse<SceneEntity[]>> => {
return post<ApiResponse<any>>("/movie/get_scene_list", request);
};
/**
* 获取场景应用到的分镜列表
* @param request - 获取场景分镜列表请求参数
* @returns Promise<ApiResponse<分镜列表>>
*/
export const getSceneShots = async (request: {
/** 场景ID */
sceneId: string;
}): Promise<
ApiResponse<{
/** 分镜列表 */
shots: VideoSegmentEntity[];
/** 已应用的分镜ID列表 */
appliedShotIds: string[];
}>
> => {
return post<ApiResponse<any>>("/movie/get_scene_shots", request);
};
/**
* 获取分镜关联的角色信息列表
* @param request - 获取分镜角色信息请求参数
* @returns Promise<ApiResponse<角色实体列表>>
*/
export const getShotRoles = async (request: {
/** 分镜ID */
shotId: string;
}): Promise<ApiResponse<RoleEntity[]>> => {
return post<ApiResponse<any>>("/movie/get_shot_roles", request);
};
/**
* 获取分镜关联的场景信息列表
* @param request - 获取分镜场景信息请求参数
* @returns Promise<ApiResponse<场景实体列表>>
*/
export const getShotScenes = async (request: {
/** 分镜ID */
shotId: string;
}): Promise<ApiResponse<SceneEntity[]>> => {
return post<ApiResponse<any>>("/movie/get_shot_scenes", request);
};
/**
* 获取分镜详细数据
* @param request - 获取分镜数据请求参数
* @returns Promise<ApiResponse<{ AI文本数据, 标签列表 }>>
*/
export const getShotData = async (request: {
/** 分镜ID */
shotId: string;
}): Promise<
ApiResponse<{
/** AI文本数据 */
text: AITextEntity;
/** 标签列表 */
tags: TagValueObject[];
}>
> => {
return post<ApiResponse<any>>("/movie/get_shot_data", request);
};
/**
* 重新生成分镜
* @param request - 重新生成分镜请求参数
* @returns Promise<ApiResponse<重新生成的分镜>>
*/
export const regenerateShot = async (request: {
/** 分镜ID */
shotId?: string;
/** 镜头描述 */
shotPrompt?: LensType[];
/** 角色ID替换参数格式为{oldId:string,newId:string}[] */
roleReplaceParams?: { oldId: string; newId: string }[];
/** 场景ID替换参数格式为{oldId:string,newId:string}[] */
sceneReplaceParams?: { oldId: string; newId: string }[];
}): Promise<ApiResponse<VideoSegmentEntity>> => {
return post<ApiResponse<any>>("/movie/regenerate_shot", request);
};
/**
* 获取分镜列表
* @param request - 获取分镜列表请求参数
* @returns Promise<ApiResponse<分镜实体列表>>
*/
export const getShotList = async (request: {
/** 项目ID */
projectId: string;
}): Promise<ApiResponse<VideoSegmentEntity[]>> => {
return post<ApiResponse<any>>("/movie/get_shot_list", request);
};
/**
* 替换分镜角色
* @param request - 替换分镜角色请求参数
* @returns Promise<ApiResponse<替换结果>>
*/
export const replaceShotRole = async (request: {
/** 分镜ID */
shotId: string;
/** 旧角色ID */
oldRoleId: string;
/** 新角色ID */
newRoleId: string;
}): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>("/movie/replace_shot_role", request);
};
/**
* 获取分镜视频剧本内容
* @param request - 获取分镜视频剧本请求参数
* @returns Promise<ApiResponse<剧本片段列表>>
*/
export const getShotVideoScript = async (request: {
/** 分镜ID */
shotId: string;
}): Promise<ApiResponse<string>> => {
return post<ApiResponse<any>>("/movie/get_shot_video_script", request);
};
/**
* AI生成剧本流式接口
* @param request - AI生成剧本请求参数
* @returns Promise<ApiResponse<流式数据>>
*/
export const generateScriptStream = (
request: {
/** 剧本提示词 */
text: string;
},
onData: (data: any) => void
): Promise<void> => {
return new Promise((resolve, reject) => {
streamJsonPost("/text_to_script/stream", request, (data) => {
switch(data.status) {
case 'streaming':
onData(data.content);
break;
case 'completed':
console.log('生成完成:', data.message);
resolve()
return;
case 'error':
console.error('生成失败:', data.message);
reject(data.message)
return;
}
})
})
};
/**
* 应用剧本
* @param request - 应用剧本请求参数
* @returns Promise<ApiResponse<应用结果>>
*/
export const applyScriptToShot = async (request: {
/** 项目ID */
project_id: string;
})=> {
return post<ApiResponse<any>>("/movie/create_movie_project_plan_v1", request);
};
/**
* 获取项目剧本数据
* @param request 请求参数
*/
export const getProjectScript = async (request: {
/** 项目ID */
project_id: string;
})=> {
return post<
ApiResponse<{
/** 项目id */
project_id: string;
/** 生成的剧本文本 */
generated_script: string;
}>
>("/movie/get_generated_script_by_project_id", request);
};
/**
* 保存剧本
* @param request 保存剧本请求参数
* @returns Promise<ApiResponse<保存结果>>
*/
export const saveScript = async (request: {
/** 项目ID */
project_id: string;
/**用户id */
user_id:string;
/** 剧本文本 */
generated_script: string;
}): Promise<ApiResponse<any>> => {
return post<ApiResponse<any>>("/movie/update_generated_script", request);
};
/**
* 中断视频任务
* @returns Promise<ApiResponse<{ projectId: string }>>
*/
export const abortVideoTask = async (request: {
/** 项目ID */
project_id: string;
}): Promise<ApiResponse<any>> => {
return post("/movie/abort_video_task", request);
};
export const pausePlanFlow = async (request: {
/** 项目ID */
project_id: string;
/** 计划ID */
plan_id: string;
}): Promise<ApiResponse<any>> => {
return post("/api/v1/video/pause", request);
};
export const resumePlanFlow = async (request: {
/** 项目ID */
project_id: string;
/** 计划ID */
plan_id: string;
}): Promise<ApiResponse<any>> => {
return post("/api/v1/video/resume", request);
};
export const createMovieProjectV1 = async (request: {
/** 剧本内容 */
script: string;
/** 用户ID */
user_id: string;
/** 模式automatic | manual */
mode: "automatic" | "manual";
/** 分辨率720p | 1080p | 4k */
resolution: "720p" | "1080p" | "4k";
/** 语言 */
language: string;
}) => {
return post<ApiResponse<{
/** 项目ID */
project_id: string;
/** 计划ID */
plan_id: string;
/** 项目名称 */
name: string;
/** 项目状态 */
status: string;
}>>("/movie/create_movie_project_v1", request);
};
/**
* 增强剧本流式接口
* @param request - 增强剧本请求参数
* @param onData - 流式数据回调
* @returns Promise<void>
*/
export const enhanceScriptStream = (
request: {
/** 原始剧本文本 */
original_script: string;
/** AI优化要求 */
aiOptimizing: string;
},
onData: (data: any) => void
): Promise<void> => {
return new Promise((resolve, reject) => {
streamJsonPost("/movie/enhance_script", request, (data) => {
switch(data.status) {
case 'streaming':
onData(data.content);
break;
case 'completed':
console.log('剧本增强完成:', data.message);
resolve()
return;
case 'error':
console.error('剧本增强失败:', data.message);
reject(data.message)
return;
}
})
})
};
/**
* AI优化镜头内容接口
* @param request - AI优化请求参数
* @returns Promise<ApiResponse<LensType[]>> 优化后的镜头数据
*/
export const optimizeShotContent = async (request: {
/** 视频片段ID */
shotId: string;
/** 用户优化需求 */
userRequirement: string;
/** 镜头数据数组 */
lensData: LensType[];
}): Promise<ApiResponse<LensType[]>> => {
return post<ApiResponse<LensType[]>>("/api/v1/shot/optimize_content", request);
};