Cache media-metadata requests.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user