feat: Add cameras[].media.severities to filter review media by severity
Review queries were issued without a severity filter, so the thumbnail
bar, gallery and timeline showed every Frigate review item -- alerts and
detections alike -- regardless of what a camera is configured to alert
on. Severity could only be narrowed interactively, in the media filter
drawer, and that choice did not persist.
`media.severities` resolves into the default query the same way
`media.reviewed` does, and is overridden by an explicit severity filter
so the media filter drawer keeps working. Unset means all severities, so
existing behaviour is unchanged.
cameras:
- camera_entity: camera.office
media:
severities:
- high
This commit is contained in:
@@ -700,6 +700,91 @@ describe('UnifiedQueryBuilder', () => {
|
||||
expect(query.getNodes()[0]).not.toHaveProperty('reviewed');
|
||||
});
|
||||
|
||||
it('should apply severities filter from config for reviews query', () => {
|
||||
const { cameraManager, foldersManager, store } = createMocks();
|
||||
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
||||
store.getCameraConfig.mockReturnValue(
|
||||
createCameraConfig({ media: { type: 'auto', severities: ['high'] } }),
|
||||
);
|
||||
|
||||
cameraManager.getCameraCapabilities.mockReturnValue(
|
||||
createCapabilities({ reviews: true }),
|
||||
);
|
||||
|
||||
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
||||
const query = builder.buildDefaultCameraQuery();
|
||||
|
||||
assert(query);
|
||||
expect(query.getNodes()[0]).toMatchObject({
|
||||
type: QueryType.Review,
|
||||
severity: new Set(['high']),
|
||||
});
|
||||
});
|
||||
|
||||
it('should not apply severities filter when unset in config', () => {
|
||||
const { cameraManager, foldersManager, store } = createMocks();
|
||||
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
||||
store.getCameraConfig.mockReturnValue(
|
||||
createCameraConfig({ media: { type: 'auto' } }),
|
||||
);
|
||||
|
||||
cameraManager.getCameraCapabilities.mockReturnValue(
|
||||
createCapabilities({ reviews: true }),
|
||||
);
|
||||
|
||||
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
||||
const query = builder.buildDefaultCameraQuery();
|
||||
|
||||
assert(query);
|
||||
assert(isReviewQuery(query.getNodes()[0]));
|
||||
expect(query.getNodes()[0]).not.toHaveProperty('severity');
|
||||
});
|
||||
|
||||
it('should not apply severities filter to non-review queries', () => {
|
||||
const { cameraManager, foldersManager, store } = createMocks();
|
||||
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
||||
store.getCameraConfig.mockReturnValue(
|
||||
createCameraConfig({ media: { type: 'auto', severities: ['high'] } }),
|
||||
);
|
||||
|
||||
cameraManager.getCameraCapabilities.mockReturnValue(
|
||||
createCapabilities({ clips: true }),
|
||||
);
|
||||
|
||||
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
||||
const query = builder.buildDefaultCameraQuery();
|
||||
|
||||
assert(query);
|
||||
expect(query.getNodes()[0]).toMatchObject({
|
||||
type: QueryType.Event,
|
||||
hasClip: true,
|
||||
});
|
||||
expect(query.getNodes()[0]).not.toHaveProperty('severity');
|
||||
});
|
||||
|
||||
it('should prefer an explicit severity option over the config', () => {
|
||||
const { cameraManager, foldersManager, store } = createMocks();
|
||||
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
||||
store.getCameraConfig.mockReturnValue(
|
||||
createCameraConfig({ media: { type: 'auto', severities: ['high'] } }),
|
||||
);
|
||||
|
||||
cameraManager.getCameraCapabilities.mockReturnValue(
|
||||
createCapabilities({ reviews: true }),
|
||||
);
|
||||
|
||||
const builder = new UnifiedQueryBuilder(cameraManager, foldersManager);
|
||||
const query = builder.buildDefaultCameraQuery(undefined, {
|
||||
severity: new Set(['medium'] as const),
|
||||
});
|
||||
|
||||
assert(query);
|
||||
expect(query.getNodes()[0]).toMatchObject({
|
||||
type: QueryType.Review,
|
||||
severity: new Set(['medium']),
|
||||
});
|
||||
});
|
||||
|
||||
it('should build clips query for camera with clips capability', () => {
|
||||
const { cameraManager, foldersManager, store } = createMocks();
|
||||
store.getCameraIDs.mockReturnValue(new Set(['camera.office']));
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { UnifiedQuery } from '../../../src/view/unified-query';
|
||||
import {
|
||||
getReviewedQueryFilterFromConfig,
|
||||
getReviewedQueryFilterFromQuery,
|
||||
getSeverityQueryFilterFromConfig,
|
||||
} from '../../../src/view/utils/query-filter';
|
||||
import { createFolder } from '../../test-utils';
|
||||
import { createEventQuery, TestViewMedia } from '../test-utils';
|
||||
@@ -81,4 +82,21 @@ describe('query-filter', () => {
|
||||
expect(getReviewedQueryFilterFromConfig('unreviewed')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSeverityQueryFilterFromConfig', () => {
|
||||
it('should return undefined when unset', () => {
|
||||
expect(getSeverityQueryFilterFromConfig(undefined)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined when empty', () => {
|
||||
expect(getSeverityQueryFilterFromConfig([])).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return a set of the configured severities', () => {
|
||||
expect(getSeverityQueryFilterFromConfig(['high'])).toEqual(new Set(['high']));
|
||||
expect(getSeverityQueryFilterFromConfig(['high', 'medium'])).toEqual(
|
||||
new Set(['high', 'medium']),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user