Show still images during live camera load.

This commit is contained in:
Dermot Duffy
2022-08-07 16:04:25 -07:00
parent d7bc6d009a
commit 88b9c7427a
11 changed files with 120 additions and 26 deletions
+2 -1
View File
@@ -1010,6 +1010,7 @@ export class FrigateCard extends LitElement {
view: this._view?.clone().mergeInContext(ev.detail),
});
}
/**
* Called before each update.
*/
@@ -1795,7 +1796,7 @@ export class FrigateCard extends LitElement {
@frigate-card:message=${this._messageHandler.bind(this)}
@frigate-card:view:change=${this._changeViewHandler.bind(this)}
@frigate-card:view:change-context=${this._addViewContextHandler.bind(this)}
@frigate-card:media-show=${this._mediaShowHandler}
@frigate-card:media-show=${this._mediaShowHandler.bind(this)}
@frigate-card:render=${() => this.requestUpdate()}
>
${renderMenuAbove ? this._renderMenu() : ''}
+79 -19
View File
@@ -17,17 +17,14 @@ import { guard } from 'lit/directives/guard.js';
import { keyed } from 'lit/directives/keyed.js';
import { until } from 'lit/directives/until.js';
import { ConditionState, getOverriddenConfig } from '../card-condition.js';
import {
dispatchFrigateCardErrorEvent,
dispatchMessageEvent,
renderProgressIndicator,
} from '../components/message.js';
import { dispatchMessageEvent, renderProgressIndicator } from '../components/message.js';
import { localize } from '../localize/localize.js';
import liveFrigateStyle from '../scss/live-frigate.scss';
import liveJSMPEGStyle from '../scss/live-jsmpeg.scss';
import liveWebRTCStyle from '../scss/live-webrtc.scss';
import liveStyle from '../scss/live.scss';
import liveCarouselStyle from '../scss/live-carousel.scss';
import liveProviderStyle from '../scss/live-provider.scss';
import {
CameraConfig,
ExtendedHomeAssistant,
@@ -44,10 +41,7 @@ import {
WebRTCCardConfig,
} from '../types.js';
import { stopEventFromActivatingCardWideActions } from '../utils/action.js';
import {
contentsChanged,
errorToConsole,
} from '../utils/basic.js';
import { contentsChanged, errorToConsole } from '../utils/basic.js';
import { getCameraIcon, getCameraTitle } from '../utils/camera.js';
import { homeAssistantSignPath } from '../utils/ha';
import { getFullDependentBrowseMediaQueryParameters } from '../utils/ha/browse-media.js';
@@ -69,6 +63,8 @@ import './surround-thumbnails';
import '../patches/ha-camera-stream';
import { EmblaCarouselPlugins } from './carousel.js';
import { renderTask } from '../utils/task.js';
import { classMap } from 'lit/directives/class-map.js';
import './image';
// Number of seconds a signed URL is valid for.
const URL_SIGN_EXPIRY_SECONDS = 24 * 60 * 60;
@@ -591,7 +587,7 @@ export class FrigateCardLiveCarousel extends LitElement {
@frigate-card:media-carousel:select=${this._setViewHandler.bind(this)}
@frigate-card:carousel:settle=${() => {
// Fetch the thumbnails after the carousel has settled.
dispatchViewContextChangeEvent(this, { thumbnails: { fetch: true }});
dispatchViewContextChangeEvent(this, { thumbnails: { fetch: true } });
}}
>
<frigate-card-next-previous-control
@@ -657,6 +653,9 @@ export class FrigateCardLiveProvider extends LitElement {
@property({ attribute: false })
public label = '';
@state()
protected _isVideoMediaLoaded = false;
protected _providerRef: Ref<Element & FrigateCardMediaPlayer> = createRef();
/**
@@ -694,7 +693,11 @@ export class FrigateCardLiveProvider extends LitElement {
this._providerRef.value?.seek(seconds);
}
protected _getResolvedProvider(): LiveProvider {
/**
* Get the fully resolved live provider.
* @returns A live provider (that is not 'auto').
*/
protected _getResolvedProvider(): Omit<LiveProvider, 'auto'> {
if (this.cameraConfig?.live_provider === 'auto') {
if (
this.cameraConfig?.webrtc_card?.entity ||
@@ -713,6 +716,35 @@ export class FrigateCardLiveProvider extends LitElement {
);
}
/**
* Determine if a camera image should be shown in lieu of the real stream
* whilst loading.
* @returns`true` if an image should be shown.
*/
protected _shouldShowImageDuringLoading(): boolean {
return (
!!this.cameraConfig?.camera_entity &&
!!this.hass &&
!!this.liveConfig?.show_image_during_load
);
}
/**
* Record that video media is being shown.
*/
protected _videoMediaShowHandler(): void {
this._isVideoMediaLoaded = true;
}
/**
* Called before each update.
*/
protected willUpdate(): void {
if (this.disabled) {
this._isVideoMediaLoaded = false;
}
}
/**
* Master render method.
* @returns A rendered template.
@@ -727,32 +759,60 @@ export class FrigateCardLiveProvider extends LitElement {
this.ariaLabel = this.label;
const provider = this._getResolvedProvider();
const showImage = !this._isVideoMediaLoaded && this._shouldShowImageDuringLoading();
const providerClasses = {
hidden: showImage,
};
return html`
${provider == 'ha'
? html` <frigate-card-live-ha
${ref(this._providerRef)}
${showImage
? html`<frigate-card-image
.imageConfig=${{
mode: 'camera' as const,
refresh_seconds: 1,
}}
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
>
</frigate-card-image>`
: html``}
${provider === 'ha'
? html` <frigate-card-live-ha
${ref(this._providerRef)}
class=${classMap(providerClasses)}
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
@frigate-card:media-show=${this._videoMediaShowHandler.bind(this)}
>
</frigate-card-live-ha>`
: provider == 'webrtc-card'
: provider === 'webrtc-card'
? html`<frigate-card-live-webrtc-card
${ref(this._providerRef)}
class=${classMap(providerClasses)}
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
.webRTCConfig=${this.liveConfig.webrtc_card}
@frigate-card:media-show=${this._videoMediaShowHandler.bind(this)}
>
</frigate-card-live-webrtc-card>`
: html` <frigate-card-live-jsmpeg
${ref(this._providerRef)}
class=${classMap(providerClasses)}
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
.jsmpegConfig=${this.liveConfig.jsmpeg}
@frigate-card:media-show=${this._videoMediaShowHandler.bind(this)}
>
</frigate-card-live-jsmpeg>`}
`;
}
/**
* Get styles.
*/
static get styles(): CSSResultGroup {
return unsafeCSS(liveProviderStyle);
}
}
@customElement('frigate-card-live-ha')
@@ -1006,11 +1066,11 @@ export class FrigateCardLiveWebRTCCard extends LitElement {
this.updateComplete.then(() => {
const video = this._getPlayer();
if (video) {
const onloadedmetadata = video.onloadedmetadata;
const onloadeddata = video.onloadeddata;
video.onloadedmetadata = (e) => {
if (onloadedmetadata) {
onloadedmetadata.call(video, e);
video.onloadeddata = (e) => {
if (onloadeddata) {
onloadeddata.call(video, e);
}
dispatchMediaShowEvent(this, video);
};
+1
View File
@@ -119,6 +119,7 @@ export const CONF_LIVE_LAZY_LOAD = `${CONF_LIVE}.lazy_load` as const;
export const CONF_LIVE_LAZY_UNLOAD = `${CONF_LIVE}.lazy_unload` as const;
export const CONF_LIVE_PRELOAD = `${CONF_LIVE}.preload` as const;
export const CONF_LIVE_TRANSITION_EFFECT = `${CONF_LIVE}.transition_effect` as const;
export const CONF_LIVE_SHOW_IMAGE_DURING_LOAD = `${CONF_LIVE}.show_image_during_load` as const;
export const CONF_LIVE_WEBRTC_CARD = `${CONF_LIVE}.webrtc_card` as const;
export const CONF_IMAGE = 'image' as const;
+11 -6
View File
@@ -9,7 +9,7 @@ import {
getConfigValue,
isConfigUpgradeable,
setConfigValue,
upgradeConfig
upgradeConfig,
} from './config-mgmt.js';
import {
CONF_CAMERAS,
@@ -57,6 +57,7 @@ import {
CONF_LIVE_LAZY_LOAD,
CONF_LIVE_LAZY_UNLOAD,
CONF_LIVE_PRELOAD,
CONF_LIVE_SHOW_IMAGE_DURING_LOAD,
CONF_LIVE_TRANSITION_EFFECT,
CONF_MEDIA_VIEWER_AUTO_MUTE,
CONF_MEDIA_VIEWER_AUTO_PAUSE,
@@ -99,7 +100,7 @@ import {
CONF_VIEW_TIMEOUT_SECONDS,
CONF_VIEW_UPDATE_CYCLE_CAMERA,
CONF_VIEW_UPDATE_FORCE,
CONF_VIEW_UPDATE_SECONDS
CONF_VIEW_UPDATE_SECONDS,
} from './const.js';
import { localize } from './localize/localize.js';
import frigate_card_editor_style from './scss/editor.scss';
@@ -110,7 +111,7 @@ import {
RawFrigateCardConfig,
RawFrigateCardConfigArray,
THUMBNAIL_WIDTH_MAX,
THUMBNAIL_WIDTH_MIN
THUMBNAIL_WIDTH_MIN,
} from './types.js';
import { arrayMove } from './utils/basic.js';
import { getCameraID, getCameraTitle } from './utils/camera.js';
@@ -1203,6 +1204,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
CONF_LIVE_TRANSITION_EFFECT,
this._transitionEffects,
)}
${this._renderSwitch(
CONF_LIVE_SHOW_IMAGE_DURING_LOAD,
defaults.live.show_image_during_load,
)}
${this._putInSubmenu(
MENU_LIVE_CONTROLS,
true,
@@ -1511,7 +1516,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
}
declare global {
interface HTMLElementTagNameMap {
"frigate-card-editor": FrigateCardEditor
}
interface HTMLElementTagNameMap {
'frigate-card-editor': FrigateCardEditor;
}
}
+1
View File
@@ -125,6 +125,7 @@
"lazy_load": "Live cameras are lazily loaded",
"lazy_unload": "Live cameras are lazily unloaded",
"preload": "Preload live view in the background",
"show_image_during_load": "Show still image while the live stream is loading",
"transition_effect": "Live camera transition effect"
},
"media_viewer": {
+1
View File
@@ -125,6 +125,7 @@
"lazy_load": "Le telecamere dal vivo sono pigramente cariche",
"lazy_unload": "Le telecamere dal vivo sono pigramente non caricate",
"preload": "Precarica Live View in background",
"show_image_during_load": "",
"transition_effect": "Effetto di transizione della telecamera dal vivo"
},
"media_viewer": {
+1
View File
@@ -125,6 +125,7 @@
"lazy_load": "As câmeras ao vivo são carregadas lentamente",
"lazy_unload": "As câmeras ao vivo são descarregadas preguiçosamente",
"preload": "Pré-carregar a visualização ao vivo em segundo plano",
"show_image_during_load": "",
"transition_effect": "Efeito de transição de câmera ao vivo"
},
"media_viewer": {
+3
View File
@@ -0,0 +1,3 @@
.hidden {
display: none;
}
+2
View File
@@ -642,6 +642,7 @@ const liveConfigDefault = {
lazy_unload: 'never' as const,
draggable: true,
transition_effect: 'slide' as const,
show_image_during_load: true,
controls: {
next_previous: {
size: 48,
@@ -739,6 +740,7 @@ const liveOverridableConfigSchema = z
.default(liveConfigDefault.controls.title),
})
.default(liveConfigDefault.controls),
show_image_during_load: z.boolean().default(liveConfigDefault.show_image_during_load),
})
.merge(actionsSchema);