Files
iot-device-management-frontend/apps/web-antd/src/api/video/device/index.ts

314 lines
8.6 KiB
TypeScript
Raw Normal View History

/**
* Video - API
*
* requestClientyudao Authorization: Bearer
* video gateway @RequestMapping
* /video/proxy/* StreamProxyController
* /video/server/* ServerController
* /video/ai/camera/*
* /video/ai/roi/* ROI
* /video/ai/algorithm/*
* /video/ai/config/*
*/
import { useAppConfig } from '@vben/hooks';
import { wvpRequestClient } from '#/api/video/request';
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
// ==================== 类型定义 ====================
export namespace VideoDeviceApi {
/** 分页响应结构 */
export interface PageResult<T> {
list: T[];
total: number;
}
/** 摄像头(拉流代理) */
export interface Camera {
id?: number;
type?: string; // 'default' | 'ffmpeg'
app?: string;
stream?: string;
cameraCode?: string; // 摄像头唯一编码
cameraName?: string; // 摄像头名称(用户自定义)
edgeDeviceId?: string; // 绑定的边缘设备ID
srcUrl?: string;
timeout?: number;
rtspType?: string; // '0'=TCP, '1'=UDP, '2'=Multicast
enable?: boolean;
enableAudio?: boolean;
enableMp4?: boolean;
enableDisableNoneReader?: boolean;
relatesMediaServerId?: string;
ffmpegCmdKey?: string;
pulling?: boolean;
mediaServerId?: string;
streamKey?: string;
createTime?: string;
}
/** ROI 区域 */
export interface Roi {
id?: number;
roiId?: string;
cameraId?: string;
deviceId?: string;
name?: string;
roiType?: string; // 'rectangle' | 'polygon'
coordinates?: string;
color?: string;
priority?: number;
enabled?: number; // 0 | 1
description?: string;
algorithms?: RoiAlgoBinding[];
}
/** ROI 算法绑定(详情嵌套结构) */
export interface RoiAlgoBinding {
bind: AlgoBind;
algorithm?: Algorithm;
}
/** 算法绑定记录 */
export interface AlgoBind {
bindId?: string;
roiId?: string;
algoCode?: string;
enabled?: number; // 0 | 1
params?: string;
}
/** 算法定义 */
export interface Algorithm {
id?: number;
algoCode?: string;
algoName?: string;
description?: string;
isActive?: boolean;
paramSchema?: string;
globalParams?: string;
}
/** 媒体服务器 */
export interface MediaServer {
id: string;
ip: string;
}
}
// ==================== 摄像头管理 ====================
/** 摄像头列表(分页) */
export function getCameraList(params: {
page: number;
count: number;
query?: string;
pulling?: boolean;
}) {
return wvpRequestClient.get<VideoDeviceApi.PageResult<VideoDeviceApi.Camera>>(
'/video/proxy/list',
{ params },
);
}
/** 新增摄像头 */
export function addCamera(data: Partial<VideoDeviceApi.Camera>) {
return wvpRequestClient.post('/video/proxy/add', data);
}
/** 编辑摄像头 */
export function updateCamera(data: Partial<VideoDeviceApi.Camera>) {
return wvpRequestClient.post('/video/proxy/update', data);
}
/** 保存摄像头(新增/编辑,有 id 为编辑) */
export function saveCamera(data: Partial<VideoDeviceApi.Camera>) {
return data.id ? updateCamera(data) : addCamera(data);
}
/** 删除摄像头 */
export function deleteCamera(id: number) {
return wvpRequestClient.delete('/video/proxy/delete', {
params: { id },
});
}
/** 开始拉流 */
export function startCamera(id: number) {
return wvpRequestClient.get('/video/proxy/start', { params: { id } });
}
/** 停止拉流 */
export function stopCamera(id: number) {
return wvpRequestClient.get('/video/proxy/stop', { params: { id } });
}
/** 在线媒体服务器列表 */
export function getMediaServerList() {
return wvpRequestClient.get<VideoDeviceApi.MediaServer[]>(
'/video/server/media_server/online/list',
);
}
/** 摄像头选项列表(用于下拉搜索选择) */
export function getCameraOptions() {
return wvpRequestClient.get<
{ cameraCode: string; cameraName: string }[]
>('/video/ai/camera/options');
}
// ==================== ROI 区域管理 ====================
/** ROI 列表(分页) */
export function getRoiList(params: {
page: number;
count: number;
cameraId?: string;
deviceId?: string;
query?: string;
}) {
return wvpRequestClient.get<VideoDeviceApi.PageResult<VideoDeviceApi.Roi>>(
'/video/ai/roi/list',
{ params },
);
}
/** ROI 详情(含算法绑定) */
export function getRoiDetail(id: number) {
return wvpRequestClient.get<VideoDeviceApi.Roi>(`/video/ai/roi/${id}`);
}
/** 某摄像头的所有 ROI */
export function getRoiByCameraId(cameraId: string) {
return wvpRequestClient.get<VideoDeviceApi.Roi[]>(
'/video/ai/roi/channel',
{ params: { cameraId } },
);
}
/** 保存 ROI新增/编辑) */
export function saveRoi(data: Partial<VideoDeviceApi.Roi>) {
return wvpRequestClient.post('/video/ai/roi/save', data);
}
/** 删除 ROI */
export function deleteRoi(roiId: string) {
return wvpRequestClient.delete(`/video/ai/roi/delete/${roiId}`);
}
/**
* URL
*
* force URL URL
* force URL
*
*
* - WVP ?access-token=xxx
* - URL <img src> 使 Authorization
*
* (a) SecurityConfig /video/ai/roi/snap/image permitAll
* (b) yudao session cookie JWT
* (c) OSS URL 302
* - 401/403 <img onerror>
*/
export async function getSnapUrl(cameraCode: string, force = false): Promise<string> {
if (force) {
// 强制刷新:先触发边缘端截图(等待完成)
try {
await wvpRequestClient.get('/video/ai/roi/snap', {
params: { cameraCode, force: true },
});
} catch (err) {
// 截图请求可能超时,但 OSS 上可能已有图片,继续返回代理 URL
if (import.meta.env.DEV) {
console.debug('[video] force snap 触发失败,继续返回已有代理 URL', err);
}
}
return (
`${apiURL}/video/ai/roi/snap/image` +
`?cameraCode=${encodeURIComponent(cameraCode)}` +
`&t=${Date.now()}`
);
}
return (
`${apiURL}/video/ai/roi/snap/image` +
`?cameraCode=${encodeURIComponent(cameraCode)}`
);
}
// ==================== 算法管理 ====================
/** 算法列表 */
export function getAlgorithmList(deviceId?: string) {
return wvpRequestClient.get<VideoDeviceApi.Algorithm[]>(
'/video/ai/algorithm/list',
{ params: deviceId ? { deviceId } : {} },
);
}
/** 保存算法全局参数 */
export function saveAlgoGlobalParams(algoCode: string, globalParams: string) {
return wvpRequestClient.post(
`/video/ai/algorithm/global-params/${algoCode}`,
{ globalParams },
);
}
/** 批量更新设备算法参数 */
export function updateDeviceAlgoParams(deviceId: string, algoCode: string, params: string) {
return wvpRequestClient.post(
`/video/ai/algorithm/device-binds/${deviceId}/${algoCode}`,
{ params },
);
}
// ==================== 算法绑定 ====================
/** 绑定算法到 ROI */
export function bindAlgo(data: { roiId: string; algoCode: string }) {
return wvpRequestClient.post('/video/ai/roi/bindAlgo', data);
}
/** 解绑算法 */
export function unbindAlgo(bindId: string) {
return wvpRequestClient.delete('/video/ai/roi/unbindAlgo', {
params: { bindId },
});
}
/** 更新算法绑定参数 */
export function updateAlgoParams(data: {
bindId: string;
params?: string;
enabled?: number;
}) {
return wvpRequestClient.post('/video/ai/roi/updateAlgoParams', data);
}
// ==================== 配置推送 ====================
/** 推送配置到边缘端 */
export function pushConfig(cameraId: string) {
return wvpRequestClient.post('/video/ai/config/push', null, {
params: { cameraId },
});
}
/** 一次性推送全部配置到本地Edge */
export function pushAllConfig() {
return wvpRequestClient.post<Record<string, any>>(
'/video/ai/config/push-all',
);
}
/** 导出摄像头配置 JSON */
export function exportConfig(cameraId: string) {
return wvpRequestClient.get<Record<string, any>>(
'/video/ai/config/export',
{ params: { cameraId } },
);
}