feat: Add support for Frigate reviews / detections [initial PR] (#2315)

- Add support for Frigate reviews / detections.
 - Add support for GenAI metadata.
- Significant internal refactor to more flexible "UnifiedQuery" to allow
mixing cameras with simple metadata and review metadata (e.g. a timeline
view of a Frigate camera with reviews, and a Reolink camera with simple
metadata).
 - Add support for folder media as camera media.

There are a few more PRs to commit prior to this going live, but
commiting this for now due to the scale of the change.

BREAKING CHANGE: `media_type` and `events_type` are retired under
`live`, `viewer` and `timeline` configuration sections, instead media
type is associated (once) with the camera under `media`.
This commit is contained in:
Dermot Duffy
2026-01-19 13:32:50 -08:00
committed by GitHub
parent 6eb0a87ca8
commit fc32727860
215 changed files with 14491 additions and 6160 deletions
+26 -10
View File
@@ -104,14 +104,16 @@ export class TriggersManager {
.actions.trigger;
const defaultView = this._api.getConfigManager().getConfig()?.view.default;
// If this is a high-fidelity event where we are certain about new media,
// don't take action unless it's to change to live (Frigate engine may pump
// out events where there's no new media to show). Other trigger actions
// (e.g. media, update) do not make sense without having some new media.
// Early exit guard: If this is a high-fidelity event where we are certain
// about new media, don't take action unless it's to change to live (Frigate
// engine may pump out events where there's no new media to show). Other
// trigger actions (e.g. media, update) do not make sense without having
// some new media.
if (
ev.fidelity === 'high' &&
!ev.snapshot &&
!ev.clip &&
!ev.review &&
!(
triggerAction === 'live' ||
(triggerAction === 'default' && defaultView === 'live')
@@ -139,12 +141,26 @@ export class TriggersManager {
},
});
} else if (ev.fidelity === 'high' && triggerAction === 'media') {
await this._api.getViewManager().setViewByParametersWithNewQuery({
params: {
view: ev.clip ? 'clip' : 'snapshot',
camera: ev.cameraID,
},
});
// Choose the most appropriate media view based on what's available.
// Priority: review > clip > snapshot
const view = ev.review
? 'review'
: ev.clip
? 'clip'
: ev.snapshot
? 'snapshot'
: /* istanbul ignore next: unreachable due to early exit guard above -- @preserve */
null;
/* istanbul ignore next: unreachable due to early exit guard above -- @preserve */
if (view) {
await this._api.getViewManager().setViewByParametersWithNewQuery({
params: {
view,
camera: ev.cameraID,
},
});
}
}
}