From fde2cc5d606e1d339e46a5e259076a8ccf3f3429 Mon Sep 17 00:00:00 2001 From: Justin Wong <46082645+uvjustin@users.noreply.github.com> Date: Fri, 5 Aug 2022 01:12:11 +0800 Subject: [PATCH 1/4] Embed thumbnails to take advantage of cache --- src/components/thumbnail.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/thumbnail.ts b/src/components/thumbnail.ts index cafdd136..b3763668 100644 --- a/src/components/thumbnail.ts +++ b/src/components/thumbnail.ts @@ -18,7 +18,6 @@ import { stopEventFromActivatingCardWideActions } from '../utils/action.js'; import { errorToConsole, prettifyTitle } from '../utils/basic.js'; import { retainEvent } from '../utils/frigate.js'; import { getEventDurationString } from '../utils/ha/browse-media.js'; -import { homeAssistantSignPath } from '../utils/ha/index.js'; import { View } from '../view.js'; import { dispatchFrigateCardErrorEvent, renderProgressIndicator } from './message.js'; @@ -33,9 +32,9 @@ export class FrigateCardThumbnailFeatureEvent extends LitElement { @property({ attribute: false }) public hass?: ExtendedHomeAssistant; - protected _signThumbnailTask = new Task( + protected _embedThumbnailTask = new Task( this, - this._signThumbnail.bind(this), + this._embedThumbnail.bind(this), // Do not re-run the task if hass changes, unless it was previously undefined. (): [boolean, string | undefined] => [!!this.hass, this.thumbnail], ); @@ -45,7 +44,7 @@ export class FrigateCardThumbnailFeatureEvent extends LitElement { * @param param0 A list of lit-task dependencies. * @returns A signed URL or null. */ - protected async _signThumbnail([haveHASS, thumbnail]: [ + protected async _embedThumbnail([haveHASS, thumbnail]: [ boolean, string | undefined, ]): Promise { @@ -55,21 +54,26 @@ export class FrigateCardThumbnailFeatureEvent extends LitElement { if (this.thumbnail?.startsWith('data:')) { return this.thumbnail; } - return await homeAssistantSignPath(this.hass, thumbnail); + return await this.hass + .fetchWithAuth(thumbnail) + .then((response) => response.blob()) + .then((blob) => URL.createObjectURL(blob)); } protected render(): TemplateResult | void { return html` ${this.thumbnail - ? html` ${this._signThumbnailTask.render({ + ? html` ${this._embedThumbnailTask.render({ initial: () => renderProgressIndicator(), pending: () => renderProgressIndicator(), error: (e: unknown) => { errorToConsole(e as Error); dispatchFrigateCardErrorEvent(this, e as Error); }, - complete: (signedThumbnail: string | null) => { - return signedThumbnail ? html`` : html``; + complete: (embeddedThumbnail: string | null) => { + return embeddedThumbnail + ? html`` + : html``; }, })}` : html` Date: Fri, 5 Aug 2022 02:22:14 +0800 Subject: [PATCH 2/4] Use base64 instead of blob URLs --- src/components/thumbnail.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/components/thumbnail.ts b/src/components/thumbnail.ts index b3763668..6c2b0172 100644 --- a/src/components/thumbnail.ts +++ b/src/components/thumbnail.ts @@ -54,10 +54,20 @@ export class FrigateCardThumbnailFeatureEvent extends LitElement { if (this.thumbnail?.startsWith('data:')) { return this.thumbnail; } - return await this.hass - .fetchWithAuth(thumbnail) - .then((response) => response.blob()) - .then((blob) => URL.createObjectURL(blob)); + return new Promise((resolve, reject) => { + //eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.hass!.fetchWithAuth(thumbnail) + .then((response) => response.blob()) + .then((blob) => { + const reader = new FileReader(); + reader.onload = () => { + const result = reader.result; + resolve(typeof result === 'string' ? result : null); + }; + reader.onerror = (e) => reject(e); + reader.readAsDataURL(blob); + }); + }); } protected render(): TemplateResult | void { From 70a6477d7e213451ecd64c99a30c5689dfb417b9 Mon Sep 17 00:00:00 2001 From: Justin Wong <46082645+uvjustin@users.noreply.github.com> Date: Fri, 5 Aug 2022 12:09:46 +0800 Subject: [PATCH 3/4] Add comment --- src/components/thumbnail.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/thumbnail.ts b/src/components/thumbnail.ts index 6c2b0172..913a8005 100644 --- a/src/components/thumbnail.ts +++ b/src/components/thumbnail.ts @@ -57,6 +57,10 @@ export class FrigateCardThumbnailFeatureEvent extends LitElement { return new Promise((resolve, reject) => { //eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.hass!.fetchWithAuth(thumbnail) + // Since we are fetching with an authorization header, we cannot just put the + // URL directly into the document; we need to embed the image. We could do this + // using blob URLs, but then we would need to keep track of them in order to + // release them properly. Instead, we embed the thumbnail using base64. .then((response) => response.blob()) .then((blob) => { const reader = new FileReader(); From 507628873e0c96d19cb4667d5af2fa817dd4f82d Mon Sep 17 00:00:00 2001 From: Justin Wong <46082645+uvjustin@users.noreply.github.com> Date: Fri, 5 Aug 2022 12:14:00 +0800 Subject: [PATCH 4/4] Reject if !this.hass --- src/components/thumbnail.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/thumbnail.ts b/src/components/thumbnail.ts index 913a8005..ddd1a2e6 100644 --- a/src/components/thumbnail.ts +++ b/src/components/thumbnail.ts @@ -55,12 +55,16 @@ export class FrigateCardThumbnailFeatureEvent extends LitElement { return this.thumbnail; } return new Promise((resolve, reject) => { - //eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.hass!.fetchWithAuth(thumbnail) - // Since we are fetching with an authorization header, we cannot just put the - // URL directly into the document; we need to embed the image. We could do this - // using blob URLs, but then we would need to keep track of them in order to - // release them properly. Instead, we embed the thumbnail using base64. + if (!this.hass) { + reject(); + return; + } + this.hass + .fetchWithAuth(thumbnail) + // Since we are fetching with an authorization header, we cannot just put the + // URL directly into the document; we need to embed the image. We could do this + // using blob URLs, but then we would need to keep track of them in order to + // release them properly. Instead, we embed the thumbnail using base64. .then((response) => response.blob()) .then((blob) => { const reader = new FileReader();