Automatically mute if autoplay is blocked.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user