Cache media-metadata requests.

This commit is contained in:
Dermot Duffy
2023-02-18 19:20:10 -08:00
parent 90b4d249d4
commit 425782f347
6 changed files with 122 additions and 53 deletions
+4 -1
View File
@@ -22,6 +22,8 @@ import {
CameraConfigs,
Engine,
CameraEndpoints,
MediaMetadataQuery,
MediaMetadataQueryResultsMap,
} from './types';
export const CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT = 10000;
@@ -106,7 +108,8 @@ export interface CameraManagerEngine {
getMediaMetadata(
hass: HomeAssistant,
cameras: CameraConfigs,
): Promise<MediaMetadata | null>;
query: MediaMetadataQuery,
): Promise<MediaMetadataQueryResultsMap | null>;
getCameraMetadata(
hass: HomeAssistant,
+39 -14
View File
@@ -4,7 +4,7 @@ import endOfHour from 'date-fns/endOfHour';
import startOfHour from 'date-fns/startOfHour';
import { CameraConfig, CardWideConfig } from '../../types';
import { ViewMedia } from '../../view/media';
import { RequestCache, RecordingSegmentsCache } from '../cache';
import { RecordingSegmentsCache, RequestCache } from '../cache';
import {
CameraManagerEngine,
CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT,
@@ -19,9 +19,6 @@ import {
EventQuery,
EventQueryResults,
EventQueryResultsMap,
FrigateEventQueryResults,
FrigateRecordingQueryResults,
FrigateRecordingSegmentsQueryResults,
MediaMetadata,
PartialEventQuery,
PartialRecordingQuery,
@@ -40,8 +37,16 @@ import {
CameraConfigs,
CameraEndpoints,
CameraEndpoint,
MediaMetadataQuery,
MediaMetadataQueryResults,
MediaMetadataQueryResultsMap,
} from '../types';
import { FrigateRecording } from './types';
import {
FrigateEventQueryResults,
FrigateRecordingQueryResults,
FrigateRecordingSegmentsQueryResults,
FrigateRecording,
} from './types';
import {
getEvents,
getEventSummary,
@@ -76,6 +81,7 @@ import { GenericCameraManagerEngine } from '../generic/engine-generic';
const EVENT_REQUEST_CACHE_MAX_AGE_SECONDS = 60;
const RECORDING_SUMMARY_REQUEST_CACHE_MAX_AGE_SECONDS = 60;
const MEDIA_METADATA_REQUEST_CACHE_AGE_SECONDS = 60;
const CAMERA_BIRDSEYE = 'birdseye' as const;
@@ -802,14 +808,26 @@ export class FrigateCameraManagerEngine
public async getMediaMetadata(
hass: HomeAssistant,
cameras: CameraConfigs,
): Promise<MediaMetadata | null> {
query: MediaMetadataQuery,
): Promise<MediaMetadataQueryResultsMap | null> {
const output: MediaMetadataQueryResultsMap = new Map();
if (this._requestCache.has(query)) {
const cachedResult = <MediaMetadataQueryResults | null>(
this._requestCache.get(query)
);
if (cachedResult) {
output.set(query, cachedResult as MediaMetadataQueryResults);
return output;
}
}
const what: Set<string> = new Set();
const where: Set<string> = new Set();
const days: Set<string> = new Set();
const instances = this._buildInstanceToCameraIDMapFromQuery(
cameras,
new Set(cameras.keys()),
query.cameraIDs,
);
const processEventSummary = async (
@@ -865,14 +883,21 @@ export class FrigateCameraManagerEngine
})(),
);
if (!what.size && !where.size && !days.size) {
return null;
}
return {
...(what.size && { what: what }),
...(where.size && { where: where }),
...(days.size && { days: days }),
const result: MediaMetadataQueryResults = {
type: QueryResultsType.MediaMetadata,
engine: Engine.Frigate,
metadata: {
...(what.size && { what: what }),
...(where.size && { where: where }),
...(days.size && { days: days }),
},
expiry: add(new Date(), { seconds: MEDIA_METADATA_REQUEST_CACHE_AGE_SECONDS }),
cached: false,
};
this._requestCache.set(query, { ...result, cached: true }, result.expiry);
output.set(query, result);
return output;
}
/**
+28
View File
@@ -1,5 +1,11 @@
import { z } from 'zod';
import { dayToDate } from '../../utils/basic';
import {
Engine,
EventQueryResults,
RecordingQueryResults,
RecordingSegmentsQueryResults,
} from '../types';
const dayStringToDate = (arg: unknown): Date | unknown => {
return typeof arg === 'string' ? dayToDate(arg) : arg;
@@ -66,3 +72,25 @@ export const eventSummarySchema = z
})
.array();
export type EventSummary = z.infer<typeof eventSummarySchema>;
// ==============================
// Frigate concrete query results
// ==============================
export interface FrigateEventQueryResults extends EventQueryResults {
engine: Engine.Frigate;
instanceID: string;
events: FrigateEvent[];
}
export interface FrigateRecordingQueryResults extends RecordingQueryResults {
engine: Engine.Frigate;
instanceID: string;
recordings: FrigateRecording[];
}
export interface FrigateRecordingSegmentsQueryResults
extends RecordingSegmentsQueryResults {
engine: Engine.Frigate;
instanceID: string;
}
+4 -1
View File
@@ -23,6 +23,8 @@ import {
CameraManagerCameraCapabilities,
Engine,
CameraEndpoints,
MediaMetadataQuery,
MediaMetadataQueryResultsMap,
} from '../types';
import { getEntityIcon, getEntityTitle } from '../../utils/ha';
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
@@ -139,7 +141,8 @@ export class GenericCameraManagerEngine implements CameraManagerEngine {
public async getMediaMetadata(
_hass: HomeAssistant,
_cameras: CameraConfigs,
): Promise<MediaMetadata | null> {
_query: MediaMetadataQuery,
): Promise<MediaMetadataQueryResultsMap | null> {
return null;
}
+34 -19
View File
@@ -31,6 +31,8 @@ import {
ResultsMap,
CameraEndpoints,
Engine,
MediaMetadataQuery,
MediaMetadataQueryResults,
} from './types.js';
import orderBy from 'lodash-es/orderBy';
import { CameraManagerEngineFactory } from './engine-factory.js';
@@ -62,6 +64,11 @@ class QueryClassifier {
): query is RecordingSegmentsQuery {
return query.type === QueryType.RecordingSegments;
}
public static isMediaMetadataQuery(
query: DataQuery | PartialDataQuery,
): query is MediaMetadataQuery {
return query.type === QueryType.MediaMetadata;
}
}
class QueryResultClassifier {
@@ -80,6 +87,11 @@ class QueryResultClassifier {
): queryResults is RecordingSegmentsQueryResults {
return queryResults.type === QueryResultsType.RecordingSegments;
}
public static isMediaMetadataQuery(
queryResults: QueryResults,
): queryResults is MediaMetadataQueryResults {
return queryResults.type === QueryResultsType.MediaMetadata;
}
}
export interface ExtendedMediaQueryResult<T extends MediaQuery> {
@@ -268,27 +280,24 @@ export class CameraManager {
const where: Set<string> = new Set();
const days: Set<string> = new Set();
const engines = this._store.getAllEngines();
const processMetadata = async (engine: CameraManagerEngine): Promise<void> => {
const engineMetadata = await engine.getMediaMetadata(
hass,
this._store.getCameras(),
);
if (engineMetadata) {
if (engineMetadata.what) {
engineMetadata.what.forEach(what.add, what);
}
if (engineMetadata.where) {
engineMetadata.where.forEach(where.add, where);
}
if (engineMetadata.days) {
engineMetadata.days.forEach(days.add, days);
}
}
const query: MediaMetadataQuery = {
type: QueryType.MediaMetadata,
cameraIDs: this._store.getCameraIDs(),
};
await allPromises(engines, processMetadata);
const results = await this._handleQuery(hass, query);
for (const [query, result] of results?.entries() ?? []) {
if (result.metadata.what) {
result.metadata.what.forEach(what.add, what);
}
if (result.metadata.where) {
result.metadata.where.forEach(where.add, where);
}
if (result.metadata.days) {
result.metadata.days.forEach(days.add, days);
}
}
if (!what.size && !where.size && !days.size) {
return null;
@@ -569,6 +578,12 @@ export class CameraManager {
this._store.getCameras(),
query,
)) as Map<QT, QueryReturnType<QT>> | null;
} else if (QueryClassifier.isMediaMetadataQuery(query)) {
engineResult = (await engine.getMediaMetadata(
hass,
this._store.getCameras(),
query,
)) as Map<QT, QueryReturnType<QT>> | null;
}
engineResult?.forEach((value, key) => results.set(key, value));
+13 -18
View File
@@ -1,6 +1,5 @@
import { CameraConfig, FrigateCardView } from '../types';
import { ViewMedia } from '../view/media';
import { FrigateEvent, FrigateRecording } from './frigate/types';
// ====
// Base
@@ -10,12 +9,14 @@ export enum QueryType {
Event = 'event-query',
Recording = 'recording-query',
RecordingSegments = 'recording-segments-query',
MediaMetadata = 'media-metadata',
}
export enum QueryResultsType {
Event = 'event-results',
Recording = 'recording-results',
RecordingSegments = 'recording-segments-results',
MediaMetadata = 'media-metadata-results',
}
export enum Engine {
@@ -65,6 +66,8 @@ export type QueryReturnType<QT> = QT extends EventQuery
? RecordingQueryResults
: QT extends RecordingSegmentsQuery
? RecordingSegmentsQueryResults
: QT extends MediaMetadataQuery
? MediaMetadataQueryResults
: never;
export type PartialQueryConcreteType<PQT> = PQT extends PartialEventQuery
? EventQuery
@@ -78,6 +81,7 @@ export type ResultsMap<QT> = Map<QT, QueryReturnType<QT>>;
export type EventQueryResultsMap = ResultsMap<EventQuery>;
export type RecordingQueryResultsMap = ResultsMap<RecordingQuery>;
export type RecordingSegmentsQueryResultsMap = ResultsMap<RecordingSegmentsQuery>;
export type MediaMetadataQueryResultsMap = ResultsMap<MediaMetadataQuery>;
export interface MediaMetadata {
where?: Set<string>;
@@ -176,24 +180,15 @@ export interface RecordingSegmentsQueryResults extends QueryResults {
segments: RecordingSegment[];
}
// ========================
// Frigate concrete results
// ========================
// ====================
// Media metadata Query
// ====================
export interface FrigateEventQueryResults extends EventQueryResults {
engine: Engine.Frigate;
instanceID: string;
events: FrigateEvent[];
export interface MediaMetadataQuery extends DataQuery {
type: QueryType.MediaMetadata;
}
export interface FrigateRecordingQueryResults extends RecordingQueryResults {
engine: Engine.Frigate;
instanceID: string;
recordings: FrigateRecording[];
}
export interface FrigateRecordingSegmentsQueryResults
extends RecordingSegmentsQueryResults {
engine: Engine.Frigate;
instanceID: string;
export interface MediaMetadataQueryResults extends QueryResults {
type: QueryResultsType.MediaMetadata;
metadata: MediaMetadata;
}