mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 11:16:35 +00:00
Show when media is being loaded (#11750)
This commit is contained in:
parent
9b4c6eea63
commit
28cd9b6408
@ -33,7 +33,8 @@ import type { HomeAssistant } from "../types";
|
|||||||
import { UNAVAILABLE_STATES } from "./entity";
|
import { UNAVAILABLE_STATES } from "./entity";
|
||||||
|
|
||||||
interface MediaPlayerEntityAttributes extends HassEntityAttributeBase {
|
interface MediaPlayerEntityAttributes extends HassEntityAttributeBase {
|
||||||
media_content_type?: any;
|
media_content_id?: string;
|
||||||
|
media_content_type?: string;
|
||||||
media_artist?: string;
|
media_artist?: string;
|
||||||
media_playlist?: string;
|
media_playlist?: string;
|
||||||
media_series_title?: string;
|
media_series_title?: string;
|
||||||
|
@ -5,61 +5,60 @@ import {
|
|||||||
SUPPORT_PAUSE,
|
SUPPORT_PAUSE,
|
||||||
SUPPORT_PLAY,
|
SUPPORT_PLAY,
|
||||||
} from "../../data/media-player";
|
} from "../../data/media-player";
|
||||||
import { resolveMediaSource } from "../../data/media_source";
|
import { ResolvedMediaSource } from "../../data/media_source";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
export class BrowserMediaPlayer {
|
export class BrowserMediaPlayer {
|
||||||
private player?: HTMLAudioElement;
|
private player: HTMLAudioElement;
|
||||||
|
|
||||||
private stopped = false;
|
// We pretend we're playing while still buffering.
|
||||||
|
public buffering = true;
|
||||||
|
|
||||||
|
private _removed = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public hass: HomeAssistant,
|
public hass: HomeAssistant,
|
||||||
public item: MediaPlayerItem,
|
public item: MediaPlayerItem,
|
||||||
|
public resolved: ResolvedMediaSource,
|
||||||
private onChange: () => void
|
private onChange: () => void
|
||||||
) {}
|
) {
|
||||||
|
const player = new Audio(this.resolved.url);
|
||||||
public async initialize() {
|
|
||||||
const resolvedUrl: any = await resolveMediaSource(
|
|
||||||
this.hass,
|
|
||||||
this.item.media_content_id
|
|
||||||
);
|
|
||||||
|
|
||||||
const player = new Audio(resolvedUrl.url);
|
|
||||||
player.addEventListener("play", this._handleChange);
|
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("pause", this._handleChange);
|
||||||
player.addEventListener("ended", this._handleChange);
|
player.addEventListener("ended", this._handleChange);
|
||||||
player.addEventListener("canplaythrough", () => {
|
player.addEventListener("canplaythrough", () => {
|
||||||
if (this.stopped) {
|
if (this._removed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.player = player;
|
if (this.buffering) {
|
||||||
player.play();
|
player.play();
|
||||||
|
}
|
||||||
this.onChange();
|
this.onChange();
|
||||||
});
|
});
|
||||||
|
this.player = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleChange = () => {
|
private _handleChange = () => {
|
||||||
if (!this.stopped) {
|
if (!this._removed) {
|
||||||
this.onChange();
|
this.onChange();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public pause() {
|
public pause() {
|
||||||
if (this.player) {
|
this.buffering = false;
|
||||||
this.player.pause();
|
this.player.pause();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public play() {
|
public play() {
|
||||||
if (this.player) {
|
this.player.play();
|
||||||
this.player.play();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop() {
|
public remove() {
|
||||||
this.stopped = true;
|
this._removed = true;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.onChange = undefined;
|
this.onChange = undefined;
|
||||||
if (this.player) {
|
if (this.player) {
|
||||||
@ -68,9 +67,7 @@ export class BrowserMediaPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get isPlaying(): boolean {
|
public get isPlaying(): boolean {
|
||||||
return (
|
return this.buffering || (!this.player.paused && !this.player.ended);
|
||||||
this.player !== undefined && !this.player.paused && !this.player.ended
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static idleStateObj(): MediaPlayerEntity {
|
static idleStateObj(): MediaPlayerEntity {
|
||||||
@ -88,19 +85,19 @@ export class BrowserMediaPlayer {
|
|||||||
toStateObj(): MediaPlayerEntity {
|
toStateObj(): MediaPlayerEntity {
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
|
||||||
const base = BrowserMediaPlayer.idleStateObj();
|
const base = BrowserMediaPlayer.idleStateObj();
|
||||||
if (!this.player) {
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
base.state = this.isPlaying ? "playing" : "paused";
|
base.state = this.isPlaying ? "playing" : "paused";
|
||||||
base.attributes = {
|
base.attributes = {
|
||||||
media_title: this.item.title,
|
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,
|
entity_picture: this.item.thumbnail,
|
||||||
// eslint-disable-next-line no-bitwise
|
// eslint-disable-next-line no-bitwise
|
||||||
supported_features: SUPPORT_PLAY | SUPPORT_PAUSE,
|
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;
|
return base;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { until } from "lit/directives/until";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { computeStateDomain } from "../../common/entity/compute_state_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 { domainIcon } from "../../common/entity/domain_icon";
|
||||||
import { supportsFeature } from "../../common/entity/supports-feature";
|
import { supportsFeature } from "../../common/entity/supports-feature";
|
||||||
import "../../components/ha-button-menu";
|
import "../../components/ha-button-menu";
|
||||||
|
import "../../components/ha-circular-progress";
|
||||||
import "../../components/ha-icon-button";
|
import "../../components/ha-icon-button";
|
||||||
import { UNAVAILABLE_STATES } from "../../data/entity";
|
import { UNAVAILABLE_STATES } from "../../data/entity";
|
||||||
import {
|
import {
|
||||||
@ -43,6 +45,7 @@ import {
|
|||||||
SUPPORT_PLAY,
|
SUPPORT_PLAY,
|
||||||
SUPPORT_STOP,
|
SUPPORT_STOP,
|
||||||
} from "../../data/media-player";
|
} from "../../data/media-player";
|
||||||
|
import { ResolvedMediaSource } from "../../data/media_source";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "../lovelace/components/hui-marquee";
|
import "../lovelace/components/hui-marquee";
|
||||||
import { BrowserMediaPlayer } from "./browser-media-player";
|
import { BrowserMediaPlayer } from "./browser-media-player";
|
||||||
@ -54,7 +57,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-bar-media-player")
|
@customElement("ha-bar-media-player")
|
||||||
class BarMediaPlayer extends LitElement {
|
export class BarMediaPlayer extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public entityId!: string;
|
@property({ attribute: false }) public entityId!: string;
|
||||||
@ -68,6 +71,8 @@ class BarMediaPlayer extends LitElement {
|
|||||||
|
|
||||||
@state() private _marqueeActive = false;
|
@state() private _marqueeActive = false;
|
||||||
|
|
||||||
|
@state() private _newMediaExpected = false;
|
||||||
|
|
||||||
@state() private _browserPlayer?: BrowserMediaPlayer;
|
@state() private _browserPlayer?: BrowserMediaPlayer;
|
||||||
|
|
||||||
private _progressInterval?: number;
|
private _progressInterval?: number;
|
||||||
@ -98,32 +103,54 @@ class BarMediaPlayer extends LitElement {
|
|||||||
clearInterval(this._progressInterval);
|
clearInterval(this._progressInterval);
|
||||||
this._progressInterval = undefined;
|
this._progressInterval = undefined;
|
||||||
}
|
}
|
||||||
|
this._tearDownBrowserPlayer();
|
||||||
if (this._browserPlayer) {
|
|
||||||
this._browserPlayer.stop();
|
|
||||||
this._browserPlayer = undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async playItem(item: MediaPlayerItem) {
|
public showResolvingNewMediaPicked() {
|
||||||
|
this._tearDownBrowserPlayer();
|
||||||
|
this._newMediaExpected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public hideResolvingNewMediaPicked() {
|
||||||
|
this._newMediaExpected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public playItem(item: MediaPlayerItem, resolved: ResolvedMediaSource) {
|
||||||
if (this.entityId !== BROWSER_PLAYER) {
|
if (this.entityId !== BROWSER_PLAYER) {
|
||||||
throw Error("Only browser supported");
|
throw Error("Only browser supported");
|
||||||
}
|
}
|
||||||
if (this._browserPlayer) {
|
this._tearDownBrowserPlayer();
|
||||||
this._browserPlayer.stop();
|
this._browserPlayer = new BrowserMediaPlayer(
|
||||||
}
|
this.hass,
|
||||||
this._browserPlayer = new BrowserMediaPlayer(this.hass, item, () =>
|
item,
|
||||||
this.requestUpdate("_browserPlayer")
|
resolved,
|
||||||
|
() => this.requestUpdate("_browserPlayer")
|
||||||
);
|
);
|
||||||
await this._browserPlayer.initialize();
|
this._newMediaExpected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
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 isBrowser = this.entityId === BROWSER_PLAYER;
|
||||||
const stateObj = this._stateObj;
|
const stateObj = this._stateObj;
|
||||||
const controls = !stateObj
|
|
||||||
? undefined
|
if (!stateObj) {
|
||||||
: !this.narrow
|
return this._renderChoosePlayer(stateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
const controls = !this.narrow
|
||||||
? computeMediaControls(stateObj)
|
? computeMediaControls(stateObj)
|
||||||
: (stateObj.state === "playing" &&
|
: (stateObj.state === "playing" &&
|
||||||
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
|
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
|
||||||
@ -152,16 +179,14 @@ class BarMediaPlayer extends LitElement {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [{}];
|
: [{}];
|
||||||
const mediaDescription = stateObj ? computeMediaDescription(stateObj) : "";
|
const mediaDescription = computeMediaDescription(stateObj);
|
||||||
const mediaDuration = formatMediaTime(stateObj?.attributes.media_duration);
|
const mediaDuration = formatMediaTime(stateObj.attributes.media_duration);
|
||||||
const mediaTitleClean = cleanupMediaTitle(
|
const mediaTitleClean = cleanupMediaTitle(
|
||||||
stateObj?.attributes.media_title || ""
|
stateObj.attributes.media_title || ""
|
||||||
);
|
);
|
||||||
|
const mediaArt =
|
||||||
const mediaArt = stateObj
|
stateObj.attributes.entity_picture_local ||
|
||||||
? stateObj.attributes.entity_picture_local ||
|
stateObj.attributes.entity_picture;
|
||||||
stateObj.attributes.entity_picture
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
@ -177,7 +202,10 @@ class BarMediaPlayer extends LitElement {
|
|||||||
<hui-marquee
|
<hui-marquee
|
||||||
.text=${mediaTitleClean ||
|
.text=${mediaTitleClean ||
|
||||||
mediaDescription ||
|
mediaDescription ||
|
||||||
this.hass.localize(`ui.card.media_player.nothing_playing`)}
|
cleanupMediaTitle(stateObj.attributes.media_content_id) ||
|
||||||
|
(stateObj.state !== "playing" && stateObj.state !== "on"
|
||||||
|
? this.hass.localize(`ui.card.media_player.nothing_playing`)
|
||||||
|
: "")}
|
||||||
.active=${this._marqueeActive}
|
.active=${this._marqueeActive}
|
||||||
@mouseover=${this._marqueeMouseOver}
|
@mouseover=${this._marqueeMouseOver}
|
||||||
@mouseleave=${this._marqueeMouseLeave}
|
@mouseleave=${this._marqueeMouseLeave}
|
||||||
@ -188,99 +216,124 @@ class BarMediaPlayer extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="controls-progress">
|
<div class="controls-progress">
|
||||||
<div class="controls">
|
${this._browserPlayer?.buffering
|
||||||
${controls === undefined
|
? html` <ha-circular-progress active></ha-circular-progress> `
|
||||||
? ""
|
|
||||||
: controls.map(
|
|
||||||
(control) => html`
|
|
||||||
<ha-icon-button
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
`ui.card.media_player.${control.action}`
|
|
||||||
)}
|
|
||||||
.path=${control.icon}
|
|
||||||
action=${control.action}
|
|
||||||
@click=${this._handleClick}
|
|
||||||
>
|
|
||||||
</ha-icon-button>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
${stateObj?.attributes.media_duration === Infinity
|
|
||||||
? html``
|
|
||||||
: this.narrow
|
|
||||||
? html`<mwc-linear-progress></mwc-linear-progress>`
|
|
||||||
: html`
|
: html`
|
||||||
<div class="progress">
|
<div class="controls">
|
||||||
<div id="CurrentProgress"></div>
|
${controls === undefined
|
||||||
<mwc-linear-progress wide></mwc-linear-progress>
|
? ""
|
||||||
<div>${mediaDuration}</div>
|
: controls.map(
|
||||||
|
(control) => html`
|
||||||
|
<ha-icon-button
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
`ui.card.media_player.${control.action}`
|
||||||
|
)}
|
||||||
|
.path=${control.icon}
|
||||||
|
action=${control.action}
|
||||||
|
@click=${this._handleClick}
|
||||||
|
>
|
||||||
|
</ha-icon-button>
|
||||||
|
`
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
${stateObj.attributes.media_duration === Infinity
|
||||||
|
? html``
|
||||||
|
: this.narrow
|
||||||
|
? html`<mwc-linear-progress></mwc-linear-progress>`
|
||||||
|
: html`
|
||||||
|
<div class="progress">
|
||||||
|
<div id="CurrentProgress"></div>
|
||||||
|
<mwc-linear-progress wide></mwc-linear-progress>
|
||||||
|
<div>${mediaDuration}</div>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
<div class="choose-player ${isBrowser ? "browser" : ""}">
|
${this._renderChoosePlayer(stateObj)}
|
||||||
<ha-button-menu corner="BOTTOM_START">
|
`;
|
||||||
${this.narrow
|
}
|
||||||
? html`
|
|
||||||
<ha-icon-button
|
private _renderChoosePlayer(stateObj: MediaPlayerEntity | undefined) {
|
||||||
slot="trigger"
|
const isBrowser = this.entityId === BROWSER_PLAYER;
|
||||||
.path=${isBrowser
|
return html`
|
||||||
? mdiMonitor
|
<div class="choose-player ${isBrowser ? "browser" : ""}">
|
||||||
: domainIcon(computeDomain(this.entityId), stateObj)}
|
<ha-button-menu corner="BOTTOM_START">
|
||||||
></ha-icon-button>
|
${
|
||||||
`
|
this.narrow
|
||||||
: html`
|
? html`
|
||||||
<mwc-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.narrow
|
.path=${isBrowser
|
||||||
? ""
|
? mdiMonitor
|
||||||
: `${stateObj ? computeStateName(stateObj) : this.entityId}
|
: domainIcon(computeDomain(this.entityId), stateObj)}
|
||||||
|
></ha-icon-button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<mwc-button
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.narrow
|
||||||
|
? ""
|
||||||
|
: `${
|
||||||
|
stateObj
|
||||||
|
? computeStateName(stateObj)
|
||||||
|
: this.entityId
|
||||||
|
}
|
||||||
`}
|
`}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="icon"
|
||||||
|
.path=${isBrowser
|
||||||
|
? mdiMonitor
|
||||||
|
: domainIcon(computeDomain(this.entityId), stateObj)}
|
||||||
|
></ha-svg-icon>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="trailingIcon"
|
||||||
|
.path=${mdiChevronDown}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
<mwc-list-item
|
||||||
|
.player=${BROWSER_PLAYER}
|
||||||
|
?selected=${isBrowser}
|
||||||
|
@click=${this._selectPlayer}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.components.media-browser.web-browser")}
|
||||||
|
</mwc-list-item>
|
||||||
|
${this._mediaPlayerEntities.map(
|
||||||
|
(source) => html`
|
||||||
|
<mwc-list-item
|
||||||
|
?selected=${source.entity_id === this.entityId}
|
||||||
|
.disabled=${UNAVAILABLE_STATES.includes(source.state)}
|
||||||
|
.player=${source.entity_id}
|
||||||
|
@click=${this._selectPlayer}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
${computeStateName(source)}
|
||||||
slot="icon"
|
</mwc-list-item>
|
||||||
.path=${isBrowser
|
`
|
||||||
? mdiMonitor
|
)}
|
||||||
: domainIcon(computeDomain(this.entityId), stateObj)}
|
</ha-button-menu>
|
||||||
></ha-svg-icon>
|
</div>
|
||||||
<ha-svg-icon
|
|
||||||
slot="trailingIcon"
|
|
||||||
.path=${mdiChevronDown}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</mwc-button>
|
|
||||||
`}
|
|
||||||
<mwc-list-item
|
|
||||||
.player=${BROWSER_PLAYER}
|
|
||||||
?selected=${isBrowser}
|
|
||||||
@click=${this._selectPlayer}
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.components.media-browser.web-browser")}
|
|
||||||
</mwc-list-item>
|
|
||||||
${this._mediaPlayerEntities.map(
|
|
||||||
(source) => html`
|
|
||||||
<mwc-list-item
|
|
||||||
?selected=${source.entity_id === this.entityId}
|
|
||||||
.disabled=${UNAVAILABLE_STATES.includes(source.state)}
|
|
||||||
.player=${source.entity_id}
|
|
||||||
@click=${this._selectPlayer}
|
|
||||||
>
|
|
||||||
${computeStateName(source)}
|
|
||||||
</mwc-list-item>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</ha-button-menu>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues) {
|
public willUpdate(changedProps: PropertyValues) {
|
||||||
super.willUpdate(changedProps);
|
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 (
|
if (
|
||||||
changedProps.has("entityId") &&
|
!oldHass ||
|
||||||
this.entityId !== BROWSER_PLAYER &&
|
oldHass.states[this.entityId] !== this.hass.states[this.entityId]
|
||||||
this._browserPlayer
|
|
||||||
) {
|
) {
|
||||||
this._browserPlayer?.stop();
|
this._newMediaExpected = false;
|
||||||
this._browserPlayer = undefined;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,6 +382,13 @@ class BarMediaPlayer extends LitElement {
|
|||||||
return this.hass!.states[this.entityId] as MediaPlayerEntity | undefined;
|
return this.hass!.states[this.entityId] as MediaPlayerEntity | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _tearDownBrowserPlayer() {
|
||||||
|
if (this._browserPlayer) {
|
||||||
|
this._browserPlayer.remove();
|
||||||
|
this._browserPlayer = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _openMoreInfo() {
|
private _openMoreInfo() {
|
||||||
if (this._browserPlayer) {
|
if (this._browserPlayer) {
|
||||||
return;
|
return;
|
||||||
|
@ -37,6 +37,7 @@ import "../../layouts/ha-app-layout";
|
|||||||
import { haStyle } from "../../resources/styles";
|
import { haStyle } from "../../resources/styles";
|
||||||
import type { HomeAssistant, Route } from "../../types";
|
import type { HomeAssistant, Route } from "../../types";
|
||||||
import "./ha-bar-media-player";
|
import "./ha-bar-media-player";
|
||||||
|
import type { BarMediaPlayer } from "./ha-bar-media-player";
|
||||||
import { showWebBrowserPlayMediaDialog } from "./show-media-player-dialog";
|
import { showWebBrowserPlayMediaDialog } from "./show-media-player-dialog";
|
||||||
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||||
import {
|
import {
|
||||||
@ -79,6 +80,8 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
|
|
||||||
@query("ha-media-player-browse") private _browser!: HaMediaPlayerBrowse;
|
@query("ha-media-player-browse") private _browser!: HaMediaPlayerBrowse;
|
||||||
|
|
||||||
|
@query("ha-bar-media-player") private _player!: BarMediaPlayer;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<ha-app-layout>
|
<ha-app-layout>
|
||||||
@ -235,15 +238,23 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
ev: HASSDomEvent<MediaPickedEvent>
|
ev: HASSDomEvent<MediaPickedEvent>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const item = ev.detail.item;
|
const item = ev.detail.item;
|
||||||
|
|
||||||
if (this._entityId !== BROWSER_PLAYER) {
|
if (this._entityId !== BROWSER_PLAYER) {
|
||||||
this.hass!.callService("media_player", "play_media", {
|
this._player.showResolvingNewMediaPicked();
|
||||||
entity_id: this._entityId,
|
try {
|
||||||
media_content_id: item.media_content_id,
|
await this.hass!.callService("media_player", "play_media", {
|
||||||
media_content_type: item.media_content_type,
|
entity_id: this._entityId,
|
||||||
});
|
media_content_id: item.media_content_id,
|
||||||
|
media_content_type: item.media_content_type,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
this._player.hideResolvingNewMediaPicked();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We won't cancel current media being played if we're going to
|
||||||
|
// open a camera.
|
||||||
if (isCameraMediaSource(item.media_content_id)) {
|
if (isCameraMediaSource(item.media_content_id)) {
|
||||||
fireEvent(this, "hass-more-info", {
|
fireEvent(this, "hass-more-info", {
|
||||||
entityId: getEntityIdFromCameraMediaSource(item.media_content_id),
|
entityId: getEntityIdFromCameraMediaSource(item.media_content_id),
|
||||||
@ -251,15 +262,15 @@ class PanelMediaBrowser extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._player.showResolvingNewMediaPicked();
|
||||||
|
|
||||||
const resolvedUrl = await resolveMediaSource(
|
const resolvedUrl = await resolveMediaSource(
|
||||||
this.hass,
|
this.hass,
|
||||||
item.media_content_id
|
item.media_content_id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (resolvedUrl.mime_type.startsWith("audio/")) {
|
if (resolvedUrl.mime_type.startsWith("audio/")) {
|
||||||
await this.shadowRoot!.querySelector("ha-bar-media-player")!.playItem(
|
this._player.playItem(item, resolvedUrl);
|
||||||
item
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user