Rework autoplay/pause into a plugin, apply to viewer & live.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { CSSResultGroup, LitElement, unsafeCSS, PropertyValues } from 'lit';
|
import { CSSResultGroup, LitElement, unsafeCSS, PropertyValues } from 'lit';
|
||||||
import EmblaCarousel, { EmblaCarouselType, EmblaOptionsType } from 'embla-carousel';
|
import EmblaCarousel, { EmblaCarouselType, EmblaOptionsType, EmblaPluginType } from 'embla-carousel';
|
||||||
|
|
||||||
import { dispatchFrigateCardEvent } from '../common';
|
import { dispatchFrigateCardEvent } from '../common';
|
||||||
|
|
||||||
@@ -53,6 +53,14 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Embla plugins to use.
|
||||||
|
* @returns An EmblaOptionsType object or undefined for no options.
|
||||||
|
*/
|
||||||
|
protected _getPlugins(): EmblaPluginType[] | undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
protected _destroyCarousel(): void {
|
protected _destroyCarousel(): void {
|
||||||
if (this._carousel) {
|
if (this._carousel) {
|
||||||
this._carousel.destroy();
|
this._carousel.destroy();
|
||||||
@@ -69,7 +77,7 @@ export class FrigateCardCarousel extends LitElement {
|
|||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
|
|
||||||
if (carouselNode) {
|
if (carouselNode) {
|
||||||
this._carousel = EmblaCarousel(carouselNode, this._getOptions());
|
this._carousel = EmblaCarousel(carouselNode, this._getOptions(), this._getPlugins());
|
||||||
this._carousel.on('init', () => dispatchFrigateCardEvent(this, 'carousel:init'));
|
this._carousel.on('init', () => dispatchFrigateCardEvent(this, 'carousel:init'));
|
||||||
this._carousel.on('select', () => {
|
this._carousel.on('select', () => {
|
||||||
const selected = this.carouselSelected();
|
const selected = this.carouselSelected();
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { EmblaCarouselType, EmblaPluginType } from 'embla-carousel';
|
||||||
|
import { FrigateCardMediaPlayer } from '../../types';
|
||||||
|
|
||||||
|
export type MediaAutoplayOptionsType = {
|
||||||
|
autoplay?: boolean;
|
||||||
|
autopause?: boolean;
|
||||||
|
playerSelector: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const defaultOptions: Partial<MediaAutoplayOptionsType> = {
|
||||||
|
autoplay: true,
|
||||||
|
autopause: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MediaAutoplayType = EmblaPluginType<MediaAutoplayOptionsType>;
|
||||||
|
|
||||||
|
export function MediaAutoplay(
|
||||||
|
userOptions?: MediaAutoplayOptionsType,
|
||||||
|
): MediaAutoplayType {
|
||||||
|
const options = Object.assign({}, defaultOptions, userOptions);
|
||||||
|
|
||||||
|
let carousel: EmblaCarouselType;
|
||||||
|
let slides: HTMLElement[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the plugin.
|
||||||
|
*/
|
||||||
|
function init(embla: EmblaCarouselType): void {
|
||||||
|
carousel = embla;
|
||||||
|
slides = carousel.slideNodes();
|
||||||
|
|
||||||
|
if (options.autopause) {
|
||||||
|
carousel.on('destroy', pauseAllHandler);
|
||||||
|
carousel.on('select', autopausePreviousHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.autoplay) {
|
||||||
|
carousel.on('select', autoplayCurrentHandler);
|
||||||
|
carousel.on('init', autoplayCurrentHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the plugin.
|
||||||
|
*/
|
||||||
|
function destroy(): void {
|
||||||
|
if (options.autopause) {
|
||||||
|
carousel.off('destroy', pauseAllHandler);
|
||||||
|
carousel.off('select', autopausePreviousHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.autoplay) {
|
||||||
|
carousel.off('select', autoplayCurrentHandler);
|
||||||
|
carousel.off('init', autoplayCurrentHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the media player from a slide.
|
||||||
|
* @param slide
|
||||||
|
* @returns A FrigateCardMediaPlayer object or `null`.
|
||||||
|
*/
|
||||||
|
function getPlayer(slide: HTMLElement): FrigateCardMediaPlayer | null {
|
||||||
|
return slide.querySelector(options.playerSelector) as FrigateCardMediaPlayer | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause all clips.
|
||||||
|
*/
|
||||||
|
function pauseAllHandler(): void {
|
||||||
|
slides.forEach((slide) => getPlayer(slide)?.pause());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autoplay the current slide.
|
||||||
|
*/
|
||||||
|
function autoplayCurrentHandler(): void {
|
||||||
|
getPlayer(slides[carousel.selectedScrollSnap()])?.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autopause the previous slide.
|
||||||
|
*/
|
||||||
|
function autopausePreviousHandler(): void {
|
||||||
|
getPlayer(slides[carousel.previousScrollSnap()])?.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
const self: MediaAutoplayType = {
|
||||||
|
name: 'MediaAutoplay',
|
||||||
|
options,
|
||||||
|
init,
|
||||||
|
destroy,
|
||||||
|
};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
+90
-3
@@ -19,16 +19,17 @@ import {
|
|||||||
LiveProvider,
|
LiveProvider,
|
||||||
frigateCardConfigDefaults,
|
frigateCardConfigDefaults,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { EmblaOptionsType } from 'embla-carousel';
|
import { EmblaOptionsType, EmblaPluginType } from 'embla-carousel';
|
||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from 'custom-card-helpers';
|
||||||
|
import { Ref, createRef, ref } from 'lit/directives/ref.js';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
import { ref } from 'lit/directives/ref';
|
|
||||||
import { until } from 'lit/directives/until.js';
|
import { until } from 'lit/directives/until.js';
|
||||||
|
|
||||||
import { BrowseMediaUtil } from '../browse-media-util.js';
|
import { BrowseMediaUtil } from '../browse-media-util.js';
|
||||||
import { ConditionState, getOverriddenConfig } from '../card-condition.js';
|
import { ConditionState, getOverriddenConfig } from '../card-condition.js';
|
||||||
import { FrigateCardMediaCarousel } from './media-carousel.js';
|
import { FrigateCardMediaCarousel } from './media-carousel.js';
|
||||||
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
import { FrigateCardNextPreviousControl } from './next-prev-control.js';
|
||||||
|
import { MediaAutoplay } from './embla-plugins/media-autoplay.js';
|
||||||
import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
import { ThumbnailCarouselTap } from './thumbnail-carousel.js';
|
||||||
import { View } from '../view.js';
|
import { View } from '../view.js';
|
||||||
import { localize } from '../localize/localize.js';
|
import { localize } from '../localize/localize.js';
|
||||||
@@ -291,6 +292,18 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Embla plugins to use.
|
||||||
|
* @returns An EmblaOptionsType object or undefined for no options.
|
||||||
|
*/
|
||||||
|
protected _getPlugins(): EmblaPluginType[] | undefined {
|
||||||
|
return [
|
||||||
|
MediaAutoplay({
|
||||||
|
playerSelector: 'frigate-card-live-provider',
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of slides to lazily load. 0 means all slides are lazy
|
* Returns the number of slides to lazily load. 0 means all slides are lazy
|
||||||
* loaded, 1 means that 1 slide on each side of the currently selected slide
|
* loaded, 1 means that 1 slide on each side of the currently selected slide
|
||||||
@@ -511,6 +524,24 @@ export class FrigateCardLiveProvider extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public label = '';
|
public label = '';
|
||||||
|
|
||||||
|
protected _providerRef: Ref<
|
||||||
|
FrigateCardLiveFrigate | FrigateCardLiveJSMPEG | FrigateCardLiveWebRTC
|
||||||
|
> = createRef();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play the video.
|
||||||
|
*/
|
||||||
|
public play(): void {
|
||||||
|
this._providerRef.value?.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause the video.
|
||||||
|
*/
|
||||||
|
public pause(): void {
|
||||||
|
this._providerRef.value?.pause();
|
||||||
|
}
|
||||||
|
|
||||||
protected _getResolvedProvider(): LiveProvider {
|
protected _getResolvedProvider(): LiveProvider {
|
||||||
if (this.cameraConfig?.live_provider === 'auto') {
|
if (this.cameraConfig?.live_provider === 'auto') {
|
||||||
if (this.cameraConfig?.webrtc?.entity || this.cameraConfig?.webrtc?.url) {
|
if (this.cameraConfig?.webrtc?.entity || this.cameraConfig?.webrtc?.url) {
|
||||||
@@ -545,18 +576,21 @@ export class FrigateCardLiveProvider extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
${provider == 'frigate'
|
${provider == 'frigate'
|
||||||
? html` <frigate-card-live-frigate
|
? html` <frigate-card-live-frigate
|
||||||
|
${ref(this._providerRef)}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.cameraEntity=${this.cameraConfig.camera_entity}
|
.cameraEntity=${this.cameraConfig.camera_entity}
|
||||||
>
|
>
|
||||||
</frigate-card-live-frigate>`
|
</frigate-card-live-frigate>`
|
||||||
: provider == 'webrtc'
|
: provider == 'webrtc'
|
||||||
? html`<frigate-card-live-webrtc
|
? html`<frigate-card-live-webrtc
|
||||||
|
${ref(this._providerRef)}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.cameraConfig=${this.cameraConfig}
|
.cameraConfig=${this.cameraConfig}
|
||||||
.webRTCConfig=${this.liveConfig.webrtc}
|
.webRTCConfig=${this.liveConfig.webrtc}
|
||||||
>
|
>
|
||||||
</frigate-card-live-webrtc>`
|
</frigate-card-live-webrtc>`
|
||||||
: html` <frigate-card-live-jsmpeg
|
: html` <frigate-card-live-jsmpeg
|
||||||
|
${ref(this._providerRef)}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.cameraConfig=${this.cameraConfig}
|
.cameraConfig=${this.cameraConfig}
|
||||||
.jsmpegConfig=${this.liveConfig.jsmpeg}
|
.jsmpegConfig=${this.liveConfig.jsmpeg}
|
||||||
@@ -574,6 +608,22 @@ export class FrigateCardLiveFrigate extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
protected cameraEntity?: string;
|
protected cameraEntity?: string;
|
||||||
|
|
||||||
|
protected _playerRef: Ref<FrigateCardLiveFrigate> = createRef();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play the video.
|
||||||
|
*/
|
||||||
|
public play(): void {
|
||||||
|
this._playerRef.value?.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause the video.
|
||||||
|
*/
|
||||||
|
public pause(): void {
|
||||||
|
this._playerRef.value?.pause();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Master render method.
|
* Master render method.
|
||||||
* @returns A rendered template.
|
* @returns A rendered template.
|
||||||
@@ -591,6 +641,7 @@ export class FrigateCardLiveFrigate extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return html` <frigate-card-ha-camera-stream
|
return html` <frigate-card-ha-camera-stream
|
||||||
|
${ref(this._playerRef)}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${this.hass.states[this.cameraEntity]}
|
.stateObj=${this.hass.states[this.cameraEntity]}
|
||||||
.controls=${true}
|
.controls=${true}
|
||||||
@@ -619,6 +670,28 @@ export class FrigateCardLiveWebRTC extends LitElement {
|
|||||||
|
|
||||||
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
protected hass?: HomeAssistant & ExtendedHomeAssistant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play the video.
|
||||||
|
*/
|
||||||
|
public play(): void {
|
||||||
|
this._getPlayer()?.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause the video.
|
||||||
|
*/
|
||||||
|
public pause(): void {
|
||||||
|
this._getPlayer()?.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the underlying video player.
|
||||||
|
* @returns The player or `null` if not found.
|
||||||
|
*/
|
||||||
|
protected _getPlayer(): HTMLVideoElement | null {
|
||||||
|
return this.renderRoot.querySelector('#video') as HTMLVideoElement | null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the WebRTC element. May throw.
|
* Create the WebRTC element. May throw.
|
||||||
*/
|
*/
|
||||||
@@ -675,7 +748,7 @@ export class FrigateCardLiveWebRTC extends LitElement {
|
|||||||
// Extract the video component after it has been rendered and generate the
|
// Extract the video component after it has been rendered and generate the
|
||||||
// media load event.
|
// media load event.
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
const video = this.renderRoot.querySelector('#video') as HTMLVideoElement;
|
const video = this._getPlayer();
|
||||||
if (video) {
|
if (video) {
|
||||||
const onloadedmetadata = video.onloadedmetadata;
|
const onloadedmetadata = video.onloadedmetadata;
|
||||||
const onplay = video.onplay;
|
const onplay = video.onplay;
|
||||||
@@ -725,6 +798,20 @@ export class FrigateCardLiveJSMPEG extends LitElement {
|
|||||||
protected _jsmpegVideoPlayer?: JSMpeg.VideoElement;
|
protected _jsmpegVideoPlayer?: JSMpeg.VideoElement;
|
||||||
protected _refreshPlayerTimerID?: number;
|
protected _refreshPlayerTimerID?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play the video.
|
||||||
|
*/
|
||||||
|
public play(): void {
|
||||||
|
this._jsmpegVideoPlayer?.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause the video.
|
||||||
|
*/
|
||||||
|
public pause(): void {
|
||||||
|
this._jsmpegVideoPlayer?.stop();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a signed player URL.
|
* Get a signed player URL.
|
||||||
* @returns A URL or null.
|
* @returns A URL or null.
|
||||||
|
|||||||
+12
-65
@@ -7,7 +7,7 @@ import {
|
|||||||
unsafeCSS,
|
unsafeCSS,
|
||||||
} from 'lit';
|
} from 'lit';
|
||||||
import { BrowseMediaUtil } from '../browse-media-util.js';
|
import { BrowseMediaUtil } from '../browse-media-util.js';
|
||||||
import { EmblaOptionsType } from 'embla-carousel';
|
import { EmblaOptionsType, EmblaPluginType } from 'embla-carousel';
|
||||||
import { HomeAssistant } from 'custom-card-helpers';
|
import { HomeAssistant } from 'custom-card-helpers';
|
||||||
import { Task } from '@lit-labs/task';
|
import { Task } from '@lit-labs/task';
|
||||||
import { createRef, Ref, ref } from 'lit/directives/ref.js';
|
import { createRef, Ref, ref } from 'lit/directives/ref.js';
|
||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
FrigateCardThumbnailCarousel,
|
FrigateCardThumbnailCarousel,
|
||||||
ThumbnailCarouselTap,
|
ThumbnailCarouselTap,
|
||||||
} from './thumbnail-carousel.js';
|
} from './thumbnail-carousel.js';
|
||||||
|
import { MediaAutoplay } from './embla-plugins/media-autoplay.js';
|
||||||
import { ResolvedMediaCache, ResolvedMediaUtil } from '../resolved-media.js';
|
import { ResolvedMediaCache, ResolvedMediaUtil } from '../resolved-media.js';
|
||||||
import { View } from '../view.js';
|
import { View } from '../view.js';
|
||||||
import {
|
import {
|
||||||
@@ -293,6 +294,16 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Embla plugins to use.
|
||||||
|
* @returns An EmblaOptionsType object or undefined for no options.
|
||||||
|
*/
|
||||||
|
protected _getPlugins(): EmblaPluginType[] | undefined {
|
||||||
|
return [MediaAutoplay({
|
||||||
|
autoplay: this.viewerConfig?.autoplay_clip,
|
||||||
|
playerSelector: 'frigate-card-ha-hls-player' })];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of slides to lazily load. 0 means all slides are lazy
|
* Returns the number of slides to lazily load. 0 means all slides are lazy
|
||||||
* loaded, 1 means that 1 slide on each side of the currently selected slide
|
* loaded, 1 means that 1 slide on each side of the currently selected slide
|
||||||
@@ -532,70 +543,6 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _playOrPauseClip(action: 'play' | 'pause', slide: HTMLElement): void {
|
|
||||||
const player = slide.querySelector('frigate-card-ha-hls-player') as
|
|
||||||
| (HTMLElement & { play: () => void; pause: () => void })
|
|
||||||
| undefined;
|
|
||||||
if (player) {
|
|
||||||
if (action === 'play') {
|
|
||||||
player.play();
|
|
||||||
} else if (action === 'pause') {
|
|
||||||
player.pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pause all clips.
|
|
||||||
*/
|
|
||||||
protected _pauseAllHandler(): void {
|
|
||||||
if (this._carousel) {
|
|
||||||
this._carousel
|
|
||||||
.slideNodes()
|
|
||||||
.forEach((slide) => this._playOrPauseClip('pause', slide));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Play the clip being shown to the user and pause the prior.
|
|
||||||
*/
|
|
||||||
protected _autoplayPauseHandler(pausePrevious: boolean): void {
|
|
||||||
if (!this._carousel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const slides = this._carousel.slideNodes();
|
|
||||||
|
|
||||||
// Pause the previous/current slide.
|
|
||||||
if (pausePrevious) {
|
|
||||||
this._carousel
|
|
||||||
.slidesInView(false)
|
|
||||||
.forEach((slide) => {
|
|
||||||
this._playOrPauseClip('pause', slides[slide])
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Play the target slide.
|
|
||||||
this._carousel
|
|
||||||
.slidesInView(true)
|
|
||||||
.forEach((slide) => {
|
|
||||||
this._playOrPauseClip('play', slides[slide])
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the carousel.
|
|
||||||
*/
|
|
||||||
protected _initCarousel(): void {
|
|
||||||
super._initCarousel();
|
|
||||||
|
|
||||||
if (this._carousel && this.viewerConfig && this.viewerConfig.autoplay_clip) {
|
|
||||||
this._carousel.on('destroy', () => this._pauseAllHandler());
|
|
||||||
this._carousel.on('init', () => this._autoplayPauseHandler(false));
|
|
||||||
this._carousel.on('select', () => this._autoplayPauseHandler(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get slides to include in the render.
|
* Get slides to include in the render.
|
||||||
* @returns The slides to include in the render and an index keyed by slide
|
* @returns The slides to include in the render and an index keyed by slide
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
// available as compilation time.
|
// available as compilation time.
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
|
|
||||||
|
import { Ref, createRef, ref } from 'lit/directives/ref';
|
||||||
import { TemplateResult, css, html } from 'lit';
|
import { TemplateResult, css, html } from 'lit';
|
||||||
import { customElement } from 'lit/decorators.js';
|
import { customElement } from 'lit/decorators.js';
|
||||||
import { dispatchMediaShowEvent } from '../common.js';
|
import { dispatchMediaShowEvent } from '../common.js';
|
||||||
@@ -34,10 +35,31 @@ customElements.whenDefined('ha-camera-stream').then(() => {
|
|||||||
@customElement('frigate-card-ha-camera-stream')
|
@customElement('frigate-card-ha-camera-stream')
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// 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') {
|
||||||
|
protected _playerRef: Ref<HTMLElement> = createRef();
|
||||||
|
|
||||||
// ========================================================================================
|
// ========================================================================================
|
||||||
// Minor modifications from:
|
// Minor modifications from:
|
||||||
// - https://github.com/home-assistant/frontend/blob/dev/src/components/ha-camera-stream.ts
|
// - https://github.com/home-assistant/frontend/blob/dev/src/components/ha-camera-stream.ts
|
||||||
// ========================================================================================
|
// ========================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play the video.
|
||||||
|
*/
|
||||||
|
public play(): void {
|
||||||
|
this._playerRef.value?.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pause the video.
|
||||||
|
*/
|
||||||
|
public pause(): void {
|
||||||
|
this._playerRef.value?.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Master render method.
|
||||||
|
* @returns A rendered template.
|
||||||
|
*/
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.stateObj) {
|
if (!this.stateObj) {
|
||||||
return html``;
|
return html``;
|
||||||
@@ -59,6 +81,7 @@ customElements.whenDefined('ha-camera-stream').then(() => {
|
|||||||
: this._url
|
: this._url
|
||||||
? html`
|
? html`
|
||||||
<frigate-card-ha-hls-player
|
<frigate-card-ha-hls-player
|
||||||
|
${ref(this._playerRef)}
|
||||||
autoplay
|
autoplay
|
||||||
playsinline
|
playsinline
|
||||||
.allowExoPlayer=${this.allowExoPlayer}
|
.allowExoPlayer=${this.allowExoPlayer}
|
||||||
|
|||||||
@@ -764,6 +764,11 @@ export interface StateParameters {
|
|||||||
style?: StyleInfo;
|
style?: StyleInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FrigateCardMediaPlayer {
|
||||||
|
play(): void;
|
||||||
|
pause(): void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Home Assistant API types.
|
* Home Assistant API types.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user