Add a performance option to control media chunk size.

This commit is contained in:
Dermot Duffy
2023-02-13 21:27:39 -08:00
parent 5910c09383
commit 03cea41675
20 changed files with 311 additions and 263 deletions
+48 -51
View File
@@ -33,12 +33,10 @@ import { EventQuery, MediaQuery, RecordingQuery } from '../camera-manager/types'
import { MediaQueriesResults } from '../view/media-queries-results';
import { errorToConsole } from '../utils/basic';
import './media-filter';
import "./surround-basic";
import './surround-basic';
import { ViewMedia } from '../view/media';
import { localize } from '../localize/localize';
const GALLERY_MEDIA_CHUNK_SIZE = 100;
const GALLERY_MEDIA_FILTER_MENU_ICONS = {
closed: 'mdi:filter-cog-outline',
open: 'mdi:filter-cog',
@@ -70,7 +68,8 @@ export class FrigateCardGallery extends LitElement {
!this.hass ||
!this.view ||
!this.view.isGalleryView() ||
!this.cameraManager
!this.cameraManager ||
!this.cardWideConfig
) {
return;
}
@@ -81,18 +80,20 @@ export class FrigateCardGallery extends LitElement {
this,
this.hass,
this.cameraManager,
this.cardWideConfig,
this.view,
);
} else {
const mediaType = this.view.is('snapshots')
? 'snapshots'
: this.view.is('clips')
? 'clips'
: null;
? 'clips'
: null;
changeViewToRecentEventsForCameraAndDependents(
this,
this.hass,
this.cameraManager,
this.cardWideConfig,
this.view,
{
...(mediaType && { mediaType: mediaType }),
@@ -105,22 +106,22 @@ export class FrigateCardGallery extends LitElement {
return html`
<frigate-card-surround-basic
.drawerIcons=${{
...(this.galleryConfig &&
this.galleryConfig.controls.filter.mode !== 'none' && {
[this.galleryConfig.controls.filter.mode]: GALLERY_MEDIA_FILTER_MENU_ICONS,
}),
}}
...(this.galleryConfig &&
this.galleryConfig.controls.filter.mode !== 'none' && {
[this.galleryConfig.controls.filter.mode]: GALLERY_MEDIA_FILTER_MENU_ICONS,
}),
}}
>
${this.galleryConfig && this.galleryConfig.controls.filter.mode !== 'none'
? html` <frigate-card-media-filter
? html` <frigate-card-media-filter
.hass=${this.hass}
.cameraManager=${this.cameraManager}
.view=${this.view}
.mediaLimit=${GALLERY_MEDIA_CHUNK_SIZE}
.cardWideConfig=${this.cardWideConfig}
slot=${this.galleryConfig.controls.filter.mode}
>
</frigate-card-media-filter>`
: ''}
: ''}
<frigate-card-gallery-core
.hass=${this.hass}
.view=${this.view}
@@ -212,10 +213,10 @@ export class FrigateCardGalleryCore extends LitElement {
const columns = this.galleryConfig?.controls.thumbnails.show_details
? Math.max(1, Math.floor(this.clientWidth / THUMBNAIL_DETAILS_WIDTH_MIN))
: Math.max(
1,
Math.ceil(this.clientWidth / THUMBNAIL_WIDTH_MAX),
Math.ceil(this.clientWidth / thumbnailSize),
);
1,
Math.ceil(this.clientWidth / THUMBNAIL_WIDTH_MAX),
Math.ceil(this.clientWidth / thumbnailSize),
);
this.style.setProperty('--frigate-card-gallery-columns', String(columns));
}
@@ -253,7 +254,6 @@ export class FrigateCardGalleryCore extends LitElement {
rawQueries,
existingMedia,
'earlier',
GALLERY_MEDIA_CHUNK_SIZE,
);
} catch (e) {
errorToConsole(e as Error);
@@ -264,8 +264,8 @@ export class FrigateCardGalleryCore extends LitElement {
const newMediaQueries = MediaQueriesClassifier.areEventQueries(query)
? new EventMediaQueries(extension.queries as EventQuery[])
: MediaQueriesClassifier.areRecordingQueries(query)
? new RecordingMediaQueries(extension.queries as RecordingQuery[])
: null;
? new RecordingMediaQueries(extension.queries as RecordingQuery[])
: null;
if (newMediaQueries) {
this.view
@@ -301,11 +301,13 @@ export class FrigateCardGalleryCore extends LitElement {
this._showExtensionLoader = true;
const oldView: View | undefined = changedProps.get('view');
if (oldView?.queryResults?.getResults() !== this.view?.queryResults?.getResults()) {
if (
oldView?.queryResults?.getResults() !== this.view?.queryResults?.getResults()
) {
// Gallery places the most recent media at the top (the query results place
// the most recent media at the end for use in the viewer). This is copied
// to a new array to avoid reversing the query results in place.
this._media = [...this.view?.queryResults?.getResults() ?? []].reverse();
this._media = [...(this.view?.queryResults?.getResults() ?? [])].reverse();
}
}
}
@@ -315,12 +317,7 @@ export class FrigateCardGalleryCore extends LitElement {
* @returns A rendered template.
*/
protected render(): TemplateResult | void {
if (
!this._media ||
!this.hass ||
!this.view ||
!this.view.isGalleryView()
) {
if (!this._media || !this.hass || !this.view || !this.view.isGalleryView()) {
return html``;
}
@@ -332,40 +329,40 @@ export class FrigateCardGalleryCore extends LitElement {
return html`
${this._media.map(
(media, index) =>
html`<frigate-card-thumbnail
(media, index) =>
html`<frigate-card-thumbnail
.hass=${this.hass}
.cameraManager=${this.cameraManager}
.media=${media}
.view=${this.view}
?details=${!!this.galleryConfig?.controls.thumbnails.show_details}
?show_favorite_control=${!!this.galleryConfig?.controls.thumbnails
.show_favorite_control}
.show_favorite_control}
?show_timeline_control=${!!this.galleryConfig?.controls.thumbnails
.show_timeline_control}
.show_timeline_control}
@click=${(ev: Event) => {
if (this.view && this._media) {
this.view
.evolve({
view: 'media',
queryResults: this.view.queryResults?.clone().selectResult(
// Media in the gallery is reversed vs the queryResults (see
// note above).
this._media.length - index - 1
),
})
.dispatchChangeEvent(this);
}
stopEventFromActivatingCardWideActions(ev);
}}
if (this.view && this._media) {
this.view
.evolve({
view: 'media',
queryResults: this.view.queryResults?.clone().selectResult(
// Media in the gallery is reversed vs the queryResults (see
// note above).
this._media.length - index - 1,
),
})
.dispatchChangeEvent(this);
}
stopEventFromActivatingCardWideActions(ev);
}}
>
</frigate-card-thumbnail>`,
)}
)}
${this._showExtensionLoader
? html`${renderProgressIndicator({
cardWideConfig: this.cardWideConfig,
componentRef: this._refLoader,
})}`
cardWideConfig: this.cardWideConfig,
componentRef: this._refLoader,
})}`
: ''}
`;
}
+1
View File
@@ -228,6 +228,7 @@ export class FrigateCardLive extends LitElement {
.timelineConfig=${config.controls.timeline}
.cameraManager=${this.cameraManager}
.inBackground=${this._inBackground}
.cardWideConfig=${this.cardWideConfig}
@frigate-card:message=${(ev: CustomEvent<Message>) => {
this._renderKey++;
this._messageReceivedPostRender = true;
+40 -26
View File
@@ -13,7 +13,7 @@ import { createRef, ref, Ref } from 'lit/directives/ref.js';
import { DateRange } from '../camera-manager/range';
import { localize } from '../localize/localize';
import mediaFilterStyle from '../scss/media-filter.scss';
import { createViewForEvents, createViewForRecordings } from '../utils/media-to-view.js';
import { executeMediaQueryForView } from '../utils/media-to-view.js';
import { errorToConsole, formatDate, prettifyTitle } from '../utils/basic';
import { ScopedRegistryHost } from '@lit-labs/scoped-registry-mixin';
import './select';
@@ -32,10 +32,8 @@ import { View } from '../view/view';
import { CameraManager } from '../camera-manager/manager';
import { HomeAssistant } from 'custom-card-helpers';
import {
EventQuery,
MediaMetadata,
QueryType,
RecordingQuery,
} from '../camera-manager/types';
import format from 'date-fns/format';
import endOfMonth from 'date-fns/endOfMonth';
@@ -43,6 +41,7 @@ import isEqual from 'lodash-es/isEqual';
import { EventMediaQueries, RecordingMediaQueries } from '../view/media-queries';
import './select.js';
import orderBy from 'lodash-es/orderBy';
import { CardWideConfig } from '../types';
interface MediaFilterCoreDefaults {
mediaType?: MediaFilterMediaType;
@@ -83,7 +82,7 @@ class FrigateCardMediaFilter extends ScopedRegistryHost(LitElement) {
public view?: View;
@property({ attribute: false })
public mediaLimit?: number;
public cardWideConfig?: CardWideConfig;
static elementDefinitions = {
'frigate-card-select': FrigateCardSelect,
@@ -202,6 +201,8 @@ class FrigateCardMediaFilter extends ScopedRegistryHost(LitElement) {
// - Similarly, if the user chooses clips or snapshots, set the actual view
// to 'clips' or 'snapshots' in order to ensure the right icon is shown as
// selected in the menu.
const limit = this.cardWideConfig?.performance?.features.media_chunk_size;
if (
mediaType === MediaFilterMediaType.Clips ||
mediaType === MediaFilterMediaType.Snapshots
@@ -209,7 +210,7 @@ class FrigateCardMediaFilter extends ScopedRegistryHost(LitElement) {
const where = getArrayValueAsSet(this._refWhere.value?.value);
const what = getArrayValueAsSet(this._refWhat.value?.value);
const queries: EventQuery[] = [
const queries = new EventMediaQueries([
{
type: QueryType.Event,
cameraIDs: cameraIDs,
@@ -217,38 +218,51 @@ class FrigateCardMediaFilter extends ScopedRegistryHost(LitElement) {
...(where && { where: where }),
...(favorite !== null && { favorite: favorite }),
...(when && { start: when.start, end: when.end }),
...(this.mediaLimit && { limit: this.mediaLimit }),
...(limit && { limit: limit }),
...(mediaType === MediaFilterMediaType.Clips && { hasClip: true }),
...(mediaType === MediaFilterMediaType.Snapshots && {
hasSnapshot: true,
}),
},
];
]);
(
await createViewForEvents(this, this.hass, this.cameraManager, this.view, {
query: new EventMediaQueries(queries),
// See 'A note on views' above for these two arguments.
...(cameraIDs.size === 1 && { targetCameraID: [...cameraIDs][0] }),
targetView: mediaType === MediaFilterMediaType.Clips ? 'clips' : 'snapshots',
})
await executeMediaQueryForView(
this,
this.hass,
this.cameraManager,
this.view,
queries,
{
// See 'A note on views' above for these two arguments.
...(cameraIDs.size === 1 && { targetCameraID: [...cameraIDs][0] }),
targetView: mediaType === MediaFilterMediaType.Clips ? 'clips' : 'snapshots',
},
)
)?.dispatchChangeEvent(this);
} else if (mediaType === MediaFilterMediaType.Recordings) {
const query: RecordingQuery = {
type: QueryType.Recording,
cameraIDs: cameraIDs,
...(when && { start: when.start, end: when.end }),
};
const queries = new RecordingMediaQueries([
{
type: QueryType.Recording,
cameraIDs: cameraIDs,
...(limit && { limit: limit }),
...(when && { start: when.start, end: when.end }),
},
]);
(
await createViewForRecordings(this, this.hass, this.cameraManager, this.view, {
query: new RecordingMediaQueries([query]),
// See 'A note on views' above for these two arguments.
...(cameraIDs.size === 1 && { targetCameraID: [...cameraIDs][0] }),
targetView: 'recordings',
})
await executeMediaQueryForView(
this,
this.hass,
this.cameraManager,
this.view,
queries,
{
// See 'A note on views' above for these two arguments.
...(cameraIDs.size === 1 && { targetCameraID: [...cameraIDs][0] }),
targetView: 'recordings',
},
)
)?.dispatchChangeEvent(this);
}
}
+8 -2
View File
@@ -9,6 +9,7 @@ import {
import { customElement, property } from 'lit/decorators.js';
import surroundStyle from '../scss/surround.scss';
import {
CardWideConfig,
ClipsOrSnapshotsOrAll,
ExtendedHomeAssistant,
MiniTimelineControlConfig,
@@ -57,6 +58,9 @@ export class FrigateCardSurround extends LitElement {
@property({ attribute: false })
public cameraManager?: CameraManager;
@property({ attribute: false })
public cardWideConfig?: CardWideConfig;
protected _cameraIDsForTimeline?: Set<string>;
/**
@@ -68,6 +72,7 @@ export class FrigateCardSurround extends LitElement {
protected async _fetchMedia(): Promise<void> {
if (
!this.cameraManager ||
!this.cardWideConfig ||
!this.fetchMedia ||
this.inBackground ||
!this.hass ||
@@ -83,6 +88,7 @@ export class FrigateCardSurround extends LitElement {
this,
this.hass,
this.cameraManager,
this.cardWideConfig,
this.view,
{
targetView: this.view.view,
@@ -218,9 +224,9 @@ export class FrigateCardSurround extends LitElement {
.cameraIDs=${this._cameraIDsForTimeline}
.mini=${true}
.timelineConfig=${this.timelineConfig}
.thumbnailDetails=${this.thumbnailConfig?.show_details}
.thumbnailSize=${this.thumbnailConfig?.size}
.thumbnailConfig=${this.thumbnailConfig}
.cameraManager=${this.cameraManager}
.cardWideConfig=${this.cardWideConfig}
>
</frigate-card-timeline-core>`
: ''}
+56 -38
View File
@@ -30,9 +30,11 @@ import { localize } from '../localize/localize';
import timelineCoreStyle from '../scss/timeline-core.scss';
import {
CameraConfig,
CardWideConfig,
ExtendedHomeAssistant,
frigateCardConfigDefaults,
FrigateCardView,
ThumbnailsControlConfig,
TimelineCoreConfig,
} from '../types';
import { stopEventFromActivatingCardWideActions } from '../utils/action';
@@ -41,10 +43,9 @@ import {
dispatchFrigateCardEvent,
isHoverableDevice,
} from '../utils/basic';
import {
createViewForEvents,
createViewForRecordings,
createQueriesForRecordingsView,
executeMediaQueryForView,
findClosestMediaIndex,
} from '../utils/media-to-view';
import { CameraManager } from '../camera-manager/manager';
@@ -168,10 +169,7 @@ export class FrigateCardTimelineCore extends LitElement {
public timelineConfig?: TimelineCoreConfig;
@property({ attribute: true, type: Boolean })
public thumbnailDetails? = false;
@property({ attribute: false })
public thumbnailSize?: number;
public thumbnailConfig?: ThumbnailsControlConfig;
// Whether or not this is a mini-timeline (in mini-mode the component takes a
// supportive role for other views).
@@ -186,6 +184,9 @@ export class FrigateCardTimelineCore extends LitElement {
@property({ attribute: false })
public cameraManager?: CameraManager;
@property({ attribute: false })
public cardWideConfig?: CardWideConfig;
@state()
protected _locked = false;
@@ -228,7 +229,7 @@ export class FrigateCardTimelineCore extends LitElement {
return `
<frigate-card-timeline-thumbnail
item='${item.id}'
${this.thumbnailDetails ? 'details' : ''}
${this.thumbnailConfig?.show_details ? 'details' : ''}
>
</frigate-card-timeline-thumbnail>`;
}
@@ -392,7 +393,6 @@ export class FrigateCardTimelineCore extends LitElement {
): Promise<void> {
const results = this.view?.queryResults;
const media = results?.getResults();
const cameraIDs = this._getTimelineCameraIDs();
if (
!media ||
!results ||
@@ -400,7 +400,6 @@ export class FrigateCardTimelineCore extends LitElement {
!this.view ||
!this.hass ||
!this.cameraManager ||
!cameraIDs ||
// Skip range changes that do not have hammerjs pan directions associated
// with them, as these outliers cause media matching issues below.
!properties.event.additionalEvent
@@ -418,7 +417,6 @@ export class FrigateCardTimelineCore extends LitElement {
findClosestMediaIndex(
media,
targetTime,
cameraIDs,
properties.event.additionalEvent === 'panright' ? 'end' : 'start',
),
);
@@ -467,6 +465,7 @@ export class FrigateCardTimelineCore extends LitElement {
!this._timeline ||
!this.view ||
!this.cameraManager ||
!this.cardWideConfig ||
!timelineCameraIDs ||
!properties.what
) {
@@ -479,34 +478,50 @@ export class FrigateCardTimelineCore extends LitElement {
this.timelineConfig?.show_recordings &&
['background', 'group-label'].includes(properties.what)
) {
view = await createViewForRecordings(
this,
this.hass,
const query = createQueriesForRecordingsView(
this.cameraManager,
this.view,
{
targetTime:
properties.what === 'background'
? properties.time
: this._timeline.getWindow().end,
...(properties.group && {
cameraIDs: new Set([String(properties.group)]),
}),
},
this.cardWideConfig,
new Set([String(properties.group)]),
);
if (query) {
view = await executeMediaQueryForView(
this,
this.hass,
this.cameraManager,
this.view,
query,
{
targetView: 'recording',
targetTime:
properties.what === 'background'
? properties.time
: this._timeline.getWindow().end,
},
);
}
} else if (this.timelineConfig?.show_recordings && properties.what === 'axis') {
view = await createViewForRecordings(
this,
this.hass,
const query = createQueriesForRecordingsView(
this.cameraManager,
this.view,
this.cardWideConfig,
timelineCameraIDs,
{
cameraIDs: timelineCameraIDs,
start: startOfHour(properties.time),
end: endOfHour(properties.time),
targetTime: properties.time,
},
);
if (query) {
view = await executeMediaQueryForView(
this,
this.hass,
this.cameraManager,
this.view,
query,
{
targetView: 'recording',
targetTime: properties.time,
},
);
}
} else if (properties.item && properties.what === 'item') {
const newResults = this.view.queryResults
?.clone()
@@ -618,7 +633,7 @@ export class FrigateCardTimelineCore extends LitElement {
protected _createEventMediaQuerys(options?: {
window?: TimelineWindow;
}): EventMediaQueries | null {
if (!this._timeline || !this._timelineSource) {
if (!this._timeline || !this._timelineSource || !this.cardWideConfig) {
return null;
}
@@ -644,15 +659,14 @@ export class FrigateCardTimelineCore extends LitElement {
if (!this.hass || !this.cameraManager || !this.view || !query) {
return null;
}
const view = await createViewForEvents(
const view = await executeMediaQueryForView(
this,
this.hass,
this.cameraManager,
this.view,
query,
{
query: query,
targetView: options?.targetView,
mediaType: this.timelineConfig?.media,
},
);
if (!view) {
@@ -1002,11 +1016,11 @@ export class FrigateCardTimelineCore extends LitElement {
* @param changedProps The changed properties
*/
protected willUpdate(changedProps: PropertyValues): void {
if (changedProps.has('thumbnailSize')) {
if (this.thumbnailSize !== undefined) {
if (changedProps.has('thumbnailConfig')) {
if (this.thumbnailConfig) {
this.style.setProperty(
'--frigate-card-thumbnail-size',
`${this.thumbnailSize}px`,
`${this.thumbnailConfig.size}px`,
);
} else {
this.style.removeProperty('--frigate-card-thumbnail-size');
@@ -1028,7 +1042,11 @@ export class FrigateCardTimelineCore extends LitElement {
changedProps.has('cameraIDs')
) {
const cameraIDs = this._getTimelineCameraIDs();
if (cameraIDs && this.cameraManager && this.timelineConfig) {
if (
cameraIDs &&
this.cameraManager &&
this.timelineConfig
) {
this._timelineSource = new TimelineDataSource(
this.cameraManager,
cameraIDs,
+7 -4
View File
@@ -1,7 +1,7 @@
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import timelineStyle from '../scss/timeline.scss';
import { ExtendedHomeAssistant, TimelineConfig } from '../types';
import { CardWideConfig, ExtendedHomeAssistant, TimelineConfig } from '../types';
import { CameraManager } from '../camera-manager/manager';
import { View } from '../view/view';
import './surround.js';
@@ -10,7 +10,7 @@ import './timeline-core.js';
// This file is kept separate from timeline-core.ts to avoid a circular dependency:
// FrigateCardTimeline ->
// FrigateCardSurround ->
// FrigateCardTimelineCore
// FrigateCardTimelineCore
@customElement('frigate-card-timeline')
export class FrigateCardTimeline extends LitElement {
@@ -26,6 +26,9 @@ export class FrigateCardTimeline extends LitElement {
@property({ attribute: false })
public cameraManager?: CameraManager;
@property({ attribute: false })
public cardWideConfig?: CardWideConfig;
/**
* Master render method.
* @returns A rendered template.
@@ -45,9 +48,9 @@ export class FrigateCardTimeline extends LitElement {
.hass=${this.hass}
.view=${this.view}
.timelineConfig=${this.timelineConfig}
.thumbnailDetails=${this.timelineConfig.controls.thumbnails.show_details}
.thumbnailSize=${this.timelineConfig.controls.thumbnails.size}
.thumbnailConfig=${this.timelineConfig.controls.thumbnails}
.cameraManager=${this.cameraManager}
.cardWideConfig=${this.cardWideConfig}
>
</frigate-card-timeline-core>
</frigate-card-surround>`;
+5 -1
View File
@@ -95,7 +95,8 @@ export class FrigateCardViewer extends LitElement {
!this.hass ||
!this.view ||
!this.viewerConfig ||
!this.cameraManager
!this.cameraManager ||
!this.cardWideConfig
) {
return;
}
@@ -115,6 +116,7 @@ export class FrigateCardViewer extends LitElement {
this,
this.hass,
this.cameraManager,
this.cardWideConfig,
this.view,
{
targetView: 'recording',
@@ -125,6 +127,7 @@ export class FrigateCardViewer extends LitElement {
this,
this.hass,
this.cameraManager,
this.cardWideConfig,
this.view,
{
targetView: 'media',
@@ -141,6 +144,7 @@ export class FrigateCardViewer extends LitElement {
.thumbnailConfig=${this.viewerConfig.controls.thumbnails}
.timelineConfig=${this.viewerConfig.controls.timeline}
.cameraManager=${this.cameraManager}
.cardWideConfig=${this.cardWideConfig}
>
<frigate-card-viewer-carousel
.hass=${this.hass}