Fix aspect ratio issue.
This commit is contained in:
@@ -63,12 +63,8 @@ export class BrowseMediaUtil {
|
|||||||
if (!media || !media.children) {
|
if (!media || !media.children) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < media.children.length; i++) {
|
const index = media.children.findIndex((child) => this.isTrueMedia(child));
|
||||||
if (this.isTrueMedia(media.children[i])) {
|
return index >= 0 ? index : null;
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
+127
-14
@@ -93,7 +93,9 @@ console.info(
|
|||||||
documentationURL: REPO_URL,
|
documentationURL: REPO_URL,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Main FrigateCard class.
|
/**
|
||||||
|
* Main FrigateCard class.
|
||||||
|
*/
|
||||||
@customElement('frigate-card')
|
@customElement('frigate-card')
|
||||||
export class FrigateCard extends LitElement {
|
export class FrigateCard extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
@@ -140,6 +142,9 @@ export class FrigateCard extends LitElement {
|
|||||||
// A cache of resolved media URLs/mimetypes for use in the whole card.
|
// A cache of resolved media URLs/mimetypes for use in the whole card.
|
||||||
protected _resolvedMediaCache = new ResolvedMediaCache();
|
protected _resolvedMediaCache = new ResolvedMediaCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Home Assistant object.
|
||||||
|
*/
|
||||||
set hass(hass: HomeAssistant & ExtendedHomeAssistant) {
|
set hass(hass: HomeAssistant & ExtendedHomeAssistant) {
|
||||||
this._hass = hass;
|
this._hass = hass;
|
||||||
|
|
||||||
@@ -156,12 +161,20 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the configuration element.
|
/**
|
||||||
|
* Get the card editor element.
|
||||||
|
* @returns A LovelaceCardEditor element.
|
||||||
|
*/
|
||||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||||
return document.createElement('frigate-card-editor');
|
return document.createElement('frigate-card-editor');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a stub basic config using the first available camera of any kind.
|
/**
|
||||||
|
* Get a stub basic config using the first available camera of any kind.
|
||||||
|
* @param _hass The Home Assistant object.
|
||||||
|
* @param entities The entities available to Home Assistant.
|
||||||
|
* @returns A valid stub card configuration.
|
||||||
|
*/
|
||||||
public static getStubConfig(
|
public static getStubConfig(
|
||||||
_hass: HomeAssistant,
|
_hass: HomeAssistant,
|
||||||
entities: string[],
|
entities: string[],
|
||||||
@@ -172,6 +185,10 @@ export class FrigateCard extends LitElement {
|
|||||||
} as FrigateCardConfig;
|
} as FrigateCardConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the menu buttons to display.
|
||||||
|
* @returns An array of menu buttons.
|
||||||
|
*/
|
||||||
protected _getMenuButtons(): MenuButton[] {
|
protected _getMenuButtons(): MenuButton[] {
|
||||||
const buttons: MenuButton[] = [];
|
const buttons: MenuButton[] = [];
|
||||||
|
|
||||||
@@ -245,6 +262,10 @@ export class FrigateCard extends LitElement {
|
|||||||
return buttons.concat(this._dynamicMenuButtons);
|
return buttons.concat(this._dynamicMenuButtons);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a dynamic (elements) menu button.
|
||||||
|
* @param button The button to add.
|
||||||
|
*/
|
||||||
public _addDynamicMenuButton(button: MenuButton): void {
|
public _addDynamicMenuButton(button: MenuButton): void {
|
||||||
if (!this._dynamicMenuButtons.includes(button)) {
|
if (!this._dynamicMenuButtons.includes(button)) {
|
||||||
this._dynamicMenuButtons = [...this._dynamicMenuButtons, button];
|
this._dynamicMenuButtons = [...this._dynamicMenuButtons, button];
|
||||||
@@ -252,6 +273,10 @@ export class FrigateCard extends LitElement {
|
|||||||
this._menu.buttons = this._getMenuButtons();
|
this._menu.buttons = this._getMenuButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a dynamic (elements) menu button that was previously added.
|
||||||
|
* @param target The button to remove.
|
||||||
|
*/
|
||||||
public _removeDynamicMenuButton(target: MenuButton): void {
|
public _removeDynamicMenuButton(target: MenuButton): void {
|
||||||
this._dynamicMenuButtons = this._dynamicMenuButtons.filter(
|
this._dynamicMenuButtons = this._dynamicMenuButtons.filter(
|
||||||
(button) => button != target,
|
(button) => button != target,
|
||||||
@@ -259,6 +284,10 @@ export class FrigateCard extends LitElement {
|
|||||||
this._menu.buttons = this._getMenuButtons();
|
this._menu.buttons = this._getMenuButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Frigate camera name through a variety of means.
|
||||||
|
* @returns The Frigate camera name or null if unavailable.
|
||||||
|
*/
|
||||||
protected async _getFrigateCameraName(): Promise<string | null> {
|
protected async _getFrigateCameraName(): Promise<string | null> {
|
||||||
// No camera name specified, apply two heuristics in this order:
|
// No camera name specified, apply two heuristics in this order:
|
||||||
// - Get the entity information and pull out the camera name from the unique_id.
|
// - Get the entity information and pull out the camera name from the unique_id.
|
||||||
@@ -304,6 +333,11 @@ export class FrigateCard extends LitElement {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get configuration parse errors.
|
||||||
|
* @param error The ZodError object from parsing.
|
||||||
|
* @returns An array of string error paths.
|
||||||
|
*/
|
||||||
protected _getParseErrorPaths<T>(error: z.ZodError<T>): string[] {
|
protected _getParseErrorPaths<T>(error: z.ZodError<T>): string[] {
|
||||||
/* Zod errors involving unions are complex, as Zod may not be able to tell
|
/* Zod errors involving unions are complex, as Zod may not be able to tell
|
||||||
* where the 'real' error is vs simply a union option not matching. This
|
* where the 'real' error is vs simply a union option not matching. This
|
||||||
@@ -338,8 +372,12 @@ export class FrigateCard extends LitElement {
|
|||||||
return contenders;
|
return contenders;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert an array of strings and indices into a more human readable string,
|
/**
|
||||||
// e.g. [a, 1, b, 2] => 'a[1] -> b[2]'
|
* Convert an array of strings and indices into a more human readable string,
|
||||||
|
* e.g. [a, 1, b, 2] => 'a[1] -> b[2]'
|
||||||
|
* @param path An array of strings and numbers.
|
||||||
|
* @returns A single string.
|
||||||
|
*/
|
||||||
protected _getParseErrorPathString(path: (string | number)[]): string {
|
protected _getParseErrorPathString(path: (string | number)[]): string {
|
||||||
let out = '';
|
let out = '';
|
||||||
for (let i = 0; i < path.length; i++) {
|
for (let i = 0; i < path.length; i++) {
|
||||||
@@ -355,7 +393,10 @@ export class FrigateCard extends LitElement {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the object configuration.
|
/**
|
||||||
|
* Set the card configuration.
|
||||||
|
* @param inputConfig The card configuration.
|
||||||
|
*/
|
||||||
public setConfig(inputConfig: FrigateCardConfig): void {
|
public setConfig(inputConfig: FrigateCardConfig): void {
|
||||||
if (!inputConfig) {
|
if (!inputConfig) {
|
||||||
throw new Error(localize('error.invalid_configuration:'));
|
throw new Error(localize('error.invalid_configuration:'));
|
||||||
@@ -401,7 +442,11 @@ export class FrigateCard extends LitElement {
|
|||||||
this._changeView(e.detail);
|
this._changeView(e.detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine whether the card should be updated.
|
/**
|
||||||
|
* Determine whether the card should be updated.
|
||||||
|
* @param changedProps The changed properties if any.
|
||||||
|
* @returns True if the card should be updated.
|
||||||
|
*/
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
if (!this.config) {
|
if (!this.config) {
|
||||||
return false;
|
return false;
|
||||||
@@ -426,6 +471,9 @@ export class FrigateCard extends LitElement {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download media being displayed in the viewer.
|
||||||
|
*/
|
||||||
protected async _downloadViewerMedia(): Promise<void> {
|
protected async _downloadViewerMedia(): Promise<void> {
|
||||||
if (!this._hass || !this._view.isViewerView()) {
|
if (!this._hass || !this._view.isViewerView()) {
|
||||||
// Should not occur.
|
// Should not occur.
|
||||||
@@ -477,6 +525,11 @@ export class FrigateCard extends LitElement {
|
|||||||
link.remove();
|
link.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a menu button being clicked.
|
||||||
|
* @param action The action to be called from the clicked button.
|
||||||
|
* @param button The button that was clicked.
|
||||||
|
*/
|
||||||
protected _menuActionHandler(action: string, button: MenuButton): void {
|
protected _menuActionHandler(action: string, button: MenuButton): void {
|
||||||
if (button.type != 'internal-menu-icon') {
|
if (button.type != 'internal-menu-icon') {
|
||||||
handleAction(this, this._hass as HomeAssistant, button, action);
|
handleAction(this, this._hass as HomeAssistant, button, action);
|
||||||
@@ -512,7 +565,10 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the Frigate UI url.
|
/**
|
||||||
|
* Get the Frigate UI URL from context.
|
||||||
|
* @returns The URL or null if unavailable.
|
||||||
|
*/
|
||||||
protected _getFrigateURLFromContext(): string | null {
|
protected _getFrigateURLFromContext(): string | null {
|
||||||
if (!this.config.frigate_url) {
|
if (!this.config.frigate_url) {
|
||||||
return null;
|
return null;
|
||||||
@@ -525,7 +581,9 @@ export class FrigateCard extends LitElement {
|
|||||||
return `${this.config.frigate_url}/events?camera=${this._frigateCameraName}`;
|
return `${this.config.frigate_url}/events?camera=${this._frigateCameraName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record interactions with the card.
|
/**
|
||||||
|
* Handle interaction with the card.
|
||||||
|
*/
|
||||||
protected _interactionHandler(): void {
|
protected _interactionHandler(): void {
|
||||||
if (!this.config.view_timeout) {
|
if (!this.config.view_timeout) {
|
||||||
return;
|
return;
|
||||||
@@ -539,6 +597,10 @@ export class FrigateCard extends LitElement {
|
|||||||
}, this.config.view_timeout * 1000);
|
}, this.config.view_timeout * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the card menu.
|
||||||
|
* @returns A rendered template.
|
||||||
|
*/
|
||||||
protected _renderMenu(): TemplateResult | void {
|
protected _renderMenu(): TemplateResult | void {
|
||||||
const classes = {
|
const classes = {
|
||||||
'hover-menu': this.config.menu_mode.startsWith('hover-'),
|
'hover-menu': this.config.menu_mode.startsWith('hover-'),
|
||||||
@@ -588,6 +650,11 @@ export class FrigateCard extends LitElement {
|
|||||||
this._mediaPlaying = false;
|
this._mediaPlaying = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the message to display and trigger an update.
|
||||||
|
* @param message The message to display.
|
||||||
|
* @param skipUpdate If true an update request is skipped.
|
||||||
|
*/
|
||||||
protected _setMessageAndUpdate(message: Message, skipUpdate?: boolean): void {
|
protected _setMessageAndUpdate(message: Message, skipUpdate?: boolean): void {
|
||||||
// Register the first message, or prioritize errors if there's pre-render competition.
|
// Register the first message, or prioritize errors if there's pre-render competition.
|
||||||
if (!this._message || (message.type == 'error' && this._message.type != 'error')) {
|
if (!this._message || (message.type == 'error' && this._message.type != 'error')) {
|
||||||
@@ -598,10 +665,18 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a message event to render to the user.
|
||||||
|
* @param e The message event.
|
||||||
|
*/
|
||||||
protected _messageHandler(e: CustomEvent<Message>): void {
|
protected _messageHandler(e: CustomEvent<Message>): void {
|
||||||
return this._setMessageAndUpdate(e.detail);
|
return this._setMessageAndUpdate(e.detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a new piece of media being shown.
|
||||||
|
* @param e Event with MediaShowInfo details for the media.
|
||||||
|
*/
|
||||||
protected _mediaShowHandler(e: CustomEvent<MediaShowInfo>): void {
|
protected _mediaShowHandler(e: CustomEvent<MediaShowInfo>): void {
|
||||||
const mediaShowInfo = e.detail;
|
const mediaShowInfo = e.detail;
|
||||||
// In Safari, with WebRTC, 0x0 is occasionally returned during loading,
|
// In Safari, with WebRTC, 0x0 is occasionally returned during loading,
|
||||||
@@ -624,18 +699,29 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler called when fullscreen is toggled.
|
||||||
|
*/
|
||||||
protected _fullScreenHandler(): void {
|
protected _fullScreenHandler(): void {
|
||||||
// Re-render after a change to fullscreen mode to take advantage of
|
// Re-render after a change to fullscreen mode to take advantage of
|
||||||
// the expanded screen real-estate (vs staying in aspect-ratio locked
|
// the expanded screen real-estate (vs staying in aspect-ratio locked
|
||||||
// modes).
|
// modes).
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component connected callback.
|
||||||
|
*/
|
||||||
connectedCallback(): void {
|
connectedCallback(): void {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
if (screenfull.isEnabled) {
|
if (screenfull.isEnabled) {
|
||||||
screenfull.on('change', this._fullScreenHandler.bind(this));
|
screenfull.on('change', this._fullScreenHandler.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component disconnected callback.
|
||||||
|
*/
|
||||||
disconnectedCallback(): void {
|
disconnectedCallback(): void {
|
||||||
if (screenfull.isEnabled) {
|
if (screenfull.isEnabled) {
|
||||||
screenfull.off('change', this._fullScreenHandler.bind(this));
|
screenfull.off('change', this._fullScreenHandler.bind(this));
|
||||||
@@ -643,6 +729,10 @@ export class FrigateCard extends LitElement {
|
|||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the aspect ratio should be enforced given the current view and
|
||||||
|
* context.
|
||||||
|
*/
|
||||||
protected _isAspectRatioEnforced(): boolean {
|
protected _isAspectRatioEnforced(): boolean {
|
||||||
const aspect_ratio_mode = this.config.dimensions?.aspect_ratio_mode ?? 'dynamic';
|
const aspect_ratio_mode = this.config.dimensions?.aspect_ratio_mode ?? 'dynamic';
|
||||||
|
|
||||||
@@ -658,6 +748,11 @@ export class FrigateCard extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the aspect ratio padding required to enforce the aspect ratio (if it is
|
||||||
|
* required).
|
||||||
|
* @returns A padding percentage.
|
||||||
|
*/
|
||||||
protected _getAspectRatioPadding(): number | null {
|
protected _getAspectRatioPadding(): number | null {
|
||||||
if (!this._isAspectRatioEnforced()) {
|
if (!this._isAspectRatioEnforced()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -676,7 +771,9 @@ export class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the call (master render method).
|
/**
|
||||||
|
* Master render method for the card.
|
||||||
|
*/
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
if (this.config.show_warning) {
|
if (this.config.show_warning) {
|
||||||
return this._showWarning(localize('common.show_warning'));
|
return this._showWarning(localize('common.show_warning'));
|
||||||
@@ -745,6 +842,9 @@ export class FrigateCard extends LitElement {
|
|||||||
</ha-card>`;
|
</ha-card>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-render method for the card.
|
||||||
|
*/
|
||||||
protected _render(): TemplateResult | void {
|
protected _render(): TemplateResult | void {
|
||||||
if (!this._hass) {
|
if (!this._hass) {
|
||||||
return html``;
|
return html``;
|
||||||
@@ -863,12 +963,20 @@ export class FrigateCard extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show a warning card.
|
/**
|
||||||
|
* Show a warning card.
|
||||||
|
* @param warning The warning message.
|
||||||
|
* @returns A rendered template.
|
||||||
|
*/
|
||||||
private _showWarning(warning: string): TemplateResult {
|
private _showWarning(warning: string): TemplateResult {
|
||||||
return html` <hui-warning> ${warning} </hui-warning> `;
|
return html` <hui-warning> ${warning} </hui-warning> `;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show an error card.
|
/**
|
||||||
|
* Show an error card.
|
||||||
|
* @param error The error message.
|
||||||
|
* @returns A rendered template.
|
||||||
|
*/
|
||||||
private _showError(error: string): TemplateResult {
|
private _showError(error: string): TemplateResult {
|
||||||
const errorCard = document.createElement('hui-error-card');
|
const errorCard = document.createElement('hui-error-card');
|
||||||
errorCard.setConfig({
|
errorCard.setConfig({
|
||||||
@@ -880,12 +988,17 @@ export class FrigateCard extends LitElement {
|
|||||||
return html` ${errorCard} `;
|
return html` ${errorCard} `;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return compiled CSS styles (thus safe to use with unsafeCSS).
|
/**
|
||||||
|
* Return compiled CSS styles (thus safe to use with unsafeCSS).
|
||||||
|
*/
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return unsafeCSS(cardStyle);
|
return unsafeCSS(cardStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the Lovelace card size.
|
/**
|
||||||
|
* Get the Lovelace card size.
|
||||||
|
* @returns The Lovelace card size in units of 50px.
|
||||||
|
*/
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
if (this._mediaShowInfo) {
|
if (this._mediaShowInfo) {
|
||||||
return this._mediaShowInfo.height / 50;
|
return this._mediaShowInfo.height / 50;
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ import './next-prev-control.js';
|
|||||||
|
|
||||||
import viewerStyle from '../scss/viewer.scss';
|
import viewerStyle from '../scss/viewer.scss';
|
||||||
|
|
||||||
const IMG_TRANSPARENT_1x1 =
|
const IMG_EMPTY = 'data:,';
|
||||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
|
|
||||||
|
|
||||||
@customElement('frigate-card-viewer')
|
@customElement('frigate-card-viewer')
|
||||||
export class FrigateCardViewer extends LitElement {
|
export class FrigateCardViewer extends LitElement {
|
||||||
@@ -549,10 +548,10 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
slideIndex: number,
|
slideIndex: number,
|
||||||
event: CustomEvent<MediaShowInfo>,
|
event: CustomEvent<MediaShowInfo>,
|
||||||
): void {
|
): void {
|
||||||
this._mediaShowInfoHandler(slideIndex, event.detail);
|
|
||||||
// Don't allow the inbound event to propagate upwards, that will be
|
// Don't allow the inbound event to propagate upwards, that will be
|
||||||
// automatically done at the appropriate time as the slide is shown.
|
// automatically done at the appropriate time as the slide is shown.
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
this._mediaShowInfoHandler(slideIndex, event.detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -632,7 +631,7 @@ export class FrigateCardViewerCore extends LitElement {
|
|||||||
/>
|
/>
|
||||||
</video>`
|
</video>`
|
||||||
: html`<img
|
: html`<img
|
||||||
src=${ifDefined(this.lazyLoad ? IMG_TRANSPARENT_1x1 : resolvedMedia.url)}
|
src=${ifDefined(this.lazyLoad ? IMG_EMPTY : resolvedMedia.url)}
|
||||||
data-src=${ifDefined(this.lazyLoad ? resolvedMedia.url : undefined)}
|
data-src=${ifDefined(this.lazyLoad ? resolvedMedia.url : undefined)}
|
||||||
title="${mediaToRender.title}"
|
title="${mediaToRender.title}"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user