fix: go2rtc live provider should work with non-proxied absolute URLs (#2724)
- Closes: #2719
This commit is contained in:
@@ -43,7 +43,10 @@ export class AdvancedCameraCardGo2RTC extends LitElement implements MediaPlayer
|
|||||||
@property({ attribute: true, type: Boolean })
|
@property({ attribute: true, type: Boolean })
|
||||||
public controls = false;
|
public controls = false;
|
||||||
|
|
||||||
|
// The player and the URL it is built for.
|
||||||
private _player?: VideoRTC;
|
private _player?: VideoRTC;
|
||||||
|
private _playerSource: string | null = null;
|
||||||
|
|
||||||
private _hasLiveError = false;
|
private _hasLiveError = false;
|
||||||
|
|
||||||
private _mediaPlayerController = new VideoMediaPlayerController(
|
private _mediaPlayerController = new VideoMediaPlayerController(
|
||||||
@@ -52,22 +55,18 @@ export class AdvancedCameraCardGo2RTC extends LitElement implements MediaPlayer
|
|||||||
() => this.controls,
|
() => this.controls,
|
||||||
);
|
);
|
||||||
|
|
||||||
private _signedURLController = new SignedURLController(
|
private _signedURLController = new SignedURLController(this, () => {
|
||||||
this,
|
const endpoint = this.camera?.getEndpoints()?.go2rtc;
|
||||||
() => {
|
if (!this.hass || !endpoint) {
|
||||||
const endpoint = this.camera?.getEndpoints()?.go2rtc;
|
return {};
|
||||||
if (!this.hass || !endpoint) {
|
}
|
||||||
return {};
|
return {
|
||||||
}
|
hass: this.hass,
|
||||||
return {
|
endpoint,
|
||||||
hass: this.hass,
|
proxyConfig: this.camera?.getLiveProxyConfig(),
|
||||||
endpoint,
|
proxyEndpointOptions: { websocket: true },
|
||||||
proxyConfig: this.camera?.getLiveProxyConfig(),
|
};
|
||||||
proxyEndpointOptions: { websocket: true },
|
});
|
||||||
};
|
|
||||||
},
|
|
||||||
() => this._createPlayer(),
|
|
||||||
);
|
|
||||||
|
|
||||||
public async getMediaPlayerController(): Promise<MediaPlayerController | null> {
|
public async getMediaPlayerController(): Promise<MediaPlayerController | null> {
|
||||||
return this._mediaPlayerController;
|
return this._mediaPlayerController;
|
||||||
@@ -92,15 +91,12 @@ export class AdvancedCameraCardGo2RTC extends LitElement implements MediaPlayer
|
|||||||
// DISCONNECT_TIMEOUT.
|
// DISCONNECT_TIMEOUT.
|
||||||
this._player?.reset();
|
this._player?.reset();
|
||||||
this._player = undefined;
|
this._player = undefined;
|
||||||
|
this._playerSource = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createPlayer(): void {
|
private _createPlayer(src: string): void {
|
||||||
const src = this._signedURLController.getValue();
|
|
||||||
if (!src) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._player = new VideoRTC();
|
this._player = new VideoRTC();
|
||||||
|
this._playerSource = src;
|
||||||
this._player.targetID = this.targetID ?? null;
|
this._player.targetID = this.targetID ?? null;
|
||||||
this._player.mediaPlayerController = this._mediaPlayerController;
|
this._player.mediaPlayerController = this._mediaPlayerController;
|
||||||
this._player.src = src;
|
this._player.src = src;
|
||||||
@@ -111,14 +107,12 @@ export class AdvancedCameraCardGo2RTC extends LitElement implements MediaPlayer
|
|||||||
if (cameraConfig?.go2rtc?.modes && cameraConfig.go2rtc.modes.length) {
|
if (cameraConfig?.go2rtc?.modes && cameraConfig.go2rtc.modes.length) {
|
||||||
this._player.mode = cameraConfig.go2rtc.modes.join(',');
|
this._player.mode = cameraConfig.go2rtc.modes.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected willUpdate(changedProps: PropertyValues): void {
|
protected willUpdate(changedProps: PropertyValues): void {
|
||||||
if (changedProps.has('camera')) {
|
if (changedProps.has('camera')) {
|
||||||
// Clear old player; the new one is created by the
|
// Stop streaming the old camera immediately; `update` builds the new
|
||||||
// SignedURLController's valueChangeCallback once the URL resolves.
|
// player as soon as that camera's URL is known.
|
||||||
this._destroyPlayer();
|
this._destroyPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +131,21 @@ export class AdvancedCameraCardGo2RTC extends LitElement implements MediaPlayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The signed URL controller announces only the URLs it resolves
|
||||||
|
// asynchronously, so the player is built from whatever URL it currently
|
||||||
|
// holds. Lit runs that controller's own update between `willUpdate` and
|
||||||
|
// `update`, so this overrides the latter.
|
||||||
|
protected update(changedProps: PropertyValues): void {
|
||||||
|
const src = this._signedURLController.getValue();
|
||||||
|
if (src !== this._playerSource) {
|
||||||
|
this._destroyPlayer();
|
||||||
|
if (src) {
|
||||||
|
this._createPlayer(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.update(changedProps);
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
const error = this._signedURLController.getError();
|
const error = this._signedURLController.getError();
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import type { VideoRTC } from '../../../../src/components/live/providers/go2rtc/video-rtc';
|
||||||
|
import type { RawAdvancedCameraCardConfig } from '../../../../src/config/types';
|
||||||
|
import { MountedCardFactory, type MountedCard } from '../../../browser/mounted-card';
|
||||||
|
import { CAMERA_ENTITY, createGenericCameraHASS } from '../../../browser/test-utils';
|
||||||
|
|
||||||
|
const PLAYER_SELECTOR = 'advanced-camera-card-live-go2rtc-player';
|
||||||
|
|
||||||
|
const mount = async (camera: RawAdvancedCameraCardConfig): Promise<MountedCard> =>
|
||||||
|
await MountedCardFactory.createFromSource(
|
||||||
|
{
|
||||||
|
type: 'custom:advanced-camera-card',
|
||||||
|
cameras: [{ camera_entity: CAMERA_ENTITY, live_provider: 'go2rtc', ...camera }],
|
||||||
|
performance: { features: { card_loading_indicator: false } },
|
||||||
|
},
|
||||||
|
createGenericCameraHASS(),
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('AdvancedCameraCardGo2RTC', () => {
|
||||||
|
it('should play a go2rtc server that is reached without signing or proxying', async () => {
|
||||||
|
// An absolute URL is not Home Assistant's to sign, and proxying is off, so
|
||||||
|
// the stream URL needs no resolving at all.
|
||||||
|
const card = await mount({
|
||||||
|
go2rtc: { url: 'http://localhost:1984', stream: 'office' },
|
||||||
|
proxy: { live: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const player = await card.waitForSelector<VideoRTC>(PLAYER_SELECTOR);
|
||||||
|
expect(player.wsURL).toBe('ws://localhost:1984/api/ws?src=office');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user