Move more static information into the media.

This commit is contained in:
Dermot Duffy
2023-01-24 19:36:54 -08:00
parent 7896dfeb3c
commit a718e5febe
9 changed files with 119 additions and 97 deletions
+5 -1
View File
@@ -32,7 +32,11 @@ export class CameraManagerEngineFactory {
return cameraConfig ? this.getEngineForCamera(cameraConfig) : null;
}
public getEngineForCamera(cameraConfig: CameraConfig): CameraManagerEngine | null {
public getEngineForCamera(cameraConfig?: CameraConfig): CameraManagerEngine | null {
if (!cameraConfig) {
return null;
}
let engineKey: string | null = null;
if (cameraConfig.frigate.camera_name) {
engineKey = 'frigate';
+2
View File
@@ -53,11 +53,13 @@ export interface CameraManagerEngine {
): Promise<QueryReturnType<RecordingSegmentsQuery> | null>;
generateMediaFromEvents(
cameraConfig: CameraConfig,
query: EventQuery,
results: QueryReturnType<EventQuery>,
): ViewMedia[] | null;
generateMediaFromRecordings(
cameraConfig: CameraConfig,
query: RecordingQuery,
results: QueryReturnType<RecordingQuery>,
): ViewMedia[] | null;
+5 -2
View File
@@ -161,12 +161,11 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine {
media: ViewMedia,
favorite: boolean,
): Promise<void> {
const clientID = cameraConfig.frigate.client_id;
if (!FrigateViewMediaClassifier.isFrigateEvent(media)) {
return;
}
await retainEvent(hass, clientID, media.getID(cameraConfig), favorite);
await retainEvent(hass, cameraConfig.frigate.client_id, media.getID(), favorite);
media.setFavorite(favorite);
}
@@ -309,6 +308,7 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine {
}
public generateMediaFromEvents(
cameraConfig: CameraConfig,
query: EventQuery,
results: QueryReturnType<EventQuery>,
): ViewMedia[] | null {
@@ -336,6 +336,7 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine {
const media = FrigateViewMediaFactory.createEventViewMedia(
mediaType,
query.cameraID,
cameraConfig,
event,
);
if (media) {
@@ -346,6 +347,7 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine {
}
public generateMediaFromRecordings(
cameraConfig: CameraConfig,
query: RecordingQuery,
results: QueryReturnType<RecordingQuery>,
): ViewMedia[] | null {
@@ -358,6 +360,7 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine {
const media = FrigateViewMediaFactory.createRecordingViewMedia(
query.cameraID,
recording,
cameraConfig,
);
if (media) {
output.push(media);
+75 -73
View File
@@ -12,16 +12,27 @@ import {
getEventMediaContentID,
getEventThumbnailURL,
getEventTitle,
getRecordingID,
getRecordingMediaContentID,
getRecordingTitle,
} from './util';
export class FrigateEventViewMedia extends ViewMedia implements EventViewMedia {
protected _event: FrigateEvent;
protected _contentID: string;
protected _thumbnail: string;
constructor(mediaType: ViewMediaType, cameraID: string, event: FrigateEvent) {
constructor(
mediaType: ViewMediaType,
cameraID: string,
event: FrigateEvent,
contentID: string,
thumbnail: string,
) {
super(mediaType, cameraID);
this._event = event;
this._contentID = contentID;
this._thumbnail = thumbnail;
}
public hasClip(): boolean {
@@ -33,36 +44,17 @@ export class FrigateEventViewMedia extends ViewMedia implements EventViewMedia {
public getEndTime(): Date | null {
return this._event.end_time ? fromUnixTime(this._event.end_time) : null;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getID(_cameraConfig?: CameraConfig): string {
public getID(): string {
return this._event.id;
}
public getContentID(cameraConfig?: CameraConfig): string | null {
if (
!cameraConfig ||
!cameraConfig.frigate.client_id ||
!cameraConfig.frigate.camera_name
) {
return null;
public getContentID(): string {
return this._contentID;
}
return getEventMediaContentID(
cameraConfig.frigate.client_id,
cameraConfig.frigate.camera_name,
this._event,
this._mediaType === 'clip' ? 'clips' : 'snapshots',
);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getTitle(_cameraConfig?: CameraConfig): string | null {
public getTitle(): string | null {
return getEventTitle(this._event);
}
public getThumbnail(cameraConfig?: CameraConfig): string | null {
if (cameraConfig?.frigate.client_id) {
return getEventThumbnailURL(cameraConfig.frigate.client_id, this._event);
}
return null;
public getThumbnail(): string | null {
return this._thumbnail;
}
public isFavorite(): boolean | null {
return this._event.retain_indefinitely ?? null;
@@ -89,37 +81,31 @@ export class FrigateEventViewMedia extends ViewMedia implements EventViewMedia {
isEqual(this.getWhat(), that.getWhat())
);
}
public getClipEquivalent(): EventViewMedia | null {
if (!this.hasClip()) {
return null;
}
return FrigateViewMediaFactory.createEventViewMedia(
'clip',
this._cameraID,
this._event,
);
}
}
export class FrigateRecordingViewMedia extends ViewMedia implements RecordingViewMedia {
protected _recording: FrigateRecording;
protected _id: string;
protected _contentID: string;
protected _title: string;
constructor(mediaType: ViewMediaType, cameraID: string, recording: FrigateRecording) {
constructor(
mediaType: ViewMediaType,
cameraID: string,
recording: FrigateRecording,
id: string,
contentID: string,
title: string,
) {
super(mediaType, cameraID);
this._recording = recording;
this._id = id;
this._contentID = contentID;
this._title = title;
}
public getID(cameraConfig?: CameraConfig): string | null {
// ID name is derived from the real camera name (not CameraID) since the
// recordings for the same camera across multiple zones will be the same and
// can be dedup'd from this id.
if (cameraConfig) {
return `${cameraConfig.frigate?.client_id ?? ''}/${
cameraConfig.frigate.camera_name ?? ''
}/${this._recording.startTime.getTime()}/${this._recording.endTime.getTime()}}`;
}
return null;
public getID(): string {
return this._id;
}
public getStartTime(): Date {
return this._recording.startTime;
@@ -127,26 +113,11 @@ export class FrigateRecordingViewMedia extends ViewMedia implements RecordingVie
public getEndTime(): Date {
return this._recording.endTime;
}
public getContentID(cameraConfig?: CameraConfig): string | null {
if (
!cameraConfig ||
!cameraConfig.frigate.client_id ||
!cameraConfig.frigate.camera_name
) {
return null;
public getContentID(): string | null {
return this._contentID;
}
return getRecordingMediaContentID(
cameraConfig.frigate.client_id,
cameraConfig.frigate.camera_name,
this._recording,
);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getTitle(cameraConfig?: CameraConfig): string | null {
if (!cameraConfig) {
return null;
}
return getRecordingTitle(cameraConfig, this._recording);
public getTitle(): string | null {
return this._title;
}
public getEventCount(): number {
return this._recording.events;
@@ -155,23 +126,54 @@ export class FrigateRecordingViewMedia extends ViewMedia implements RecordingVie
export class FrigateViewMediaFactory {
static createEventViewMedia(
type: 'clip' | 'snapshot',
mediaType: 'clip' | 'snapshot',
cameraID: string,
cameraConfig: CameraConfig,
event: FrigateEvent,
): FrigateEventViewMedia | null {
if (
(type === 'clip' && event.has_clip) ||
(type === 'snapshot' && event.has_snapshot)
(mediaType === 'clip' && !event.has_clip) ||
(mediaType === 'snapshot' && !event.has_snapshot) ||
!cameraConfig.frigate.client_id ||
!cameraConfig.frigate.camera_name
) {
return new FrigateEventViewMedia(type, cameraID, event);
}
return null;
}
return new FrigateEventViewMedia(
mediaType,
cameraID,
event,
getEventMediaContentID(
cameraConfig.frigate.client_id,
cameraConfig.frigate.camera_name,
event,
mediaType === 'clip' ? 'clips' : 'snapshots',
),
getEventThumbnailURL(cameraConfig.frigate.client_id, event),
);
}
static createRecordingViewMedia(
cameraID: string,
recording: FrigateRecording,
cameraConfig: CameraConfig,
): FrigateRecordingViewMedia | null {
return new FrigateRecordingViewMedia('recording', cameraID, recording);
if (!cameraConfig.frigate.client_id || !cameraConfig.frigate.camera_name) {
return null;
}
return new FrigateRecordingViewMedia(
'recording',
cameraID,
recording,
getRecordingID(cameraConfig, recording),
getRecordingMediaContentID(
cameraConfig.frigate.client_id,
cameraConfig.frigate.camera_name,
recording,
),
getRecordingTitle(cameraConfig, recording),
);
}
}
+15
View File
@@ -82,3 +82,18 @@ export const getRecordingMediaContentID = (
String(recording.startTime.getHours()).padStart(2, '0'),
].join('/');
};
/**
* Get a recording ID for internal de-duping.
*/
export const getRecordingID = (
cameraConfig: CameraConfig,
recording: FrigateRecording,
): string => {
// ID name is derived from the real camera name (not CameraID) since the
// recordings for the same camera across multiple zones will be the same and
// can be dedup'd from this id.
return `${cameraConfig.frigate?.client_id ?? ''}/${
cameraConfig.frigate.camera_name ?? ''
}/${recording.startTime.getTime()}/${recording.endTime.getTime()}}`;
};
+7 -5
View File
@@ -184,19 +184,21 @@ export class CameraManager {
const mediaArray: ViewMedia[] = [];
for (const [query, result] of results.entries()) {
const engine = this._engineFactory.getEngineForQuery(this._cameras, query);
if (engine) {
const cameraConfig = this._cameras.get(query.cameraID);
const engine = this._engineFactory.getEngineForCamera(cameraConfig);
if (engine && cameraConfig) {
let media: ViewMedia[] | null = null;
if (
QueryClassifier.isEventQuery(query) &&
QueryResultClassifier.isEventQueryResult(result)
) {
media = engine.generateMediaFromEvents(query, result);
media = engine.generateMediaFromEvents(cameraConfig, query, result);
} else if (
QueryClassifier.isRecordingQuery(query) &&
QueryResultClassifier.isRecordingQuery(result)
) {
media = engine.generateMediaFromRecordings(query, result);
media = engine.generateMediaFromRecordings(cameraConfig, query, result);
}
if (media) {
mediaArray.push(...media);
@@ -210,7 +212,7 @@ export class CameraManager {
// are assumed to be unique.
uniqBy(
mediaArray,
(media) => media.getID(this._cameras.get(media.getCameraID())) ?? media,
(media) => media.getID() ?? media,
),
// Sort all items leading with the most recent.
+3 -3
View File
@@ -1402,10 +1402,10 @@ export class FrigateCard extends LitElement {
const media = this._view.queryResults?.getSelectedResult();
if (this._view.isViewerView() && media && this._cameras) {
media_content_id = media.getContentID(cameraConfig);
media_content_id = media.getContentID();
media_content_type = media.getContentType();
title = media.getTitle(cameraConfig);
thumbnail = media.getThumbnail(cameraConfig);
title = media.getTitle();
thumbnail = media.getThumbnail();
} else if (this._view?.is('live') && cameraEntity) {
media_content_id = `media-source://camera/${cameraEntity}`;
media_content_type = 'application/vnd.apple.mpegurl';
+3 -4
View File
@@ -256,8 +256,8 @@ export class FrigateCardThumbnail extends LitElement {
return;
}
const thumbnail = this.media.getThumbnail(this.cameraConfig);
const title = this.media.getTitle(this.cameraConfig) ?? '';
const thumbnail = this.media.getThumbnail();
const title = this.media.getTitle() ?? '';
const starClasses = {
star: true,
@@ -271,7 +271,6 @@ export class FrigateCardThumbnail extends LitElement {
// Only show timeline control if the recording has a start & end time.
(this.media.getStartTime() && this.media.getEndTime()));
const clientID = this.cameraConfig?.frigate.client_id;
return html` ${ViewMediaClassifier.isEvent(this.media)
? html`<frigate-card-thumbnail-feature-event
aria-label="${title ?? ''}"
@@ -289,7 +288,7 @@ export class FrigateCardThumbnail extends LitElement {
.date=${this.media.getStartTime() ?? undefined}
></frigate-card-thumbnail-feature-recording>`
: html``}
${this.show_favorite_control && this.media && this.hass && clientID
${this.show_favorite_control && this.media && this.hass
? html` <ha-icon
class="${classMap(starClasses)}"
icon=${this.media.isFavorite() ? 'mdi:star' : 'mdi:star-outline'}
+4 -9
View File
@@ -1,4 +1,3 @@
import { CameraConfig } from '../types.js';
export type ViewMediaType = 'clip' | 'snapshot' | 'recording';
export class ViewMedia {
@@ -18,8 +17,7 @@ export class ViewMedia {
public getMediaType(): ViewMediaType {
return this._mediaType;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getID(_cameraConfig?: CameraConfig): string | null {
public getID(): string | null {
return null;
}
public getStartTime(): Date | null {
@@ -28,16 +26,13 @@ export class ViewMedia {
public getEndTime(): Date | null {
return null;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getContentID(_cameraConfig?: CameraConfig): string | null {
public getContentID(): string | null {
return null;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getTitle(_cameraConfig?: CameraConfig): string | null {
public getTitle(): string | null {
return null;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getThumbnail(_cameraConfig?: CameraConfig): string | null {
public getThumbnail(): string | null {
return null;
}
public isFavorite(): boolean | null {