Add support for 'scroll up refresh' in the gallery.
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
CameraEndpoints,
|
||||
MediaMetadataQuery,
|
||||
MediaMetadataQueryResultsMap,
|
||||
EngineOptions,
|
||||
} from './types';
|
||||
|
||||
export const CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT = 10000;
|
||||
@@ -58,18 +59,21 @@ export interface CameraManagerEngine {
|
||||
hass: HomeAssistant,
|
||||
cameras: CameraConfigs,
|
||||
query: EventQuery,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<EventQueryResultsMap | null>;
|
||||
|
||||
getRecordings(
|
||||
hass: HomeAssistant,
|
||||
cameras: CameraConfigs,
|
||||
query: RecordingQuery,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<RecordingQueryResultsMap | null>;
|
||||
|
||||
getRecordingSegments(
|
||||
hass: HomeAssistant,
|
||||
cameras: CameraConfigs,
|
||||
query: RecordingSegmentsQuery,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<RecordingSegmentsQueryResultsMap | null>;
|
||||
|
||||
generateMediaFromEvents(
|
||||
@@ -102,12 +106,14 @@ export interface CameraManagerEngine {
|
||||
cameras: CameraConfigs,
|
||||
media: ViewMedia,
|
||||
target: Date,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<number | null>;
|
||||
|
||||
getMediaMetadata(
|
||||
hass: HomeAssistant,
|
||||
cameras: CameraConfigs,
|
||||
query: MediaMetadataQuery,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<MediaMetadataQueryResultsMap | null>;
|
||||
|
||||
getCameraMetadata(
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
MediaMetadataQuery,
|
||||
MediaMetadataQueryResults,
|
||||
MediaMetadataQueryResultsMap,
|
||||
EngineOptions,
|
||||
} from '../types';
|
||||
import {
|
||||
FrigateEventQueryResults,
|
||||
@@ -426,6 +427,7 @@ export class FrigateCameraManagerEngine
|
||||
hass: HomeAssistant,
|
||||
cameras: CameraConfigs,
|
||||
query: EventQuery,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<EventQueryResultsMap | null> {
|
||||
const output: EventQueryResultsMap = new Map();
|
||||
|
||||
@@ -437,7 +439,8 @@ export class FrigateCameraManagerEngine
|
||||
return;
|
||||
}
|
||||
const instanceQuery = { ...query, cameraIDs: cameraIDs };
|
||||
const cachedResult = this._requestCache.get(instanceQuery);
|
||||
const cachedResult =
|
||||
engineOptions?.useCache ?? true ? this._requestCache.get(instanceQuery) : null;
|
||||
if (cachedResult) {
|
||||
output.set(query, cachedResult as EventQueryResults);
|
||||
return;
|
||||
@@ -467,7 +470,9 @@ export class FrigateCameraManagerEngine
|
||||
cached: false,
|
||||
};
|
||||
|
||||
this._requestCache.set(query, { ...result, cached: true }, result.expiry);
|
||||
if (engineOptions?.useCache ?? true) {
|
||||
this._requestCache.set(query, { ...result, cached: true }, result.expiry);
|
||||
}
|
||||
output.set(instanceQuery, result);
|
||||
};
|
||||
|
||||
@@ -491,6 +496,7 @@ export class FrigateCameraManagerEngine
|
||||
hass: HomeAssistant,
|
||||
cameras: CameraConfigs,
|
||||
query: RecordingQuery,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<RecordingQueryResultsMap | null> {
|
||||
const output: RecordingQueryResultsMap = new Map();
|
||||
|
||||
@@ -499,7 +505,8 @@ export class FrigateCameraManagerEngine
|
||||
cameraID: string,
|
||||
): Promise<void> => {
|
||||
const query = { ...baseQuery, cameraIDs: new Set([cameraID]) };
|
||||
const cachedResult = this._requestCache.get(query);
|
||||
const cachedResult =
|
||||
engineOptions?.useCache ?? true ? this._requestCache.get(query) : null;
|
||||
if (cachedResult) {
|
||||
output.set(query, cachedResult as RecordingQueryResults);
|
||||
return;
|
||||
@@ -557,7 +564,9 @@ export class FrigateCameraManagerEngine
|
||||
}),
|
||||
cached: false,
|
||||
};
|
||||
this._requestCache.set(query, { ...result, cached: true }, result.expiry);
|
||||
if (engineOptions?.useCache ?? true) {
|
||||
this._requestCache.set(query, { ...result, cached: true }, result.expiry);
|
||||
}
|
||||
output.set(query, result);
|
||||
};
|
||||
|
||||
@@ -573,6 +582,7 @@ export class FrigateCameraManagerEngine
|
||||
hass: HomeAssistant,
|
||||
cameras: CameraConfigs,
|
||||
query: RecordingSegmentsQuery,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<RecordingSegmentsQueryResultsMap | null> {
|
||||
const output: RecordingSegmentsQueryResultsMap = new Map();
|
||||
|
||||
@@ -595,7 +605,10 @@ export class FrigateCameraManagerEngine
|
||||
// query is different -- the segments won't be). This is since the
|
||||
// volume of data in segment transfers can be high, and the segments can
|
||||
// be used in high frequency situations (e.g. video seeking).
|
||||
const cachedSegments = this._recordingSegmentsCache.get(cameraID, range);
|
||||
const cachedSegments =
|
||||
engineOptions?.useCache ?? true
|
||||
? this._recordingSegmentsCache.get(cameraID, range)
|
||||
: null;
|
||||
if (cachedSegments) {
|
||||
output.set(query, <FrigateRecordingSegmentsQueryResults>{
|
||||
type: QueryResultsType.RecordingSegments,
|
||||
@@ -615,7 +628,10 @@ export class FrigateCameraManagerEngine
|
||||
};
|
||||
|
||||
const segments = await getRecordingSegments(hass, request);
|
||||
this._recordingSegmentsCache.add(cameraID, range, segments);
|
||||
|
||||
if (engineOptions?.useCache ?? true) {
|
||||
this._recordingSegmentsCache.add(cameraID, range, segments);
|
||||
}
|
||||
|
||||
output.set(query, <FrigateRecordingSegmentsQueryResults>{
|
||||
type: QueryResultsType.RecordingSegments,
|
||||
@@ -757,6 +773,7 @@ export class FrigateCameraManagerEngine
|
||||
cameras: CameraConfigs,
|
||||
media: ViewMedia,
|
||||
target: Date,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<number | null> {
|
||||
const start = media.getStartTime();
|
||||
const end = media.getEndTime();
|
||||
@@ -772,7 +789,7 @@ export class FrigateCameraManagerEngine
|
||||
type: QueryType.RecordingSegments,
|
||||
};
|
||||
|
||||
const results = await this.getRecordingSegments(hass, cameras, query);
|
||||
const results = await this.getRecordingSegments(hass, cameras, query, engineOptions);
|
||||
|
||||
if (results) {
|
||||
return this._getSeekTimeInSegments(
|
||||
@@ -811,9 +828,10 @@ export class FrigateCameraManagerEngine
|
||||
hass: HomeAssistant,
|
||||
cameras: CameraConfigs,
|
||||
query: MediaMetadataQuery,
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<MediaMetadataQueryResultsMap | null> {
|
||||
const output: MediaMetadataQueryResultsMap = new Map();
|
||||
if (this._requestCache.has(query)) {
|
||||
if ((engineOptions?.useCache ?? true) && this._requestCache.has(query)) {
|
||||
const cachedResult = <MediaMetadataQueryResults | null>(
|
||||
this._requestCache.get(query)
|
||||
);
|
||||
@@ -860,10 +878,15 @@ export class FrigateCameraManagerEngine
|
||||
};
|
||||
|
||||
const processRecordings = async (cameraIDs: Set<string>): Promise<void> => {
|
||||
const recordings = await this.getRecordings(hass, cameras, {
|
||||
type: QueryType.Recording,
|
||||
cameraIDs: cameraIDs,
|
||||
});
|
||||
const recordings = await this.getRecordings(
|
||||
hass,
|
||||
cameras,
|
||||
{
|
||||
type: QueryType.Recording,
|
||||
cameraIDs: cameraIDs,
|
||||
},
|
||||
engineOptions,
|
||||
);
|
||||
if (!recordings) {
|
||||
return;
|
||||
}
|
||||
@@ -902,7 +925,9 @@ export class FrigateCameraManagerEngine
|
||||
cached: false,
|
||||
};
|
||||
|
||||
this._requestCache.set(query, { ...result, cached: true }, result.expiry);
|
||||
if (engineOptions?.useCache ?? true) {
|
||||
this._requestCache.set(query, { ...result, cached: true }, result.expiry);
|
||||
}
|
||||
output.set(query, result);
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
DataQuery,
|
||||
EventQuery,
|
||||
EventQueryResultsMap,
|
||||
MediaMetadata,
|
||||
PartialEventQuery,
|
||||
PartialRecordingQuery,
|
||||
PartialRecordingSegmentsQuery,
|
||||
@@ -25,6 +24,7 @@ import {
|
||||
CameraEndpoints,
|
||||
MediaMetadataQuery,
|
||||
MediaMetadataQueryResultsMap,
|
||||
EngineOptions,
|
||||
} from '../types';
|
||||
import { getEntityIcon, getEntityTitle } from '../../utils/ha';
|
||||
import { EntityRegistryManager } from '../../utils/ha/entity-registry';
|
||||
@@ -71,6 +71,7 @@ export class GenericCameraManagerEngine implements CameraManagerEngine {
|
||||
_hass: HomeAssistant,
|
||||
_cameras: CameraConfigs,
|
||||
_query: EventQuery,
|
||||
_engineOptions?: EngineOptions,
|
||||
): Promise<EventQueryResultsMap | null> {
|
||||
return null;
|
||||
}
|
||||
@@ -79,6 +80,7 @@ export class GenericCameraManagerEngine implements CameraManagerEngine {
|
||||
_hass: HomeAssistant,
|
||||
_cameras: CameraConfigs,
|
||||
_query: RecordingQuery,
|
||||
_engineOptions?: EngineOptions,
|
||||
): Promise<RecordingQueryResultsMap | null> {
|
||||
return null;
|
||||
}
|
||||
@@ -87,6 +89,7 @@ export class GenericCameraManagerEngine implements CameraManagerEngine {
|
||||
_hass: HomeAssistant,
|
||||
_cameras: CameraConfigs,
|
||||
_query: RecordingSegmentsQuery,
|
||||
_engineOptions?: EngineOptions,
|
||||
): Promise<RecordingSegmentsQueryResultsMap | null> {
|
||||
return null;
|
||||
}
|
||||
@@ -134,6 +137,7 @@ export class GenericCameraManagerEngine implements CameraManagerEngine {
|
||||
_cameras: CameraConfigs,
|
||||
_media: ViewMedia,
|
||||
_target: Date,
|
||||
_engineOptions?: EngineOptions,
|
||||
): Promise<number | null> {
|
||||
return null;
|
||||
}
|
||||
@@ -142,6 +146,7 @@ export class GenericCameraManagerEngine implements CameraManagerEngine {
|
||||
_hass: HomeAssistant,
|
||||
_cameras: CameraConfigs,
|
||||
_query: MediaMetadataQuery,
|
||||
_engineOptions?: EngineOptions,
|
||||
): Promise<MediaMetadataQueryResultsMap | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
Engine,
|
||||
MediaMetadataQuery,
|
||||
MediaMetadataQueryResults,
|
||||
EngineOptions,
|
||||
} from './types.js';
|
||||
import orderBy from 'lodash-es/orderBy';
|
||||
import { CameraManagerEngineFactory } from './engine-factory.js';
|
||||
@@ -361,31 +362,35 @@ export class CameraManager {
|
||||
public async getEvents(
|
||||
hass: HomeAssistant,
|
||||
query: EventQuery | EventQuery[],
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<EventQueryResultsMap> {
|
||||
return await this._handleQuery(hass, query);
|
||||
return await this._handleQuery(hass, query, engineOptions);
|
||||
}
|
||||
|
||||
public async getRecordings(
|
||||
hass: HomeAssistant,
|
||||
query: RecordingQuery | RecordingQuery[],
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<RecordingQueryResultsMap> {
|
||||
return await this._handleQuery(hass, query);
|
||||
return await this._handleQuery(hass, query, engineOptions);
|
||||
}
|
||||
|
||||
public async getRecordingSegments(
|
||||
hass: HomeAssistant,
|
||||
query: RecordingSegmentsQuery | RecordingSegmentsQuery[],
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<RecordingSegmentsQueryResultsMap> {
|
||||
return await this._handleQuery(hass, query);
|
||||
return await this._handleQuery(hass, query, engineOptions);
|
||||
}
|
||||
|
||||
public async executeMediaQueries<T extends MediaQuery>(
|
||||
hass: HomeAssistant,
|
||||
queries: T[],
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<ViewMedia[] | null> {
|
||||
return this._convertQueryResultsToMedia(
|
||||
hass,
|
||||
await this._handleQuery(hass, queries),
|
||||
await this._handleQuery(hass, queries, engineOptions),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -394,6 +399,7 @@ export class CameraManager {
|
||||
queries: T[],
|
||||
results: ViewMedia[],
|
||||
direction: 'earlier' | 'later',
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<ExtendedMediaQueryResult<T> | null> {
|
||||
const getTimeFromResults = (want: 'earliest' | 'latest'): Date | null => {
|
||||
let output: Date | null = null;
|
||||
@@ -446,7 +452,7 @@ export class CameraManager {
|
||||
|
||||
const newChunkMedia = this._convertQueryResultsToMedia(
|
||||
hass,
|
||||
await this._handleQuery(hass, newChunkQueries),
|
||||
await this._handleQuery(hass, newChunkQueries, engineOptions),
|
||||
);
|
||||
|
||||
if (!newChunkMedia.length) {
|
||||
@@ -554,6 +560,7 @@ export class CameraManager {
|
||||
protected async _handleQuery<QT extends DataQuery>(
|
||||
hass: HomeAssistant,
|
||||
query: QT | QT[],
|
||||
engineOptions?: EngineOptions,
|
||||
): Promise<Map<QT, QueryReturnType<QT>>> {
|
||||
const _queries = arrayify(query);
|
||||
const results = new Map<QT, QueryReturnType<QT>>();
|
||||
@@ -573,24 +580,28 @@ export class CameraManager {
|
||||
hass,
|
||||
this._store.getCameras(),
|
||||
query,
|
||||
engineOptions,
|
||||
)) as Map<QT, QueryReturnType<QT>> | null;
|
||||
} else if (QueryClassifier.isRecordingQuery(query)) {
|
||||
engineResult = (await engine.getRecordings(
|
||||
hass,
|
||||
this._store.getCameras(),
|
||||
query,
|
||||
engineOptions,
|
||||
)) as Map<QT, QueryReturnType<QT>> | null;
|
||||
} else if (QueryClassifier.isRecordingSegmentsQuery(query)) {
|
||||
engineResult = (await engine.getRecordingSegments(
|
||||
hass,
|
||||
this._store.getCameras(),
|
||||
query,
|
||||
engineOptions,
|
||||
)) as Map<QT, QueryReturnType<QT>> | null;
|
||||
} else if (QueryClassifier.isMediaMetadataQuery(query)) {
|
||||
engineResult = (await engine.getMediaMetadata(
|
||||
hass,
|
||||
this._store.getCameras(),
|
||||
query,
|
||||
engineOptions,
|
||||
)) as Map<QT, QueryReturnType<QT>> | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,10 @@ export interface CameraEndpoints {
|
||||
|
||||
export type CameraConfigs = Map<string, CameraConfig>;
|
||||
|
||||
export interface EngineOptions {
|
||||
useCache?: boolean;
|
||||
}
|
||||
|
||||
// ===========
|
||||
// Event Query
|
||||
// ===========
|
||||
@@ -148,7 +152,7 @@ export interface EventQuery extends MediaQuery {
|
||||
|
||||
// Frigate equivalent: sub_label
|
||||
tags?: Set<string>;
|
||||
|
||||
|
||||
// Frigate equivalent: zone
|
||||
where?: Set<string>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user