Refactor media fetching logic out of viewer & gallery.

This commit is contained in:
Dermot Duffy
2022-01-14 21:31:16 -08:00
parent 22f25fcde7
commit f976f3d210
3 changed files with 123 additions and 135 deletions
+84 -6
View File
@@ -1,16 +1,21 @@
import { HomeAssistant } from 'custom-card-helpers';
import dayjs from 'dayjs';
import dayjs_custom_parse_format from 'dayjs/plugin/customParseFormat.js';
import type {
BrowseMediaQueryParameters,
BrowseMediaSource,
CameraConfig,
ExtendedHomeAssistant,
} from './types.js';
import { HomeAssistant } from 'custom-card-helpers';
import { homeAssistantWSRequest } from './common.js';
import { browseMediaSourceSchema } from './types.js';
import dayjs from 'dayjs';
import dayjs_custom_parse_format from 'dayjs/plugin/customParseFormat.js';
import { View } from './view.js';
import { browseMediaSourceSchema } from './types.js';
import {
dispatchErrorMessageEvent,
dispatchMessageEvent,
homeAssistantWSRequest,
} from './common.js';
import { localize } from './localize/localize.js';
dayjs.extend(dayjs_custom_parse_format);
@@ -154,4 +159,77 @@ export class BrowseMediaUtil {
cameraConfig,
);
}
/**
* Fetch the latest media and dispatch a change view event to reflect the
* results. If no media is found a suitable message event will be triggered
* instead.
* @param node The HTMLElement to dispatch events from.
* @param hass The Home Assistant object.
* @param view The current view to evolve.
* @param browseMediaQueryParameters The media parameters to query with.
* @returns
*/
static async fetchLatestMediaAndDispatchViewChange(
node: HTMLElement,
hass: HomeAssistant & ExtendedHomeAssistant,
view: Readonly<View>,
browseMediaQueryParameters: BrowseMediaQueryParameters,
): Promise<void> {
let parent: BrowseMediaSource | null;
try {
parent = await BrowseMediaUtil.browseMediaQuery(hass, browseMediaQueryParameters);
} catch (e) {
return dispatchErrorMessageEvent(node, (e as Error).message);
}
const childIndex = BrowseMediaUtil.getFirstTrueMediaChildIndex(parent);
if (!parent || !parent.children || childIndex == null) {
return dispatchMessageEvent(
node,
browseMediaQueryParameters.mediaType == 'clips'
? localize('common.no_clip')
: localize('common.no_snapshot'),
browseMediaQueryParameters.mediaType == 'clips'
? 'mdi:filmstrip-off'
: 'mdi:camera-off',
);
}
view
.evolve({
target: parent,
childIndex: childIndex,
})
.dispatchChangeEvent(node);
}
/**
* Fetch the media of a child BrowseMediaSource object and dispatch a change
* view event to reflect the results.
* @param node The HTMLElement to dispatch events from.
* @param hass The Home Assistant object.
* @param view The current view to evolve.
* @param child The BrowseMediaSource child to query for.
* @returns
*/
static async fetchChildMediaAndDispatchViewChange(
node: HTMLElement,
hass: HomeAssistant & ExtendedHomeAssistant,
view: Readonly<View>,
child: Readonly<BrowseMediaSource>,
): Promise<void> {
let parent: BrowseMediaSource;
try {
parent = await BrowseMediaUtil.browseMedia(hass, child.media_content_id);
} catch (e) {
return dispatchErrorMessageEvent(node, (e as Error).message);
}
view
.evolve({
target: parent,
previous: view,
})
.dispatchChangeEvent(node);
}
}
+33 -88
View File
@@ -3,21 +3,13 @@ import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit
import { HomeAssistant } from 'custom-card-helpers';
import { customElement, property, state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
import { until } from 'lit/directives/until.js';
import type {
BrowseMediaSource,
BrowseMediaQueryParameters,
ExtendedHomeAssistant,
} from '../types.js';
import type { BrowseMediaQueryParameters, ExtendedHomeAssistant } from '../types.js';
import { BrowseMediaUtil } from '../browse-media-util.js';
import { View } from '../view.js';
import { dispatchErrorMessageEvent, dispatchMessageEvent } from '../common.js';
import { localize } from '../localize/localize.js';
import { renderProgressIndicator } from './message.js';
import galleryStyle from '../scss/gallery.scss';
import { actionHandler } from '../action-handler-directive.js';
const MAX_THUMBNAIL_WIDTH = 175;
const DEFAULT_COLUMNS = 5;
@@ -28,7 +20,7 @@ export class FrigateCardGallery extends LitElement {
protected hass?: HomeAssistant & ExtendedHomeAssistant;
@property({ attribute: false })
protected view?: View;
protected view?: Readonly<View>;
@property({ attribute: false })
protected browseMediaQueryParameters?: BrowseMediaQueryParameters;
@@ -38,58 +30,24 @@ export class FrigateCardGallery extends LitElement {
* @returns A rendered template.
*/
protected render(): TemplateResult | void {
return html`${until(this._render(), renderProgressIndicator())}`;
}
/**
* Asyncronously render the element.
* @returns A rendered template.
*/
protected async _render(): Promise<TemplateResult | void> {
if (
!this.hass ||
!this.view ||
!this.browseMediaQueryParameters ||
!(this.view.is('clips') || this.view.is('snapshots'))
) {
return html``;
if (!this.hass || !this.view || !this.browseMediaQueryParameters) {
return;
}
let parent: BrowseMediaSource | null;
try {
if (this.view.target) {
parent = await BrowseMediaUtil.browseMedia(
this.hass,
this.view.target.media_content_id,
);
} else {
parent = await BrowseMediaUtil.browseMediaQuery(
this.hass,
this.browseMediaQueryParameters,
);
}
} catch (e: any) {
return dispatchErrorMessageEvent(this, e.message);
}
if (
!parent ||
!parent.children ||
BrowseMediaUtil.getFirstTrueMediaChildIndex(parent) == null
) {
return dispatchMessageEvent(
if (!this.view.target) {
BrowseMediaUtil.fetchLatestMediaAndDispatchViewChange(
this,
this.view.is('clips')
? localize('common.no_clips')
: localize('common.no_snapshots'),
this.view.is('clips') ? 'mdi:filmstrip-off' : 'mdi:camera-off',
this.hass,
this.view,
this.browseMediaQueryParameters,
);
return renderProgressIndicator();
}
this.view.target = parent;
return html` <frigate-card-gallery-core .hass=${this.hass} .view=${this.view}>
</frigate-card-gallery-core>`;
return html`
<frigate-card-gallery-core .hass=${this.hass} .view=${this.view}>
</frigate-card-gallery-core>
`;
}
/**
@@ -106,7 +64,7 @@ export class FrigateCardGalleryCore extends LitElement {
protected hass?: HomeAssistant & ExtendedHomeAssistant;
@property({ attribute: false })
protected view?: View;
protected view?: Readonly<View>;
protected _resizeObserver: ResizeObserver;
@@ -137,6 +95,7 @@ export class FrigateCardGalleryCore extends LitElement {
protected render(): TemplateResult | void {
if (
!this.hass ||
!this.view ||
!this.view.target ||
!this.view.target.children ||
@@ -156,11 +115,7 @@ export class FrigateCardGalleryCore extends LitElement {
<div class="mdc-image-list__image-aspect-container">
<div class="mdc-image-list__image">
<ha-card
.actionHandler=${actionHandler({
hasHold: false,
hasDoubleClick: false,
})}
@action=${() => {
@click=${() => {
if (this.view && this.view.previous) {
this.view.previous.dispatchChangeEvent(this);
}
@@ -181,18 +136,14 @@ export class FrigateCardGalleryCore extends LitElement {
${child.can_expand
? html`<div class="mdc-image-list__image">
<ha-card
.actionHandler=${actionHandler({
hasHold: false,
hasDoubleClick: false,
})}
@action=${() => {
if (this.view) {
new View({
view: this.view.view,
camera: this.view.camera,
target: child,
previous: this.view ?? undefined,
}).dispatchChangeEvent(this);
@click=${() => {
if (this.hass && this.view) {
BrowseMediaUtil.fetchChildMediaAndDispatchViewChange(
this,
this.hass,
this.view,
child,
);
}
}}
outlined=""
@@ -206,21 +157,15 @@ export class FrigateCardGalleryCore extends LitElement {
title="${child.title}"
class="mdc-image-list__image"
src="${child.thumbnail}"
.actionHandler=${actionHandler({
hasHold: false,
hasDoubleClick: false,
})}
@action=${() => {
@click=${() => {
if (this.view) {
new View({
view: this.view.is('clips')
? 'clip'
: 'snapshot',
camera: this.view.camera,
target: this.view.target ?? undefined,
childIndex: index,
previous: this.view ?? undefined,
}).dispatchChangeEvent(this);
this.view
.evolve({
view: this.view.is('clips') ? 'clip' : 'snapshot',
childIndex: index,
previous: this.view,
})
.dispatchChangeEvent(this);
}
}}
/>`
+6 -41
View File
@@ -33,9 +33,7 @@ import { View } from '../view.js';
import {
createMediaShowInfo,
dispatchErrorMessageEvent,
dispatchMessageEvent,
} from '../common.js';
import { localize } from '../localize/localize.js';
import { renderProgressIndicator } from '../components/message.js';
import './next-prev-control.js';
@@ -60,44 +58,6 @@ export class FrigateCardViewer extends LitElement {
@property({ attribute: false })
protected resolvedMediaCache?: ResolvedMediaCache;
/**
* Asyncronously render the element.
* @returns A rendered template.
*/
protected async _fetchLatestMedia(): Promise<void> {
if (!this.view || !this.hass || !this.browseMediaQueryParameters) {
return;
}
let parent: BrowseMediaSource | null;
try {
parent = await BrowseMediaUtil.browseMediaQuery(
this.hass,
this.browseMediaQueryParameters,
);
} catch (e) {
return dispatchErrorMessageEvent(this, (e as Error).message);
}
const childIndex = BrowseMediaUtil.getFirstTrueMediaChildIndex(parent);
if (!parent || !parent.children || childIndex == null) {
return dispatchMessageEvent(
this,
this.browseMediaQueryParameters.mediaType == 'clips'
? localize('common.no_clip')
: localize('common.no_snapshot'),
this.browseMediaQueryParameters.mediaType == 'clips'
? 'mdi:filmstrip-off'
: 'mdi:camera-off',
);
}
this.view
.evolve({
target: parent,
childIndex: childIndex,
})
.dispatchChangeEvent(this);
}
/**
* Master render method.
* @returns A rendered template.
@@ -108,7 +68,12 @@ export class FrigateCardViewer extends LitElement {
}
if (!this.view.target) {
this._fetchLatestMedia();
BrowseMediaUtil.fetchLatestMediaAndDispatchViewChange(
this,
this.hass,
this.view,
this.browseMediaQueryParameters,
);
return renderProgressIndicator();
}