Generalize camera endpoints.
This commit is contained in:
@@ -18,9 +18,10 @@ import {
|
||||
CameraManagerCameraCapabilities,
|
||||
CameraManagerMediaCapabilities,
|
||||
CameraManagerCameraMetadata,
|
||||
CameraURLContext,
|
||||
CameraEndpointsContext,
|
||||
CameraConfigs,
|
||||
Engine,
|
||||
CameraEndpoints,
|
||||
} from './types';
|
||||
|
||||
export const CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT = 10000;
|
||||
@@ -118,8 +119,8 @@ export interface CameraManagerEngine {
|
||||
|
||||
getMediaCapabilities(media: ViewMedia): CameraManagerMediaCapabilities | null;
|
||||
|
||||
getCameraURL(
|
||||
getCameraEndpoints(
|
||||
cameraConfig: CameraConfig,
|
||||
context?: CameraURLContext,
|
||||
): string | null;
|
||||
context?: CameraEndpointsContext,
|
||||
): CameraEndpoints | null;
|
||||
}
|
||||
|
||||
@@ -36,8 +36,10 @@ import {
|
||||
RecordingSegment,
|
||||
RecordingSegmentsQuery,
|
||||
RecordingSegmentsQueryResultsMap,
|
||||
CameraURLContext,
|
||||
CameraEndpointsContext,
|
||||
CameraConfigs,
|
||||
CameraEndpoints,
|
||||
CameraEndpoint,
|
||||
} from '../types';
|
||||
import { FrigateRecording } from './types';
|
||||
import {
|
||||
@@ -1006,47 +1008,91 @@ export class FrigateCameraManagerEngine
|
||||
};
|
||||
}
|
||||
|
||||
public getCameraURL(
|
||||
public getCameraEndpoints(
|
||||
cameraConfig: CameraConfig,
|
||||
context?: CameraURLContext,
|
||||
): string | null {
|
||||
if (!cameraConfig.frigate.url) {
|
||||
return null;
|
||||
}
|
||||
if (!cameraConfig.frigate.camera_name) {
|
||||
return cameraConfig.frigate.url;
|
||||
}
|
||||
context?: CameraEndpointsContext,
|
||||
): CameraEndpoints | null {
|
||||
const getUIEndpoint = (): CameraEndpoint | null => {
|
||||
if (!cameraConfig.frigate.url) {
|
||||
return null;
|
||||
}
|
||||
if (!cameraConfig.frigate.camera_name) {
|
||||
return { endpoint: cameraConfig.frigate.url };
|
||||
}
|
||||
|
||||
const eventsURL =
|
||||
`${cameraConfig.frigate.url}/events?camera=` + cameraConfig.frigate.camera_name;
|
||||
const recordingsURL =
|
||||
`${cameraConfig.frigate.url}/recording/` + cameraConfig.frigate.camera_name;
|
||||
const cameraURL =
|
||||
`${cameraConfig.frigate.url}/cameras/` + cameraConfig.frigate.camera_name;
|
||||
|
||||
// If media is available, use it since it may result in a more precisely
|
||||
// correct URL.
|
||||
switch (context?.media?.getMediaType()) {
|
||||
case 'clip':
|
||||
case 'snapshot':
|
||||
return eventsURL;
|
||||
case 'recording':
|
||||
const startTime = context.media.getStartTime();
|
||||
if (startTime) {
|
||||
return recordingsURL + format(startTime, 'yyyy-MM-dd/HH');
|
||||
if (context?.view === 'live') {
|
||||
return { endpoint: cameraURL };
|
||||
}
|
||||
|
||||
const eventsURL =
|
||||
`${cameraConfig.frigate.url}/events?camera=` + cameraConfig.frigate.camera_name;
|
||||
const recordingsURL =
|
||||
`${cameraConfig.frigate.url}/recording/` + cameraConfig.frigate.camera_name;
|
||||
|
||||
// If media is available, use it since it may result in a more precisely
|
||||
// correct URL.
|
||||
switch (context?.media?.getMediaType()) {
|
||||
case 'clip':
|
||||
case 'snapshot':
|
||||
return { endpoint: eventsURL };
|
||||
case 'recording':
|
||||
const startTime = context.media.getStartTime();
|
||||
if (startTime) {
|
||||
return { endpoint: recordingsURL + format(startTime, 'yyyy-MM-dd/HH') };
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, fall back to just using the view if we have that.
|
||||
switch (context?.view) {
|
||||
case 'clip':
|
||||
case 'clips':
|
||||
case 'snapshots':
|
||||
case 'snapshot':
|
||||
return { endpoint: eventsURL };
|
||||
case 'recording':
|
||||
case 'recordings':
|
||||
return { endpoint: recordingsURL };
|
||||
}
|
||||
|
||||
return {
|
||||
endpoint: cameraURL,
|
||||
};
|
||||
};
|
||||
|
||||
const getGo2RTC = (): CameraEndpoint | null => {
|
||||
return {
|
||||
endpoint:
|
||||
`/api/frigate/${cameraConfig.frigate.client_id}` +
|
||||
// go2rtc is exposed by the integration under the (slightly
|
||||
// misleading) 'mse' path, even though that path can serve all go2rtc
|
||||
// modes.
|
||||
`/mse/api/ws?src=${cameraConfig.frigate.camera_name}`,
|
||||
sign: true,
|
||||
};
|
||||
};
|
||||
|
||||
const getJSMPEG = (): CameraEndpoint | null => {
|
||||
return {
|
||||
endpoint:
|
||||
`/api/frigate/${cameraConfig.frigate.client_id}` +
|
||||
`/jsmpeg/${cameraConfig.frigate.camera_name}`,
|
||||
sign: true,
|
||||
};
|
||||
};
|
||||
|
||||
const ui = getUIEndpoint();
|
||||
const go2rtc = getGo2RTC();
|
||||
const jsmpeg = getJSMPEG();
|
||||
|
||||
return ui || go2rtc || jsmpeg
|
||||
? {
|
||||
...(ui && { ui: ui }),
|
||||
...(go2rtc && { go2rtc: go2rtc }),
|
||||
...(jsmpeg && { jsmpeg: jsmpeg }),
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, fall back to just using the view if we have that.
|
||||
switch (context?.view) {
|
||||
case 'clip':
|
||||
case 'clips':
|
||||
case 'snapshots':
|
||||
case 'snapshot':
|
||||
return eventsURL;
|
||||
case 'recording':
|
||||
case 'recordings':
|
||||
return recordingsURL;
|
||||
}
|
||||
|
||||
return `${cameraConfig.frigate.url}/cameras/${cameraConfig.frigate.camera_name}`;
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,13 @@ import {
|
||||
RecordingQueryResultsMap,
|
||||
RecordingSegmentsQuery,
|
||||
RecordingSegmentsQueryResultsMap,
|
||||
CameraURLContext,
|
||||
CameraEndpointsContext,
|
||||
CameraConfigs,
|
||||
RecordingQuery,
|
||||
QueryReturnType,
|
||||
CameraManagerCameraCapabilities,
|
||||
Engine,
|
||||
CameraEndpoints,
|
||||
} from '../types';
|
||||
import { getEntityIcon, getEntityTitle } from '../../utils/ha';
|
||||
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
|
||||
@@ -177,10 +178,10 @@ export class GenericCameraManagerEngine implements CameraManagerEngine {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getCameraURL(
|
||||
public getCameraEndpoints(
|
||||
_cameraConfig: CameraConfig,
|
||||
_context?: CameraURLContext,
|
||||
): string | null {
|
||||
_context?: CameraEndpointsContext,
|
||||
): CameraEndpoints | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
CameraManagerCameraMetadata,
|
||||
CameraManagerCapabilities,
|
||||
CameraManagerMediaCapabilities,
|
||||
CameraURLContext,
|
||||
CameraEndpointsContext,
|
||||
DataQuery,
|
||||
EventQuery,
|
||||
EventQueryResults,
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
RecordingSegmentsQueryResults,
|
||||
RecordingSegmentsQueryResultsMap,
|
||||
ResultsMap,
|
||||
CameraEndpoints,
|
||||
} from './types.js';
|
||||
import orderBy from 'lodash-es/orderBy';
|
||||
import { CameraManagerEngineFactory } from './engine-factory.js';
|
||||
@@ -643,13 +644,16 @@ export class CameraManager {
|
||||
);
|
||||
}
|
||||
|
||||
public getCameraURL(cameraID: string, context?: CameraURLContext): string | null {
|
||||
public getCameraEndpoints(
|
||||
cameraID: string,
|
||||
context?: CameraEndpointsContext,
|
||||
): CameraEndpoints | null {
|
||||
const cameraConfig = this._store.getCameraConfig(cameraID);
|
||||
const engine = this._store.getEngineForCameraID(cameraID);
|
||||
if (!cameraConfig || !engine) {
|
||||
return null;
|
||||
}
|
||||
return engine.getCameraURL(cameraConfig, context);
|
||||
return engine.getCameraEndpoints(cameraConfig, context);
|
||||
}
|
||||
|
||||
public getCameraMetadata(
|
||||
|
||||
@@ -106,11 +106,22 @@ export interface CameraManagerCameraMetadata {
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export interface CameraURLContext {
|
||||
export interface CameraEndpointsContext {
|
||||
media?: ViewMedia;
|
||||
view?: FrigateCardView;
|
||||
}
|
||||
|
||||
export interface CameraEndpoint {
|
||||
endpoint: string;
|
||||
sign?: boolean;
|
||||
}
|
||||
|
||||
export interface CameraEndpoints {
|
||||
ui?: CameraEndpoint;
|
||||
go2rtc?: CameraEndpoint;
|
||||
jsmpeg?: CameraEndpoint;
|
||||
}
|
||||
|
||||
export type CameraConfigs = Map<string, CameraConfig>;
|
||||
|
||||
// ===========
|
||||
|
||||
Reference in New Issue
Block a user