Skip to content

Commit

Permalink
Add watermark to temporary image on live view.
Browse files Browse the repository at this point in the history
  • Loading branch information
dermotduffy committed Oct 8, 2023
1 parent 04aef8a commit de7d412
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 53 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4107,9 +4107,9 @@ See [screenshot above](#screenshots-card-casting).

<a name="v4-troubleshooting"></a>

### v4 doesn't show recordings / clips
### Small circular logo/watermark continually shown on livestream

You must be using a version of the [Frigate integration](https://github.com/blakeblackshear/frigate-hass-integration) >= 3.0.0-rc.2 to see recordings. Using an older version of the integration may also show blank thumbnails in the events viewer. Please upgrade your integration accordingly.
If the `live.show_image_during_load` option is enabled (the default), a temporary image from Home Assistant is rendered and refreshed every `1s` while the full stream is loading. When this temporary image is being shown, a small circular icon is rendered on the top-right of the livestream to indicate to the user that this is not the true stream. If the icon persists, it means your underlying stream is not actually loading and may be misconfigured / broken.

### `Forbidden media source identifier`

Expand Down
38 changes: 22 additions & 16 deletions src/components/live/live-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit
import { customElement, property } from 'lit/decorators.js';
import { createRef, ref, Ref } from 'lit/directives/ref.js';
import { CameraConfig } from '../../config/types';
import basicBlockStyle from '../../scss/basic-block.scss';
import liveImageStyle from '../../scss/live-image.scss';
import { FrigateCardMediaPlayer } from '../../types.js';
import '../image.js';
import { getStateObjOrDispatchError } from '../../utils/get-state-obj';
Expand All @@ -16,6 +16,9 @@ export class FrigateCardLiveImage extends LitElement implements FrigateCardMedia
@property({ attribute: false })
public cameraConfig?: CameraConfig;

@property({ attribute: true })
public watermark?: string;

protected _refImage: Ref<Element & FrigateCardMediaPlayer> = createRef();

public async play(): Promise<void> {
Expand Down Expand Up @@ -61,24 +64,27 @@ export class FrigateCardLiveImage extends LitElement implements FrigateCardMedia

getStateObjOrDispatchError(this, this.hass, this.cameraConfig);

return html` <frigate-card-image
${ref(this._refImage)}
.imageConfig=${{
mode: this.cameraConfig.image.url ? ('url' as const) : ('camera' as const),
refresh_seconds: this.cameraConfig.image.refresh_seconds,
url: this.cameraConfig.image.url,
// The live provider will take care of zoom and layout options.
zoomable: false,
}}
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
>
</frigate-card-image>`;
return html`
<frigate-card-image
${ref(this._refImage)}
.imageConfig=${{
mode: this.cameraConfig.image.url ? ('url' as const) : ('camera' as const),
refresh_seconds: this.cameraConfig.image.refresh_seconds,
url: this.cameraConfig.image.url,
// The live provider will take care of zoom and layout options.
zoomable: false,
}}
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
>
</frigate-card-image>
${this.watermark ? html`<ha-icon icon="${this.watermark}"></ha-icon>` : ''}
`;
}

static get styles(): CSSResultGroup {
return unsafeCSS(basicBlockStyle);
return unsafeCSS(liveImageStyle);
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/components/live/live.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import {
} from '../../utils/media-info.js';
import { updateElementStyleFromMediaLayoutConfig } from '../../utils/media-layout.js';
import { playMediaMutingIfNecessary } from '../../utils/media.js';
import { Timer } from '../../utils/timer.js';
import { dispatchViewContextChangeEvent, View } from '../../view/view.js';
import { EmblaCarouselPlugins } from '../carousel.js';
import { renderMessage } from '../message.js';
Expand All @@ -61,7 +60,6 @@ import '../title-control.js';
import {
FrigateCardTitleControl,
getDefaultTitleConfigForView,
showTitleControlAfterDelay,
} from '../title-control.js';
import { getStateObjOrDispatchError } from '../../utils/get-state-obj.js';

Expand Down Expand Up @@ -388,7 +386,6 @@ export class FrigateCardLiveCarousel extends LitElement {

// Index between camera name and slide number.
protected _cameraToSlide: Record<string, number> = {};
protected _titleTimer = new Timer();
protected _refTitleControl: Ref<FrigateCardTitleControl> = createRef();

protected _getTransitionEffect(): TransitionEffect {
Expand Down Expand Up @@ -657,7 +654,7 @@ export class FrigateCardLiveCarousel extends LitElement {
}}
@frigate-card:media:loaded=${() => {
if (this._refTitleControl.value) {
showTitleControlAfterDelay(this._refTitleControl.value, this._titleTimer);
this._refTitleControl.value.show();
}
}}
>
Expand Down Expand Up @@ -698,7 +695,7 @@ export class FrigateCardLiveCarousel extends LitElement {
.text="${cameraMetadataCurrent
? `${localize('common.live')}: ${cameraMetadataCurrent.title}`
: ''}"
.logo="${cameraMetadataCurrent?.engineLogo}"
.logo="${cameraMetadataCurrent.engineLogo}"
.fitInto=${this as HTMLElement}
>
</frigate-card-title-control> `
Expand Down Expand Up @@ -965,6 +962,9 @@ export class FrigateCardLiveProvider
${ref(this._refProvider)}
.hass=${this.hass}
.cameraConfig=${this.cameraConfig}
watermark=${ifDefined(
showImageDuringLoading ? 'mdi:progress-helper' : undefined,
)}
@frigate-card:media:loaded=${(ev: Event) => {
if (provider === 'image') {
// Only count the media has loaded if the required provider is
Expand Down
24 changes: 0 additions & 24 deletions src/components/title-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,12 @@ import { customElement, property } from 'lit/decorators.js';
import { createRef, Ref, ref } from 'lit/directives/ref.js';
import { TitleControlConfig } from '../config/types';
import titleStyle from '../scss/title-control.scss';
import { Timer } from '../utils/timer';
import { View } from '../view/view.js';

type PaperToast = HTMLElement & {
opened: boolean;
};

export const showTitleControlAfterDelay = (
control: FrigateCardTitleControl,
timer: Timer,
delay = 0.5,
): void => {
const show = () => {
timer.stop();
control.show();
};

if (control.isVisible()) {
// If it's already visible, update it immediately (but also update it
// after the timer expires to ensure it re-positions if necessary, see
// comment below).
show();
}

// Allow a brief pause after the media loads, but before the title is
// displayed. This allows for a pleasant appearance/disappear of the title,
// and allows for the browser to finish rendering the carousel.
timer.start(delay, show);
};

export const getDefaultTitleConfigForView = (
view?: Readonly<View>,
baseConfig?: TitleControlConfig,
Expand Down
5 changes: 1 addition & 4 deletions src/components/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ import {
setControlsOnVideo,
} from '../utils/media.js';
import { screenshotMedia } from '../utils/screenshot.js';
import { Timer } from '../utils/timer';
import { ViewMediaClassifier } from '../view/media-classifier';
import { MediaQueriesClassifier } from '../view/media-queries-classifier';
import { MediaQueriesResults } from '../view/media-queries-results.js';
Expand All @@ -79,7 +78,6 @@ import './title-control.js';
import {
FrigateCardTitleControl,
getDefaultTitleConfigForView,
showTitleControlAfterDelay,
} from './title-control.js';

export interface MediaViewerViewContext {
Expand Down Expand Up @@ -227,7 +225,6 @@ export class FrigateCardViewerCarousel extends LitElement {
protected _selected = 0;

protected _media: ViewMedia[] | null = null;
protected _titleTimer = new Timer();
protected _refTitleControl: Ref<FrigateCardTitleControl> = createRef();
protected _player: FrigateCardMediaPlayer | null = null;

Expand Down Expand Up @@ -466,7 +463,7 @@ export class FrigateCardViewerCarousel extends LitElement {
}}
@frigate-card:media:loaded=${(ev: CustomEvent<MediaLoadedInfo>) => {
if (this._refTitleControl.value) {
showTitleControlAfterDelay(this._refTitleControl.value, this._titleTimer);
this._refTitleControl.value.show();
}
this._player = ev.detail.player ?? null;
this._seekHandler();
Expand Down
14 changes: 14 additions & 0 deletions src/scss/live-image.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
:host {
width: 100%;
height: 100%;
display: block;
position: relative;
}

ha-icon {
position: absolute;
top: 10px;
right: 10px;
opacity: 50%;
color: white;
}
15 changes: 13 additions & 2 deletions src/scss/title-control.scss
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
:host {
--paper-toast-background-color: rgba(0,0,0,0.6);
--paper-toast-background-color: rgba(0, 0, 0, 0.6);
--paper-toast-color: white;

pointer-events: none;
position: relative;
}

paper-toast {
max-width: unset;
min-width: unset;
display: flex;
align-items: center;

// Without this the paper-toast will consume vertical space before being
// opened, which causes the card to render blank space needlessly. It also
// won't work with 'display: none', it appears to need something with
// width/height properties even before being opened.
position: absolute;
}

paper-toast.paper-toast-open {
position: relative;
}

paper-toast img {
max-height: 24px;
padding-left: 10px;
}
}

0 comments on commit de7d412

Please sign in to comment.