Automatically mute if autoplay is blocked.
This commit is contained in:
@@ -352,7 +352,7 @@ See the [fully expanded live configuration example](#config-expanded-live) for h
|
||||
| `preload` | `false` | :heavy_multiplication_x: | Whether or not to preload the live view. Preloading causes the live view to render in the background regardless of what view is actually shown, so it's instantly available when requested. This consumes additional network/CPU resources continually. |
|
||||
| `auto_play` | `all` | :heavy_multiplication_x: | Whether to automatically play live camera feeds. `never` will never automatically play, `selected` will automatically play when a camera is selected in the carousel, `visible` will automatically play when the browser/tab becomes visible or `all` on any opportunity to automatically play (i.e. either case). Some live providers (e.g. `webrtc-card`, `jsmpeg`) do not support the prevention of automatic play on initial load, but should still respect the value of this flag on play-after-pause.|
|
||||
| `auto_pause` | `never` | :heavy_multiplication_x: | Whether to automatically pause live camera feeds. `never` will never automatically pause, `unselected` will automatically pause when a camera is unselected in the carousel, `hidden` will automatically pause when the browser/tab becomes hidden or `all` on any opportunity to automatically pause (i.e. either case). **Caution**: Some live providers (e.g. `jsmpeg`) may not offer human-accessible means to resume play if it is paused, unless the `auto_play` option (above) is used.|
|
||||
| `auto_mute` | `all` | :heavy_multiplication_x: | Whether to automatically mute live camera feeds. `never` will never automatically mute, `unselected` will automatically mute when a camera is unselected in the carousel, `hidden` will automatically mute when the browser/tab becomes hidden or `all` on any opportunity to automatically mute (i.e. either case).|
|
||||
| `auto_mute` | `all` | :heavy_multiplication_x: | Whether to automatically mute live camera feeds. `never` will never automatically mute, `unselected` will automatically mute when a camera is unselected in the carousel, `hidden` will automatically mute when the browser/tab becomes hidden or `all` on any opportunity to automatically mute (i.e. either case). Note that if `auto_play` is enabled, the stream may mute itself automatically in order to honor the `auto_play` setting, as some browsers will not auto play media that is unmuted -- that is to say, where necessary, the `auto_play` parameter will take priority over the `auto_mute` parameter.|
|
||||
| `auto_unmute` | `never` | :heavy_multiplication_x: | Whether to automatically unmute live camera feeds. `never` will never automatically unmute, `selected` will automatically unmute when a camera is unselected in the carousel, `visible` will automatically unmute when the browser/tab becomes visible or `all` on any opportunity to automatically unmute (i.e. either case).|
|
||||
| `lazy_load` | `true` | :heavy_multiplication_x: | Whether or not to lazily load cameras in the camera carousel. Setting this will `false` will cause all cameras to load simultaneously when the `live` carousel is opened (or cause all cameras to load continually if both `lazy_load` and `preload` are `true`). This will result in a smoother carousel experience at a cost of (potentially) a substantial amount of continually streamed data. |
|
||||
| `lazy_unload` | `never` | :heavy_multiplication_x: | When to lazily **un**load lazyily-loaded cameras. `never` will never lazily-unload, `unselected` will lazy-unload a camera when it is unselected in the carousel, `hidden` will lazy-unload all cameras when the browser/tab becomes hidden or `all` on any opportunity to lazily unload (i.e. either case). This will cause a reloading delay on revisiting that camera in the carousel but will save the streaming network resources that are otherwise consumed. This option has no effect if `lazy_load` is false. Some live providers (e.g. `webrtc-card`) implement their own lazy unloading independently which may occur regardless of the value of this setting.|
|
||||
|
||||
@@ -8,7 +8,11 @@ import {
|
||||
} from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import liveMSEStyle from '../../scss/live-go2rtc.scss';
|
||||
import { CameraConfig, ExtendedHomeAssistant } from '../../types.js';
|
||||
import {
|
||||
CameraConfig,
|
||||
ExtendedHomeAssistant,
|
||||
FrigateCardMediaPlayer,
|
||||
} from '../../types.js';
|
||||
import '../image.js';
|
||||
import {
|
||||
hideMediaControlsTemporarily,
|
||||
@@ -51,7 +55,7 @@ class FrigateCardGo2RTCPlayer extends VideoRTC {
|
||||
}
|
||||
|
||||
@customElement('frigate-card-live-go2rtc')
|
||||
export class FrigateCardGo2RTC extends LitElement {
|
||||
export class FrigateCardGo2RTC extends LitElement implements FrigateCardMediaPlayer {
|
||||
// Not an reactive property to avoid resetting the video.
|
||||
public hass?: ExtendedHomeAssistant;
|
||||
|
||||
@@ -63,8 +67,8 @@ export class FrigateCardGo2RTC extends LitElement {
|
||||
|
||||
protected _player?: FrigateCardGo2RTCPlayer;
|
||||
|
||||
public play(): void {
|
||||
this._player?.video?.play();
|
||||
public async play(): Promise<void> {
|
||||
return this._player?.video?.play();
|
||||
}
|
||||
|
||||
public pause(): void {
|
||||
@@ -83,6 +87,10 @@ export class FrigateCardGo2RTC extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
public isMuted(): boolean {
|
||||
return this._player?.video.muted ?? true;
|
||||
}
|
||||
|
||||
public seek(seconds: number): void {
|
||||
if (this._player?.video) {
|
||||
this._player.video.currentTime = seconds;
|
||||
|
||||
@@ -9,9 +9,8 @@ import '../../patches/ha-camera-stream';
|
||||
import '../../patches/ha-hls-player.js';
|
||||
import '../../patches/ha-web-rtc-player.ts';
|
||||
|
||||
|
||||
@customElement('frigate-card-live-ha')
|
||||
export class FrigateCardLiveHA extends LitElement {
|
||||
export class FrigateCardLiveHA extends LitElement implements FrigateCardMediaPlayer {
|
||||
@property({ attribute: false })
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
@@ -20,45 +19,30 @@ export class FrigateCardLiveHA extends LitElement {
|
||||
|
||||
protected _playerRef: Ref<Element & FrigateCardMediaPlayer> = createRef();
|
||||
|
||||
/**
|
||||
* Play the video.
|
||||
*/
|
||||
public play(): void {
|
||||
this._playerRef.value?.play();
|
||||
public async play(): Promise<void> {
|
||||
return this._playerRef.value?.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the video.
|
||||
*/
|
||||
public pause(): void {
|
||||
this._playerRef.value?.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute the video.
|
||||
*/
|
||||
public mute(): void {
|
||||
this._playerRef.value?.mute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmute the video.
|
||||
*/
|
||||
public unmute(): void {
|
||||
this._playerRef.value?.unmute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the video.
|
||||
*/
|
||||
public isMuted(): boolean {
|
||||
return this._playerRef.value?.isMuted() ?? true;
|
||||
}
|
||||
|
||||
public seek(seconds: number): void {
|
||||
this._playerRef.value?.seek(seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Master render method.
|
||||
* @returns A rendered template.
|
||||
*/
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
@@ -79,9 +63,6 @@ export class FrigateCardLiveHA extends LitElement {
|
||||
</frigate-card-ha-camera-stream>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get styles.
|
||||
*/
|
||||
static get styles(): CSSResultGroup {
|
||||
return unsafeCSS(liveHAStyle);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ import { HomeAssistant } from 'custom-card-helpers';
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import liveImageStyle from '../../scss/live-image.scss';
|
||||
import { CameraConfig, LiveImageConfig } from '../../types.js';
|
||||
import { CameraConfig, FrigateCardMediaPlayer, LiveImageConfig } from '../../types.js';
|
||||
import { getStateObjOrDispatchError } from './live.js';
|
||||
import '../image.js';
|
||||
|
||||
@customElement('frigate-card-live-image')
|
||||
export class FrigateCardLiveImage extends LitElement {
|
||||
export class FrigateCardLiveImage extends LitElement implements FrigateCardMediaPlayer {
|
||||
@property({ attribute: false })
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
@@ -20,46 +20,31 @@ export class FrigateCardLiveImage extends LitElement {
|
||||
@state()
|
||||
protected _playing = true;
|
||||
|
||||
/**
|
||||
* Play the video.
|
||||
*/
|
||||
public play(): void {
|
||||
public async play(): Promise<void> {
|
||||
this._playing = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the video.
|
||||
*/
|
||||
public pause(): void {
|
||||
this._playing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute the video.
|
||||
*/
|
||||
public mute(): void {
|
||||
// Not implemented.
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmute the video.
|
||||
*/
|
||||
public unmute(): void {
|
||||
// Not implemented.
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the video.
|
||||
*/
|
||||
public isMuted(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public seek(_seconds: number): void {
|
||||
// Not implemented.
|
||||
}
|
||||
|
||||
/**
|
||||
* Master render method.
|
||||
* @returns A rendered template.
|
||||
*/
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.hass || !this.liveImageConfig) {
|
||||
return;
|
||||
@@ -80,9 +65,6 @@ export class FrigateCardLiveImage extends LitElement {
|
||||
</frigate-card-image>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get styles.
|
||||
*/
|
||||
static get styles(): CSSResultGroup {
|
||||
return unsafeCSS(liveImageStyle);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
CameraConfig,
|
||||
CardWideConfig,
|
||||
ExtendedHomeAssistant,
|
||||
FrigateCardMediaPlayer,
|
||||
JSMPEGConfig,
|
||||
} from '../../types.js';
|
||||
import { dispatchMediaLoadedEvent } from '../../utils/media-info.js';
|
||||
@@ -24,7 +25,7 @@ const JSMPEG_URL_SIGN_EXPIRY_SECONDS = 24 * 60 * 60;
|
||||
const JSMPEG_URL_SIGN_REFRESH_THRESHOLD_SECONDS = 1 * 60 * 60;
|
||||
|
||||
@customElement('frigate-card-live-jsmpeg')
|
||||
export class FrigateCardLiveJSMPEG extends LitElement {
|
||||
export class FrigateCardLiveJSMPEG extends LitElement implements FrigateCardMediaPlayer {
|
||||
@property({ attribute: false })
|
||||
public cameraConfig?: CameraConfig;
|
||||
|
||||
@@ -43,24 +44,14 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
||||
protected _jsmpegVideoPlayer?: JSMpeg.VideoElement;
|
||||
protected _refreshPlayerTimerID?: number;
|
||||
|
||||
/**
|
||||
* Play the video.
|
||||
*/
|
||||
public play(): void {
|
||||
this._jsmpegVideoPlayer?.play();
|
||||
public async play(): Promise<void> {
|
||||
return this._jsmpegVideoPlayer?.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the video.
|
||||
*/
|
||||
public pause(): void {
|
||||
this._jsmpegVideoPlayer?.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute the video (included for completeness, JSMPEG live disables audio as
|
||||
* Frigate does not encode it).
|
||||
*/
|
||||
public mute(): void {
|
||||
const player = this._jsmpegVideoPlayer?.player;
|
||||
if (player) {
|
||||
@@ -68,10 +59,6 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmute the video (included for completeness, JSMPEG live disables audio as
|
||||
* Frigate does not encode it).
|
||||
*/
|
||||
public unmute(): void {
|
||||
const player = this._jsmpegVideoPlayer?.player;
|
||||
if (player) {
|
||||
@@ -79,9 +66,10 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the video (unsupported).
|
||||
*/
|
||||
public isMuted(): boolean {
|
||||
return this._jsmpegVideoPlayer ? this._jsmpegVideoPlayer.player.volume === 0 : true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public seek(_seconds: number): void {
|
||||
// JSMPEG does not support seeking.
|
||||
|
||||
@@ -8,18 +8,25 @@ import {
|
||||
CameraConfig,
|
||||
CardWideConfig,
|
||||
FrigateCardError,
|
||||
FrigateCardMediaPlayer,
|
||||
WebRTCCardConfig,
|
||||
} from '../../types.js';
|
||||
import { contentsChanged } from '../../utils/basic.js';
|
||||
import { dispatchMediaLoadedEvent } from '../../utils/media-info.js';
|
||||
import { dispatchErrorMessageEvent, renderProgressIndicator } from '../message.js';
|
||||
import { renderTask } from '../../utils/task.js';
|
||||
import { hideMediaControlsTemporarily, MEDIA_LOAD_CONTROLS_HIDE_SECONDS } from '../../utils/media.js';
|
||||
import {
|
||||
hideMediaControlsTemporarily,
|
||||
MEDIA_LOAD_CONTROLS_HIDE_SECONDS,
|
||||
} from '../../utils/media.js';
|
||||
|
||||
// Create a wrapper for AlexxIT's WebRTC card
|
||||
// - https://github.com/AlexxIT/WebRTC
|
||||
@customElement('frigate-card-live-webrtc-card')
|
||||
export class FrigateCardLiveWebRTCCard extends LitElement {
|
||||
export class FrigateCardLiveWebRTCCard
|
||||
extends LitElement
|
||||
implements FrigateCardMediaPlayer
|
||||
{
|
||||
@property({ attribute: false, hasChanged: contentsChanged })
|
||||
public webRTCConfig?: WebRTCCardConfig;
|
||||
|
||||
@@ -34,29 +41,14 @@ export class FrigateCardLiveWebRTCCard extends LitElement {
|
||||
// A task to await the load of the WebRTC component.
|
||||
protected _webrtcTask = new Task(this, this._getWebRTCCardElement, () => [1]);
|
||||
|
||||
/**
|
||||
* Play the video.
|
||||
*/
|
||||
public play(): void {
|
||||
this._getPlayer()
|
||||
?.play()
|
||||
.catch(() => {
|
||||
// WebRTC appears to generate additional spurious load events, which may
|
||||
// result in loads after a play() call, which causes the browser to spam
|
||||
// the logs unless the promise rejection is handled here.
|
||||
});
|
||||
public async play(): Promise<void> {
|
||||
return this._getPlayer()?.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the video.
|
||||
*/
|
||||
public pause(): void {
|
||||
this._getPlayer()?.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute the video.
|
||||
*/
|
||||
public mute(): void {
|
||||
const player = this._getPlayer();
|
||||
if (player) {
|
||||
@@ -64,9 +56,6 @@ export class FrigateCardLiveWebRTCCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmute the video.
|
||||
*/
|
||||
public unmute(): void {
|
||||
const player = this._getPlayer();
|
||||
if (player) {
|
||||
@@ -74,9 +63,10 @@ export class FrigateCardLiveWebRTCCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the video.
|
||||
*/
|
||||
public isMuted(): boolean {
|
||||
return this._getPlayer()?.muted ?? true;
|
||||
}
|
||||
|
||||
public seek(seconds: number): void {
|
||||
const player = this._getPlayer();
|
||||
if (player) {
|
||||
|
||||
+20
-21
@@ -515,9 +515,8 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
<frigate-card-live-provider
|
||||
?disabled=${this.liveConfig.lazy_load}
|
||||
.cameraConfig=${cameraConfig}
|
||||
.cameraEndpoints=${guard(
|
||||
[this.cameraManager],
|
||||
() => this.cameraManager?.getCameraEndpoints(cameraID),
|
||||
.cameraEndpoints=${guard([this.cameraManager], () =>
|
||||
this.cameraManager?.getCameraEndpoints(cameraID),
|
||||
)}
|
||||
.label=${cameraMetadata?.title ?? ''}
|
||||
.liveConfig=${config}
|
||||
@@ -664,7 +663,10 @@ export class FrigateCardLiveCarousel extends LitElement {
|
||||
}
|
||||
|
||||
@customElement('frigate-card-live-provider')
|
||||
export class FrigateCardLiveProvider extends LitElement {
|
||||
export class FrigateCardLiveProvider
|
||||
extends LitElement
|
||||
implements FrigateCardMediaPlayer
|
||||
{
|
||||
@property({ attribute: false })
|
||||
public hass?: ExtendedHomeAssistant;
|
||||
|
||||
@@ -694,37 +696,34 @@ export class FrigateCardLiveProvider extends LitElement {
|
||||
|
||||
protected _providerRef: Ref<Element & FrigateCardMediaPlayer> = createRef();
|
||||
|
||||
/**
|
||||
* Play the video.
|
||||
*/
|
||||
public play(): void {
|
||||
this._providerRef.value?.play();
|
||||
public async play(): Promise<void> {
|
||||
// If the play call fails, and the media is not already muted, mute it first
|
||||
// and then try again. This works around some browsers that prevent
|
||||
// auto-play unless the video is muted.
|
||||
this._providerRef.value?.play().catch((ev) => {
|
||||
if (ev.name === 'NotAllowedError' && !this.isMuted()) {
|
||||
this.mute();
|
||||
this._providerRef.value?.play().catch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the video.
|
||||
*/
|
||||
public pause(): void {
|
||||
this._providerRef.value?.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute the video.
|
||||
*/
|
||||
public mute(): void {
|
||||
this._providerRef.value?.mute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmute the video.
|
||||
*/
|
||||
public unmute(): void {
|
||||
this._providerRef.value?.unmute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the video.
|
||||
*/
|
||||
public isMuted(): boolean {
|
||||
return this._providerRef.value?.isMuted() ?? true;
|
||||
}
|
||||
|
||||
public seek(seconds: number): void {
|
||||
this._providerRef.value?.seek(seconds);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ customElements.whenDefined('ha-camera-stream').then(() => {
|
||||
|
||||
@customElement('frigate-card-ha-camera-stream')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class FrigateCardHaCameraStream extends customElements.get('ha-camera-stream') {
|
||||
class FrigateCardHaCameraStream
|
||||
extends customElements.get('ha-camera-stream')
|
||||
implements FrigateCardMediaPlayer
|
||||
{
|
||||
// Due to an obscure behavior when this card is casted, this element needs
|
||||
// to use query rather than the ref directive to find the player.
|
||||
@query('#player')
|
||||
@@ -52,34 +55,26 @@ customElements.whenDefined('ha-camera-stream').then(() => {
|
||||
// - https://github.com/home-assistant/frontend/blob/dev/src/components/ha-camera-stream.ts
|
||||
// ========================================================================================
|
||||
|
||||
/**
|
||||
* Play the video.
|
||||
*/
|
||||
public play(): void {
|
||||
this._player?.play();
|
||||
public async play(): Promise<void> {
|
||||
return this._player?.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the video.
|
||||
*/
|
||||
public pause(): void {
|
||||
this._player?.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute the video.
|
||||
*/
|
||||
public mute(): void {
|
||||
this._player?.mute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmute the video.
|
||||
*/
|
||||
public unmute(): void {
|
||||
this._player?.unmute();
|
||||
}
|
||||
|
||||
public isMuted(): boolean {
|
||||
return this._player?.isMuted() ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the video (unsupported).
|
||||
*/
|
||||
|
||||
@@ -19,33 +19,28 @@ import {
|
||||
hideMediaControlsTemporarily,
|
||||
MEDIA_LOAD_CONTROLS_HIDE_SECONDS,
|
||||
} from '../utils/media.js';
|
||||
import { FrigateCardMediaPlayer } from '../types.js';
|
||||
|
||||
customElements.whenDefined('ha-hls-player').then(() => {
|
||||
@customElement('frigate-card-ha-hls-player')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class FrigateCardHaHlsPlayer extends customElements.get('ha-hls-player') {
|
||||
class FrigateCardHaHlsPlayer
|
||||
extends customElements.get('ha-hls-player')
|
||||
implements FrigateCardMediaPlayer
|
||||
{
|
||||
// Due to an obscure behavior when this card is casted, this element needs
|
||||
// to use query rather than the ref directive to find the player.
|
||||
@query('#video')
|
||||
protected _video: HTMLVideoElement;
|
||||
|
||||
/**
|
||||
* Play the video.
|
||||
*/
|
||||
public play(): void {
|
||||
this._video?.play();
|
||||
public async play(): Promise<void> {
|
||||
return this._video?.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the video.
|
||||
*/
|
||||
public pause(): void {
|
||||
this._video?.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute the video.
|
||||
*/
|
||||
public mute(): void {
|
||||
// The muted property is only for the initial muted state. Must explicitly
|
||||
// set the muted on the video player to make the change dynamic.
|
||||
@@ -54,9 +49,6 @@ customElements.whenDefined('ha-hls-player').then(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmute the video.
|
||||
*/
|
||||
public unmute(): void {
|
||||
// See note in mute().
|
||||
if (this._video) {
|
||||
@@ -64,9 +56,10 @@ customElements.whenDefined('ha-hls-player').then(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the video.
|
||||
*/
|
||||
public isMuted(): boolean {
|
||||
return this._video?.muted() ?? true;
|
||||
}
|
||||
|
||||
public seek(seconds: number): void {
|
||||
if (this._video) {
|
||||
hideMediaControlsTemporarily(this._video);
|
||||
|
||||
@@ -23,29 +23,23 @@ import liveHAComponentsStyle from '../scss/live-ha-components.scss';
|
||||
customElements.whenDefined('ha-web-rtc-player').then(() => {
|
||||
@customElement('frigate-card-ha-web-rtc-player')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class FrigateCardHaWebRtcPlayer extends customElements.get('ha-web-rtc-player') {
|
||||
class FrigateCardHaWebRtcPlayer
|
||||
extends customElements.get('ha-web-rtc-player')
|
||||
implements FrigateCardMediaPlayer
|
||||
{
|
||||
// Due to an obscure behavior when this card is casted, this element needs
|
||||
// to use query rather than the ref directive to find the player.
|
||||
@query('#remote-stream')
|
||||
protected _video: HTMLVideoElement;
|
||||
|
||||
/**
|
||||
* Play the video.
|
||||
*/
|
||||
public play(): void {
|
||||
this._video?.play();
|
||||
public async play(): Promise<void> {
|
||||
return this._video?.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the video.
|
||||
*/
|
||||
public pause(): void {
|
||||
this._video?.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute the video.
|
||||
*/
|
||||
public mute(): void {
|
||||
// The muted property is only for the initial muted state. Must explicitly
|
||||
// set the muted on the video player to make the change dynamic.
|
||||
@@ -54,9 +48,6 @@ customElements.whenDefined('ha-web-rtc-player').then(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmute the video.
|
||||
*/
|
||||
public unmute(): void {
|
||||
// See note in mute().
|
||||
if (this._video) {
|
||||
@@ -64,9 +55,10 @@ customElements.whenDefined('ha-web-rtc-player').then(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the video.
|
||||
*/
|
||||
public isMuted(): boolean {
|
||||
return this._video?.muted ?? true;
|
||||
}
|
||||
|
||||
public seek(seconds: number): void {
|
||||
if (this._video) {
|
||||
this._video.currentTime = seconds;
|
||||
|
||||
+2
-1
@@ -1347,10 +1347,11 @@ export interface StateParameters {
|
||||
}
|
||||
|
||||
export interface FrigateCardMediaPlayer {
|
||||
play(): void;
|
||||
play(): Promise<void>;
|
||||
pause(): void;
|
||||
mute(): void;
|
||||
unmute(): void;
|
||||
isMuted(): boolean;
|
||||
seek(seconds: number): void;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user