Initial code for major engine refactor.

This commit is contained in:
Dermot Duffy
2023-01-24 19:36:54 -08:00
parent 03de7b473a
commit 093925cb40
39 changed files with 3506 additions and 2459 deletions
+57 -76
View File
@@ -53,8 +53,6 @@ import {
FrigateCardView,
FRIGATE_CARD_VIEWS_USER_SPECIFIED,
MediaLoadedInfo,
MEDIA_TYPE_IMAGE,
MEDIA_TYPE_VIDEO,
MESSAGE_TYPE_PRIORITIES,
MenuButton,
Message,
@@ -80,7 +78,6 @@ import {
isTriggeredState,
sideLoadHomeAssistantElements,
} from './utils/ha';
import { getEventID } from './utils/ha/browse-media.js';
import { DeviceList, getAllDevices } from './utils/ha/device-registry.js';
import {
ExtendedEntityCache,
@@ -94,8 +91,10 @@ import { isValidMediaLoadedInfo } from './utils/media-info.js';
import { View } from './view.js';
import pkg from '../package.json';
import { ViewContext } from 'view';
import { DataManager } from './utils/data-manager.js';
import { DataManager } from './utils/data/data-manager.js';
import { setLowPerformanceProfile, setPerformanceCSSStyles } from './performance.js';
import { DataManagerEngineFactory } from './utils/data/data-manager-engine-factory.js';
import { RequestCache } from './utils/data/data-manager-cache.js';
/** A note on media callbacks:
*
@@ -144,6 +143,8 @@ console.info(
documentationURL: REPO_URL,
});
type InitializedType = 'initialized' | 'initializing';
/**
* Main FrigateCard class.
*/
@@ -204,7 +205,6 @@ export class FrigateCard extends LitElement {
// A cache of resolved media URLs/mimetypes for use in the whole card.
protected _resolvedMediaCache = new ResolvedMediaCache();
// Shared timeline data manager (for main timeline view and mini-timelines).
protected _dataManager?: DataManager;
// The mouse handler may be called continually, throttle it to at most once
@@ -212,8 +212,7 @@ export class FrigateCard extends LitElement {
protected _boundMouseHandler = throttle(this._mouseHandler.bind(this), 1 * 1000);
// Whether the card has been successfully initialized.
protected _loadedHAElements = false;
protected _loadedLanguages = false;
protected _initialized?: InitializedType;
protected _triggers: Map<string, Date> = new Map();
protected _untriggerTimerID: number | null = null;
@@ -530,7 +529,8 @@ export class FrigateCard extends LitElement {
if (
!this._isBeingCasted() &&
(this._view?.isViewerView() || (this._view?.is('timeline') && !!this._view?.media))
(this._view?.isViewerView() ||
(this._view?.is('timeline') && !!this._view?.queryResults?.hasSelectedResult()))
) {
buttons.push({
icon: 'mdi:download',
@@ -1016,6 +1016,7 @@ export class FrigateCard extends LitElement {
}
protected _changeView(args?: { view?: View; resetMessage?: boolean }): void {
console.debug(`Frigate Card view change: `, args?.view ?? '[default]');
const changeView = (view: View): void => {
if (View.isMediaChange(this._view, view)) {
this._currentMediaLoadedInfo = null;
@@ -1099,18 +1100,12 @@ export class FrigateCard extends LitElement {
* Called before each update.
*/
protected willUpdate(changedProps: PropertyValues): void {
// Side load the necessary elements if not already initialized (do not need
// to block for the loading to complete).
if (!this._loadedHAElements) {
sideLoadHomeAssistantElements().then((success) => {
if (success) {
this._loadedHAElements = true;
}
});
}
if (this._cameras && (changedProps.has('_config') || changedProps.has('_cameras'))) {
this._dataManager = new DataManager(this._cameras);
this._dataManager = new DataManager(
new DataManagerEngineFactory(),
this._cameras,
new RequestCache(),
);
}
if (changedProps.has('_cardWideConfig')) {
@@ -1237,6 +1232,12 @@ export class FrigateCard extends LitElement {
}
}
/**
* Initialize the card.
*/
protected async _initialize(): Promise<void> {
await Promise.all([sideLoadHomeAssistantElements(), loadLanguages()]);
}
/**
* Determine whether the element should be updated.
* @param changedProps The changed properties if any.
@@ -1244,11 +1245,13 @@ export class FrigateCard extends LitElement {
*/
protected shouldUpdate(changedProps: PropertyValues): boolean {
// Load the relevant languages. Cannot do anything until then.
if (!this._loadedLanguages) {
loadLanguages().then(() => {
this._loadedLanguages = true;
this.requestUpdate();
});
if (this._initialized !== 'initialized') {
if (this._initialized !== 'initializing') {
this._initialize().then(() => {
this._initialized = 'initialized';
this.requestUpdate();
});
}
return false;
}
@@ -1318,12 +1321,9 @@ export class FrigateCard extends LitElement {
// Should not occur.
return;
}
const media = this._view.queryResults?.getSelectedResult();
if (
!this._view.media ||
(this._view.media.media_content_type !== MEDIA_TYPE_VIDEO &&
this._view.media.media_content_type !== MEDIA_TYPE_IMAGE)
) {
if (!media) {
this._setMessageAndUpdate({
message: localize('error.download_no_media'),
type: 'error',
@@ -1336,35 +1336,8 @@ export class FrigateCard extends LitElement {
return;
}
let path: string;
if (this._view.media.frigate?.event) {
const event_id = getEventID(this._view.media);
if (!event_id) {
this._setMessageAndUpdate({
message: localize('error.download_no_event_id'),
type: 'error',
});
return;
}
path =
`/api/frigate/${cameraConfig.frigate.client_id}` +
`/notifications/${event_id}/` +
`${
this._view.media.media_content_type === MEDIA_TYPE_VIDEO
? 'clip.mp4'
: 'snapshot.jpg'
}` +
`?download=true`;
} else if (this._view.media.frigate?.recording) {
const recording = this._view.media.frigate.recording;
path =
`/api/frigate/${cameraConfig.frigate.client_id}` +
`/recording/${cameraConfig.frigate.camera_name}` +
`/start/${recording.start_time}` +
`/end/${recording.end_time}` +
`?download=true`;
} else {
const path = this._dataManager?.getMediaDownloadPath(media);
if (!path) {
return;
}
@@ -1412,30 +1385,35 @@ export class FrigateCard extends LitElement {
* @returns
*/
protected _mediaPlayerAction(mediaPlayer: string, action: 'play' | 'stop'): void {
if (!['play', 'stop'].includes(action)) {
if (!['play', 'stop'].includes(action) || !this._view) {
return;
}
let media_content_id: string;
let media_content_type: string;
const extra = {};
const cameraConfig = this._getSelectedCameraConfig();
const cameraEntity = cameraConfig?.camera_entity ?? null;
let media_content_id: string | null = null;
let media_content_type: string | null = null;
let title: string | null = null;
let thumbnail: string | null = null;
if (this._view?.isViewerView() && this._view.media) {
media_content_id = this._view.media.media_content_id;
media_content_type = this._view.media.media_content_type;
extra['thumb'] = this._view.media.thumbnail;
extra['title'] = this._view.media.title;
const cameraConfig = this._getSelectedCameraConfig();
if (!cameraConfig) {
return;
}
const cameraEntity = cameraConfig.camera_entity ?? null;
const media = this._view.queryResults?.getSelectedResult();
if (this._view.isViewerView() && media && this._cameras) {
media_content_id = media.getContentID(cameraConfig);
media_content_type = media.getContentType();
title = media.getTitle(cameraConfig);
thumbnail = media.getThumbnail(cameraConfig);
} else if (this._view?.is('live') && cameraEntity) {
if (this._hass?.states && cameraEntity in this._hass.states) {
extra['thumb'] =
this._hass.states[cameraEntity].attributes.entity_picture ?? null;
}
extra['title'] = getCameraTitle(this._hass, cameraConfig);
media_content_id = `media-source://camera/${cameraEntity}`;
media_content_type = 'application/vnd.apple.mpegurl';
} else {
title = getCameraTitle(this._hass, cameraConfig);
thumbnail = this._hass?.states[cameraEntity]?.attributes?.entity_picture ?? null;
}
if (!media_content_id || !media_content_type) {
return;
}
@@ -1444,7 +1422,10 @@ export class FrigateCard extends LitElement {
entity_id: mediaPlayer,
media_content_id: media_content_id,
media_content_type: media_content_type,
extra: extra,
extra: {
...(title && { title: title }),
...(thumbnail && { thumb: thumbnail }),
},
});
} else if (action === 'stop') {
this._hass?.callService('media_player', 'media_stop', {