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 { import type {
BrowseMediaQueryParameters, BrowseMediaQueryParameters,
BrowseMediaSource, BrowseMediaSource,
CameraConfig, CameraConfig,
ExtendedHomeAssistant, ExtendedHomeAssistant,
} from './types.js'; } 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 { 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); dayjs.extend(dayjs_custom_parse_format);
@@ -154,4 +159,77 @@ export class BrowseMediaUtil {
cameraConfig, 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 { HomeAssistant } from 'custom-card-helpers';
import { customElement, property, state } from 'lit/decorators.js'; import { customElement, property, state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import { until } from 'lit/directives/until.js';
import type { import type { BrowseMediaQueryParameters, ExtendedHomeAssistant } from '../types.js';
BrowseMediaSource,
BrowseMediaQueryParameters,
ExtendedHomeAssistant,
} from '../types.js';
import { BrowseMediaUtil } from '../browse-media-util.js'; import { BrowseMediaUtil } from '../browse-media-util.js';
import { View } from '../view.js'; import { View } from '../view.js';
import { dispatchErrorMessageEvent, dispatchMessageEvent } from '../common.js';
import { localize } from '../localize/localize.js';
import { renderProgressIndicator } from './message.js'; import { renderProgressIndicator } from './message.js';
import galleryStyle from '../scss/gallery.scss'; import galleryStyle from '../scss/gallery.scss';
import { actionHandler } from '../action-handler-directive.js';
const MAX_THUMBNAIL_WIDTH = 175; const MAX_THUMBNAIL_WIDTH = 175;
const DEFAULT_COLUMNS = 5; const DEFAULT_COLUMNS = 5;
@@ -28,7 +20,7 @@ export class FrigateCardGallery extends LitElement {
protected hass?: HomeAssistant & ExtendedHomeAssistant; protected hass?: HomeAssistant & ExtendedHomeAssistant;
@property({ attribute: false }) @property({ attribute: false })
protected view?: View; protected view?: Readonly<View>;
@property({ attribute: false }) @property({ attribute: false })
protected browseMediaQueryParameters?: BrowseMediaQueryParameters; protected browseMediaQueryParameters?: BrowseMediaQueryParameters;
@@ -38,58 +30,24 @@ export class FrigateCardGallery extends LitElement {
* @returns A rendered template. * @returns A rendered template.
*/ */
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
return html`${until(this._render(), renderProgressIndicator())}`; if (!this.hass || !this.view || !this.browseMediaQueryParameters) {
} return;
/**
* 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``;
} }
let parent: BrowseMediaSource | null; if (!this.view.target) {
try { BrowseMediaUtil.fetchLatestMediaAndDispatchViewChange(
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(
this, this,
this.view.is('clips') this.hass,
? localize('common.no_clips') this.view,
: localize('common.no_snapshots'), this.browseMediaQueryParameters,
this.view.is('clips') ? 'mdi:filmstrip-off' : 'mdi:camera-off',
); );
return renderProgressIndicator();
} }
this.view.target = parent; return html`
<frigate-card-gallery-core .hass=${this.hass} .view=${this.view}>
return html` <frigate-card-gallery-core .hass=${this.hass} .view=${this.view}> </frigate-card-gallery-core>
</frigate-card-gallery-core>`; `;
} }
/** /**
@@ -106,7 +64,7 @@ export class FrigateCardGalleryCore extends LitElement {
protected hass?: HomeAssistant & ExtendedHomeAssistant; protected hass?: HomeAssistant & ExtendedHomeAssistant;
@property({ attribute: false }) @property({ attribute: false })
protected view?: View; protected view?: Readonly<View>;
protected _resizeObserver: ResizeObserver; protected _resizeObserver: ResizeObserver;
@@ -137,6 +95,7 @@ export class FrigateCardGalleryCore extends LitElement {
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
if ( if (
!this.hass ||
!this.view || !this.view ||
!this.view.target || !this.view.target ||
!this.view.target.children || !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-aspect-container">
<div class="mdc-image-list__image"> <div class="mdc-image-list__image">
<ha-card <ha-card
.actionHandler=${actionHandler({ @click=${() => {
hasHold: false,
hasDoubleClick: false,
})}
@action=${() => {
if (this.view && this.view.previous) { if (this.view && this.view.previous) {
this.view.previous.dispatchChangeEvent(this); this.view.previous.dispatchChangeEvent(this);
} }
@@ -181,18 +136,14 @@ export class FrigateCardGalleryCore extends LitElement {
${child.can_expand ${child.can_expand
? html`<div class="mdc-image-list__image"> ? html`<div class="mdc-image-list__image">
<ha-card <ha-card
.actionHandler=${actionHandler({ @click=${() => {
hasHold: false, if (this.hass && this.view) {
hasDoubleClick: false, BrowseMediaUtil.fetchChildMediaAndDispatchViewChange(
})} this,
@action=${() => { this.hass,
if (this.view) { this.view,
new View({ child,
view: this.view.view, );
camera: this.view.camera,
target: child,
previous: this.view ?? undefined,
}).dispatchChangeEvent(this);
} }
}} }}
outlined="" outlined=""
@@ -206,21 +157,15 @@ export class FrigateCardGalleryCore extends LitElement {
title="${child.title}" title="${child.title}"
class="mdc-image-list__image" class="mdc-image-list__image"
src="${child.thumbnail}" src="${child.thumbnail}"
.actionHandler=${actionHandler({ @click=${() => {
hasHold: false,
hasDoubleClick: false,
})}
@action=${() => {
if (this.view) { if (this.view) {
new View({ this.view
view: this.view.is('clips') .evolve({
? 'clip' view: this.view.is('clips') ? 'clip' : 'snapshot',
: 'snapshot', childIndex: index,
camera: this.view.camera, previous: this.view,
target: this.view.target ?? undefined, })
childIndex: index, .dispatchChangeEvent(this);
previous: this.view ?? undefined,
}).dispatchChangeEvent(this);
} }
}} }}
/>` />`
+6 -41
View File
@@ -33,9 +33,7 @@ import { View } from '../view.js';
import { import {
createMediaShowInfo, createMediaShowInfo,
dispatchErrorMessageEvent, dispatchErrorMessageEvent,
dispatchMessageEvent,
} from '../common.js'; } from '../common.js';
import { localize } from '../localize/localize.js';
import { renderProgressIndicator } from '../components/message.js'; import { renderProgressIndicator } from '../components/message.js';
import './next-prev-control.js'; import './next-prev-control.js';
@@ -60,44 +58,6 @@ export class FrigateCardViewer extends LitElement {
@property({ attribute: false }) @property({ attribute: false })
protected resolvedMediaCache?: ResolvedMediaCache; 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. * Master render method.
* @returns A rendered template. * @returns A rendered template.
@@ -108,7 +68,12 @@ export class FrigateCardViewer extends LitElement {
} }
if (!this.view.target) { if (!this.view.target) {
this._fetchLatestMedia(); BrowseMediaUtil.fetchLatestMediaAndDispatchViewChange(
this,
this.hass,
this.view,
this.browseMediaQueryParameters,
);
return renderProgressIndicator(); return renderProgressIndicator();
} }