mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-31 13:07:49 +00:00
Show when media is loaded
This commit is contained in:
parent
3db626a07b
commit
685952c192
@ -5,43 +5,43 @@ import {
|
||||
SUPPORT_PAUSE,
|
||||
SUPPORT_PLAY,
|
||||
} from "../../data/media-player";
|
||||
import { resolveMediaSource } from "../../data/media_source";
|
||||
import { ResolvedMediaSource } from "../../data/media_source";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
export class BrowserMediaPlayer {
|
||||
private player?: HTMLAudioElement;
|
||||
private player: HTMLAudioElement;
|
||||
|
||||
private stopped = false;
|
||||
// We pretend we're playing while still buffering.
|
||||
private _buffering = true;
|
||||
|
||||
private _removed = false;
|
||||
|
||||
constructor(
|
||||
public hass: HomeAssistant,
|
||||
public item: MediaPlayerItem,
|
||||
public resolved: ResolvedMediaSource,
|
||||
private onChange: () => void
|
||||
) {}
|
||||
|
||||
public async initialize() {
|
||||
const resolvedUrl: any = await resolveMediaSource(
|
||||
this.hass,
|
||||
this.item.media_content_id
|
||||
);
|
||||
|
||||
const player = new Audio(resolvedUrl.url);
|
||||
) {
|
||||
const player = new Audio(this.resolved.url);
|
||||
player.addEventListener("play", this._handleChange);
|
||||
player.addEventListener("playing", this._handleChange);
|
||||
player.addEventListener("playing", () => {
|
||||
this._buffering = false;
|
||||
this._handleChange();
|
||||
});
|
||||
player.addEventListener("pause", this._handleChange);
|
||||
player.addEventListener("ended", this._handleChange);
|
||||
player.addEventListener("canplaythrough", () => {
|
||||
if (this.stopped) {
|
||||
if (this._removed) {
|
||||
return;
|
||||
}
|
||||
this.player = player;
|
||||
player.play();
|
||||
this.onChange();
|
||||
});
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
private _handleChange = () => {
|
||||
if (!this.stopped) {
|
||||
if (!this._removed) {
|
||||
this.onChange();
|
||||
}
|
||||
};
|
||||
@ -58,8 +58,8 @@ export class BrowserMediaPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.stopped = true;
|
||||
public remove() {
|
||||
this._removed = true;
|
||||
// @ts-ignore
|
||||
this.onChange = undefined;
|
||||
if (this.player) {
|
||||
@ -68,9 +68,7 @@ export class BrowserMediaPlayer {
|
||||
}
|
||||
|
||||
public get isPlaying(): boolean {
|
||||
return (
|
||||
this.player !== undefined && !this.player.paused && !this.player.ended
|
||||
);
|
||||
return this._buffering || (!this.player.paused && !this.player.ended);
|
||||
}
|
||||
|
||||
static idleStateObj(): MediaPlayerEntity {
|
||||
@ -88,19 +86,19 @@ export class BrowserMediaPlayer {
|
||||
toStateObj(): MediaPlayerEntity {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
|
||||
const base = BrowserMediaPlayer.idleStateObj();
|
||||
if (!this.player) {
|
||||
return base;
|
||||
}
|
||||
base.state = this.isPlaying ? "playing" : "paused";
|
||||
base.attributes = {
|
||||
media_title: this.item.title,
|
||||
media_duration: this.player.duration,
|
||||
media_position: this.player.currentTime,
|
||||
media_position_updated_at: base.last_updated,
|
||||
entity_picture: this.item.thumbnail,
|
||||
// eslint-disable-next-line no-bitwise
|
||||
supported_features: SUPPORT_PLAY | SUPPORT_PAUSE,
|
||||
};
|
||||
|
||||
if (this.player.duration) {
|
||||
base.attributes.media_duration = this.player.duration;
|
||||
base.attributes.media_position = this.player.currentTime;
|
||||
base.attributes.media_position_updated_at = base.last_updated;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
} from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { until } from "lit/directives/until";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||
@ -27,6 +28,7 @@ import { computeStateName } from "../../common/entity/compute_state_name";
|
||||
import { domainIcon } from "../../common/entity/domain_icon";
|
||||
import { supportsFeature } from "../../common/entity/supports-feature";
|
||||
import "../../components/ha-button-menu";
|
||||
import "../../components/ha-circular-progress";
|
||||
import "../../components/ha-icon-button";
|
||||
import { UNAVAILABLE_STATES } from "../../data/entity";
|
||||
import {
|
||||
@ -43,6 +45,7 @@ import {
|
||||
SUPPORT_PLAY,
|
||||
SUPPORT_STOP,
|
||||
} from "../../data/media-player";
|
||||
import { ResolvedMediaSource } from "../../data/media_source";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../lovelace/components/hui-marquee";
|
||||
import { BrowserMediaPlayer } from "./browser-media-player";
|
||||
@ -54,7 +57,7 @@ declare global {
|
||||
}
|
||||
|
||||
@customElement("ha-bar-media-player")
|
||||
class BarMediaPlayer extends LitElement {
|
||||
export class BarMediaPlayer extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public entityId!: string;
|
||||
@ -68,6 +71,8 @@ class BarMediaPlayer extends LitElement {
|
||||
|
||||
@state() private _marqueeActive = false;
|
||||
|
||||
@state() private _newMediaExpected = false;
|
||||
|
||||
@state() private _browserPlayer?: BrowserMediaPlayer;
|
||||
|
||||
private _progressInterval?: number;
|
||||
@ -98,27 +103,42 @@ class BarMediaPlayer extends LitElement {
|
||||
clearInterval(this._progressInterval);
|
||||
this._progressInterval = undefined;
|
||||
}
|
||||
|
||||
if (this._browserPlayer) {
|
||||
this._browserPlayer.stop();
|
||||
this._browserPlayer = undefined;
|
||||
}
|
||||
this._tearDownBrowserPlayer();
|
||||
}
|
||||
|
||||
public async playItem(item: MediaPlayerItem) {
|
||||
public showResolvingNewMediaPicked() {
|
||||
this._tearDownBrowserPlayer();
|
||||
this._newMediaExpected = true;
|
||||
}
|
||||
|
||||
public playItem(item: MediaPlayerItem, resolved: ResolvedMediaSource) {
|
||||
if (this.entityId !== BROWSER_PLAYER) {
|
||||
throw Error("Only browser supported");
|
||||
}
|
||||
if (this._browserPlayer) {
|
||||
this._browserPlayer.stop();
|
||||
}
|
||||
this._browserPlayer = new BrowserMediaPlayer(this.hass, item, () =>
|
||||
this.requestUpdate("_browserPlayer")
|
||||
this._tearDownBrowserPlayer();
|
||||
this._browserPlayer = new BrowserMediaPlayer(
|
||||
this.hass,
|
||||
item,
|
||||
resolved,
|
||||
() => this.requestUpdate("_browserPlayer")
|
||||
);
|
||||
await this._browserPlayer.initialize();
|
||||
this._newMediaExpected = false;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this._newMediaExpected) {
|
||||
return html`
|
||||
<div class="controls-progress">
|
||||
${until(
|
||||
// Only show spinner after 500ms
|
||||
new Promise((resolve) => setTimeout(resolve, 500)).then(
|
||||
() => html`<ha-circular-progress active></ha-circular-progress>`
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const isBrowser = this.entityId === BROWSER_PLAYER;
|
||||
const stateObj = this._stateObj;
|
||||
const controls = !stateObj
|
||||
@ -274,13 +294,19 @@ class BarMediaPlayer extends LitElement {
|
||||
|
||||
public willUpdate(changedProps: PropertyValues) {
|
||||
super.willUpdate(changedProps);
|
||||
if (changedProps.has("entityId")) {
|
||||
this._tearDownBrowserPlayer();
|
||||
}
|
||||
if (!changedProps.has("hass") || this.entityId === BROWSER_PLAYER) {
|
||||
return;
|
||||
}
|
||||
// Reset new media expected if media player state changes
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
if (
|
||||
changedProps.has("entityId") &&
|
||||
this.entityId !== BROWSER_PLAYER &&
|
||||
this._browserPlayer
|
||||
!oldHass ||
|
||||
oldHass.states[this.entityId] !== this.hass.states[this.entityId]
|
||||
) {
|
||||
this._browserPlayer?.stop();
|
||||
this._browserPlayer = undefined;
|
||||
this._newMediaExpected = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,6 +355,13 @@ class BarMediaPlayer extends LitElement {
|
||||
return this.hass!.states[this.entityId] as MediaPlayerEntity | undefined;
|
||||
}
|
||||
|
||||
private _tearDownBrowserPlayer() {
|
||||
if (this._browserPlayer) {
|
||||
this._browserPlayer.remove();
|
||||
this._browserPlayer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _openMoreInfo() {
|
||||
if (this._browserPlayer) {
|
||||
return;
|
||||
|
@ -37,6 +37,7 @@ import "../../layouts/ha-app-layout";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../types";
|
||||
import "./ha-bar-media-player";
|
||||
import type { BarMediaPlayer } from "./ha-bar-media-player";
|
||||
import { showWebBrowserPlayMediaDialog } from "./show-media-player-dialog";
|
||||
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||
import {
|
||||
@ -79,6 +80,8 @@ class PanelMediaBrowser extends LitElement {
|
||||
|
||||
@query("ha-media-player-browse") private _browser!: HaMediaPlayerBrowse;
|
||||
|
||||
@query("ha-bar-media-player") private _player!: BarMediaPlayer;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-app-layout>
|
||||
@ -235,7 +238,9 @@ class PanelMediaBrowser extends LitElement {
|
||||
ev: HASSDomEvent<MediaPickedEvent>
|
||||
): Promise<void> {
|
||||
const item = ev.detail.item;
|
||||
|
||||
if (this._entityId !== BROWSER_PLAYER) {
|
||||
this._player.showResolvingNewMediaPicked();
|
||||
this.hass!.callService("media_player", "play_media", {
|
||||
entity_id: this._entityId,
|
||||
media_content_id: item.media_content_id,
|
||||
@ -244,6 +249,8 @@ class PanelMediaBrowser extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
// We won't cancel current media being played if we're going to
|
||||
// open a camera.
|
||||
if (isCameraMediaSource(item.media_content_id)) {
|
||||
fireEvent(this, "hass-more-info", {
|
||||
entityId: getEntityIdFromCameraMediaSource(item.media_content_id),
|
||||
@ -251,15 +258,15 @@ class PanelMediaBrowser extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
this._player.showResolvingNewMediaPicked();
|
||||
|
||||
const resolvedUrl = await resolveMediaSource(
|
||||
this.hass,
|
||||
item.media_content_id
|
||||
);
|
||||
|
||||
if (resolvedUrl.mime_type.startsWith("audio/")) {
|
||||
await this.shadowRoot!.querySelector("ha-bar-media-player")!.playItem(
|
||||
item
|
||||
);
|
||||
this._player.playItem(item, resolvedUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user