From 7c0e0133ee76521b95e203c30d66c0ba97dfae1e Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sun, 26 Sep 2021 14:05:06 -0700 Subject: [PATCH] Fullscreen improvements (esp. portrait media) --- src/card.ts | 48 +++++++++++++++++++++++++++--------- src/editor.ts | 12 +++++++++ src/patches/ha-hls-player.ts | 6 +++-- src/scss/card.scss | 12 ++++----- src/types.ts | 2 +- src/view.ts | 4 +++ 6 files changed, 63 insertions(+), 21 deletions(-) diff --git a/src/card.ts b/src/card.ts index f60a1bd6..8dfcea6f 100644 --- a/src/card.ts +++ b/src/card.ts @@ -192,7 +192,7 @@ export class FrigateCard extends LitElement { description: localize('menu.frigate_ui'), }); } - if ((this.config.menu_buttons?.fullscreen ?? false) && screenfull.isEnabled) { + if ((this.config.menu_buttons?.fullscreen ?? true) && screenfull.isEnabled) { buttons.set('fullscreen', { icon: screenfull.isFullscreen ? 'mdi:fullscreen-exit' : 'mdi:fullscreen', description: localize('menu.fullscreen'), @@ -418,14 +418,15 @@ export class FrigateCard extends LitElement { protected _getAspectRatioPadding(): number | null { const aspect_ratio_mode = this.config.dimensions?.aspect_ratio_mode ?? 'dynamic'; - // Do not constrain aspect ratio if either it's entire disabled or it's a - // media view (i.e. not the gallery) and there's a loaded media item in - // dynamic mode (as the aspect_ratio is essentially whatever the media - // dimensions are). + // Do not artifically constrain aspect ratio if: + // - It's fullscreen. + // - Aspect ratio enforcement is disabled. + // - Or it's a media view (i.e. not the gallery) and there's a loaded media + // item. if ( (screenfull.isEnabled && screenfull.isFullscreen) || aspect_ratio_mode == 'unconstrained' || - (!this._view.isGalleryView() && aspect_ratio_mode == 'dynamic' && this._mediaInfo) + (this._view.isMediaView() && aspect_ratio_mode == 'dynamic' && this._mediaInfo) ) { return null; } @@ -452,11 +453,34 @@ export class FrigateCard extends LitElement { } const padding = this._getAspectRatioPadding(); - let containerStyleMap = {}; + const outerStyle = {}, innerStyle = {}; + + // Padding to force a particular aspect ratio. if (padding != null) { - containerStyleMap = { - 'padding-top': `${padding}%`, - }; + outerStyle['padding-top'] = `${padding}%`; + } + + // Special treatment required when: + // + // - It's in fullscreen mode + // - It's viewing a media clip + // - And the media clip is taller than wider (portrait) + // + // We cannot seem to scale the video by height in CSS without actually + // styling the underlying video element (which we do not have access to as + // it's buried past multiple shadow roots), so instead scale the width in + // terms of'vh' (viewport height) in proportion to the aspect-ratio of the + // media. + if ( + screenfull.isEnabled && + screenfull.isFullscreen && + this._view.isMediaView() && + this._mediaInfo && + this._mediaInfo.width < this._mediaInfo.height + ) { + innerStyle['max-width'] = `${ + (100 * this._mediaInfo.width) / this._mediaInfo.height + }vh`; } const contentClasses = { @@ -466,8 +490,8 @@ export class FrigateCard extends LitElement { return html` ${this.config.menu_mode == 'above' ? this._renderMenu() : ''} -
-
+
+
${this._view.is('clips') || this._view.is('snapshots') ? html` +
+ + +
` : ''}
diff --git a/src/patches/ha-hls-player.ts b/src/patches/ha-hls-player.ts index e05422c1..7ce1e024 100644 --- a/src/patches/ha-hls-player.ts +++ b/src/patches/ha-hls-player.ts @@ -31,8 +31,10 @@ customElements.whenDefined("ha-hls-player").then(() => { .muted=${this.muted} ?playsinline=${this.playsInline} ?controls=${this.controls} - @loadedmetadata=${(e) => dispatchMediaLoadEvent(this, e)} - @loadeddata=${this._elementResized} + @loadeddata=${(e) => { + this._elementResized(); + dispatchMediaLoadEvent(this, e); + }} @pause=${() => dispatchPauseEvent(this)} @play=${() => dispatchPlayEvent(this)} > diff --git a/src/scss/card.scss b/src/scss/card.scss index 534733fe..d56110e5 100644 --- a/src/scss/card.scss +++ b/src/scss/card.scss @@ -2,19 +2,18 @@ position: relative; overflow: auto; height: 100%; + width: 100%; + margin: auto; + display: flex; + justify-content: center; } .frigate-card-contents { width: 100%; - height: 100%; + margin: auto; overflow: auto; -ms-overflow-style: none; /* Hide scrollbar: IE and Edge */ scrollbar-width: none; /* Hide scrollbar: Firefox */ - - // Vertically align items in case the container is larger than the media (e.g. - // fullscreen mode, or forced aspect ratios). - display: flex; - align-items: center; } /* Hide scrollbar for Chrome, Safari and Opera */ @@ -61,6 +60,7 @@ ha-card a { frigate-card-gallery, frigate-card-viewer, frigate-card-live { width: 100%; + display: block; } frigate-card-gallery { height: 100%; diff --git a/src/types.ts b/src/types.ts index ae41dd8e..0cd9036e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -82,7 +82,7 @@ export const frigateCardConfigSchema = z.object({ clips: z.boolean().default(true), snapshots: z.boolean().default(true), frigate_ui: z.boolean().default(true), - fullscreen: z.boolean().default(false), + fullscreen: z.boolean().default(true), }) .optional(), entities: z diff --git a/src/view.ts b/src/view.ts index 236944e0..7e61850d 100644 --- a/src/view.ts +++ b/src/view.ts @@ -28,6 +28,10 @@ export class View { return this.view == 'clips' || this.view == 'snapshots'; } + public isMediaView(): boolean { + return !this.isGalleryView(); + } + get media(): BrowseMediaSource | undefined { if (this.target) { if (this.target.children && this.childIndex !== undefined) {