Rename DataManager to CameraManager.

This commit is contained in:
Dermot Duffy
2023-01-24 19:36:54 -08:00
parent e0d2dfaa60
commit 39b44863ce
23 changed files with 183 additions and 187 deletions
+310
View File
@@ -0,0 +1,310 @@
import { HomeAssistant } from 'custom-card-helpers';
import { CameraConfig } from '../types.js';
import { arrayify, setify } from '../utils/basic.js';
import {
DataQuery,
EventQuery,
EventQueryResults,
PartialDataQuery,
PartialEventQuery,
PartialQueryConcreteType,
PartialRecordingQuery,
PartialRecordingSegmentsQuery,
QueryResults,
QueryResultsType,
QueryReturnType,
QueryType,
RecordingQuery,
RecordingQueryResults,
RecordingSegmentsQuery,
RecordingSegmentsQueryResults,
} from './types.js';
import orderBy from 'lodash-es/orderBy';
import { CameraManagerEngineFactory } from './engine-factory.js';
import { ViewMedia } from '../view-media.js';
import { MediaQueries, MediaQueriesResults } from '../view.js';
import { MemoryRequestCache } from './cache.js';
export class QueryClassifier {
public static isEventQuery(query: DataQuery | PartialDataQuery): query is EventQuery {
return query.type === QueryType.Event;
}
public static isRecordingQuery(
query: DataQuery | PartialDataQuery,
): query is RecordingQuery {
return query.type === QueryType.Recording;
}
public static isRecordingSegmentsQuery(
query: DataQuery | PartialDataQuery,
): query is RecordingSegmentsQuery {
return query.type === QueryType.RecordingSegments;
}
}
export class QueryResultClassifier {
public static isEventQueryResult(
queryResults: QueryResults,
): queryResults is EventQueryResults {
return queryResults.type === QueryResultsType.Event;
}
public static isRecordingQuery(
queryResults: QueryResults,
): queryResults is RecordingQueryResults {
return queryResults.type === QueryResultsType.Recording;
}
public static isRecordingSegmentsQuery(
queryResults: QueryResults,
): queryResults is RecordingSegmentsQueryResults {
return queryResults.type === QueryResultsType.RecordingSegments;
}
}
export type RequestCache = MemoryRequestCache<DataQuery, QueryResults>;
export class CameraManager {
protected _engineFactory: CameraManagerEngineFactory;
protected _cameras: Map<string, CameraConfig>;
protected _requestCache: RequestCache;
constructor(
engineFactory: CameraManagerEngineFactory,
cameras: Map<string, CameraConfig>,
requestCache: RequestCache,
) {
this._engineFactory = engineFactory;
this._cameras = cameras;
this._requestCache = requestCache;
}
public generateDefaultEventQueries(
cameraIDs: string | Set<string>,
partialQuery: PartialEventQuery,
): EventQuery[] {
return this._generateDefaultQueries(cameraIDs, {
...partialQuery,
type: QueryType.Event,
});
}
public generateDefaultRecordingQueries(
cameraIDs: string | Set<string>,
partialQuery: PartialRecordingQuery,
): RecordingQuery[] {
return this._generateDefaultQueries(cameraIDs, {
...partialQuery,
type: QueryType.Recording,
});
}
public generateDefaultRecordingSegmentsQueries(
cameraIDs: string | Set<string>,
partialQuery: PartialRecordingSegmentsQuery,
): RecordingSegmentsQuery[] {
return this._generateDefaultQueries(cameraIDs, {
...partialQuery,
type: QueryType.RecordingSegments,
});
}
protected _generateDefaultQueries<PQT extends Partial<DataQuery>>(
cameraIDs: string | Set<string>,
partialQuery: PQT,
): PartialQueryConcreteType<PQT>[] {
const concreteQueries: PartialQueryConcreteType<PQT>[] = [];
const _cameraIDs = setify(cameraIDs);
_cameraIDs.forEach((cameraID) => {
const cameraConfig = this._cameras.get(cameraID);
if (!cameraConfig) {
return;
}
const engine = this._engineFactory.getEngineForCamera(cameraConfig);
if (!engine) {
return;
}
let query: DataQuery | null = null;
if (QueryClassifier.isEventQuery(partialQuery)) {
query = engine.generateDefaultEventQuery(cameraID, cameraConfig, partialQuery);
} else if (QueryClassifier.isRecordingQuery(partialQuery)) {
query = engine.generateDefaultRecordingQuery(
cameraID,
cameraConfig,
partialQuery,
);
} else if (QueryClassifier.isRecordingSegmentsQuery(partialQuery)) {
query = engine.generateDefaultRecordingSegmentsQuery(
cameraID,
cameraConfig,
partialQuery,
);
}
if (query) {
concreteQueries.push(query as PartialQueryConcreteType<PQT>);
}
});
return concreteQueries;
}
public async getEvents(
hass: HomeAssistant,
query: EventQuery | EventQuery[],
): Promise<Map<EventQuery, EventQueryResults>> {
return await this._handleQuery(hass, query);
}
public async getRecordings(
hass: HomeAssistant,
query: RecordingQuery | RecordingQuery[],
): Promise<Map<RecordingQuery, RecordingQueryResults>> {
return await this._handleQuery(hass, query);
}
public async getRecordingSegments(
hass: HomeAssistant,
query: RecordingSegmentsQuery | RecordingSegmentsQuery[],
): Promise<Map<RecordingSegmentsQuery, RecordingSegmentsQueryResults>> {
return await this._handleQuery(hass, query);
}
public async executeMediaQuery(
hass: HomeAssistant,
mediaQuerys: MediaQueries,
): Promise<MediaQueriesResults | null> {
const queries: (RecordingQuery | EventQuery)[] | null = mediaQuerys.getQueries();
if (!queries) {
return null;
}
const results = await this._handleQuery(hass, queries);
const mediaArray: ViewMedia[] = [];
for (const [query, result] of results.entries()) {
const engine = this._engineFactory.getEngineForQuery(this._cameras, query);
if (engine) {
let media: ViewMedia[] | null = null;
if (
QueryClassifier.isEventQuery(query) &&
QueryResultClassifier.isEventQueryResult(result)
) {
media = engine.generateMediaFromEvents(query, result);
} else if (
QueryClassifier.isRecordingQuery(query) &&
QueryResultClassifier.isRecordingQuery(result)
) {
media = engine.generateMediaFromRecordings(query, result);
}
if (media) {
mediaArray.push(...media);
}
}
}
return new MediaQueriesResults(
orderBy(mediaArray, (media) => media.getStartTime(), 'desc'),
// Select the first (most-recent) item.
mediaArray.length ? 0 : null,
);
}
public getMediaDownloadPath(media: ViewMedia): string | null {
const cameraConfig = this._cameras.get(media.getCameraID());
const engine = cameraConfig
? this._engineFactory.getEngineForCamera(cameraConfig)
: null;
if (!cameraConfig || !engine) {
return null;
}
return engine.getMediaDownloadPath(cameraConfig, media);
}
public async favoriteMedia(
hass: HomeAssistant,
cameraConfig: CameraConfig,
media: ViewMedia,
favorite: boolean,
): Promise<void> {
const engine = this._engineFactory.getEngineForCamera(cameraConfig);
if (engine) {
engine.favoriteMedia(hass, cameraConfig, media, favorite);
}
}
public areMediaQueriesResultsFresh(
queries: MediaQueries,
results: MediaQueriesResults,
): boolean {
const cameraIDs: Set<string> = new Set();
(queries.getQueries() ?? []).forEach((query) => cameraIDs.add(query.cameraID));
for (const cameraID of cameraIDs) {
const cameraConfig = this._cameras.get(cameraID);
if (!cameraConfig) {
return false;
}
const engine = this._engineFactory.getEngineForCamera(cameraConfig);
if (!engine || !engine.areMediaQueriesResultsFresh(queries, results)) {
return false;
}
}
return true;
}
protected async _handleQuery<QT extends DataQuery>(
hass: HomeAssistant,
query: QT | QT[],
): Promise<Map<QT, QueryReturnType<QT>>> {
const _queries = arrayify(query);
const results = new Map<QT, QueryReturnType<QT>>();
const queryStartTime = new Date();
let queryCachedCount = 0;
const processQuery = async (query: QT): Promise<void> => {
const cachedResult: QueryReturnType<QT> | null = this._requestCache.get(
query,
) as QueryReturnType<QT> | null;
if (cachedResult) {
queryCachedCount++;
results.set(query, cachedResult);
return;
}
const engine = this._engineFactory.getEngineForQuery(this._cameras, query);
if (!engine) {
return;
}
let result: QueryResults | null = null;
if (QueryClassifier.isEventQuery(query)) {
result = await engine.getEvents(hass, this._cameras, query);
} else if (QueryClassifier.isRecordingQuery(query)) {
result = await engine.getRecordings(hass, this._cameras, query);
} else if (QueryClassifier.isRecordingSegmentsQuery(query)) {
result = await engine.getRecordingSegments(hass, this._cameras, query);
}
if (result) {
if (result.expiry) {
this._requestCache.set(query, result, result.expiry);
}
results.set(query, result as QueryReturnType<QT>);
}
};
await Promise.all(_queries.map((query) => processQuery(query)));
console.debug(
'Frigate Card CameraManager request (Cached:',
`${queryCachedCount}/${_queries.length},`,
`Duration: ${(new Date().getTime() - queryStartTime.getTime()) / 1000}s,`,
'Queries:',
_queries,
', Results:',
results,
')',
);
return results;
}
}