Allow video playback in media_player.

This commit is contained in:
Thomas Lovén 2022-07-26 22:42:07 +00:00
parent df745e7f3b
commit 6e5a1c18a3
5 changed files with 154 additions and 45 deletions

View File

@ -619,13 +619,19 @@ const MediaPlayerMixin = (SuperClass) => {
return class MediaPlayerMixinClass extends SuperClass {
constructor() {
super();
this.player = new Audio();
this._audio_player = new Audio();
this._video_player = document.createElement("video");
this._video_player.controls = true;
this._video_player.style.setProperty("width", "100%");
this.player = this._audio_player;
this._player_enabled = false;
for (const ev of ["play", "pause", "ended", "volumechange"]) {
this.player.addEventListener(ev, () => this._player_update());
this._audio_player.addEventListener(ev, () => this._player_update());
this._video_player.addEventListener(ev, () => this._player_update());
}
for (const ev of ["timeupdate"]) {
this.player.addEventListener(ev, () => this._player_update_choked());
this._audio_player.addEventListener(ev, () => this._player_update_choked());
this._video_player.addEventListener(ev, () => this._player_update_choked());
}
this.firstInteraction.then(() => {
this._player_enabled = true;
@ -633,10 +639,18 @@ const MediaPlayerMixin = (SuperClass) => {
this.player.play();
});
this.addEventListener("command-player-play", (ev) => {
var _a;
if ((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.media_content_id)
var _a, _b, _c;
if (this.player.src)
this.player.pause();
if ((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.media_type)
if ((_b = ev.detail) === null || _b === void 0 ? void 0 : _b.media_type.startsWith("video"))
this.player = this._video_player;
else
this.player = this._audio_player;
if ((_c = ev.detail) === null || _c === void 0 ? void 0 : _c.media_content_id)
this.player.src = ev.detail.media_content_id;
this.player.play();
this._show_video_player();
});
this.addEventListener("command-player-pause", (ev) => this.player.pause());
this.addEventListener("command-player-stop", (ev) => {
@ -660,8 +674,30 @@ const MediaPlayerMixin = (SuperClass) => {
this.player.currentTime = ev.detail.position;
setTimeout(() => this._player_update(), 10);
});
this.addEventListener("command-player-turn-off", (ev) => {
if (this.player === this._video_player &&
this._video_player.isConnected)
this.closePopup();
else if (this.player.src)
this.player.pause();
this.player.src = "";
this._player_update();
});
this.connectionPromise.then(() => this._player_update());
}
_show_video_player() {
if (this.player === this._video_player && this.player.src) {
selectTree(document, "home-assistant $ dialog-media-player-browse").then((el) => el === null || el === void 0 ? void 0 : el.closeDialog());
this.showPopup(undefined, this._video_player, {
dismiss_action: () => this._video_player.pause(),
size: "wide",
});
}
else if (this.player !== this._video_player &&
this._video_player.isConnected) {
this.closePopup();
}
}
_player_update_choked() {
if (this._player_update_cooldown)
return;
@ -670,13 +706,13 @@ const MediaPlayerMixin = (SuperClass) => {
}
_player_update() {
const state = this._player_enabled
? this.player.src
? this.player.ended
? !this.player.src || this.player.src === window.location.href
? "off"
: this.player.ended
? "stopped"
: this.player.paused
? "paused"
: "playing"
: "stopped"
: "unavailable";
this.sendUpdate({
player: {
@ -1167,13 +1203,17 @@ class BrowserModPopup extends s {
}
this._autocloseListener = undefined;
if (this._autoclose) {
this._autocloseListener = this._dismiss.bind(this);
window.browser_mod.addEventListener("browser-mod-activity", this._autocloseListener);
this._autocloseListener = () => this.dialog.close();
window.browser_mod.addEventListener("browser-mod-activity", this._autocloseListener, { once: true });
}
}
async setupDialog(title, content, { right_button = undefined, right_button_action = undefined, left_button = undefined, left_button_action = undefined, dismissable = true, dismiss_action = undefined, timeout = undefined, timeout_action = undefined, size = undefined, style = undefined, autoclose = false, } = {}) {
this.title = title;
if (content && typeof content === "object") {
if (content && content instanceof HTMLElement) {
this.card = undefined;
this.content = content;
}
else if (content && typeof content === "object") {
// Create a card from config in content
this.card = true;
const helpers = await window.loadCardHelpers();
@ -1204,30 +1244,30 @@ class BrowserModPopup extends s {
this._autoclose = autoclose;
}
async _primary() {
var _a, _b, _c;
var _a, _b, _c, _d;
if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action)
this._actions.dismiss_action = undefined;
await this.closeDialog();
(_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.right_button_action) === null || _c === void 0 ? void 0 : _c.call(_b);
(_b = this.dialog) === null || _b === void 0 ? void 0 : _b.close();
(_d = (_c = this._actions) === null || _c === void 0 ? void 0 : _c.right_button_action) === null || _d === void 0 ? void 0 : _d.call(_c);
}
async _secondary() {
var _a, _b, _c;
var _a, _b, _c, _d;
if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action)
this._actions.dismiss_action = undefined;
await this.closeDialog();
(_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.left_button_action) === null || _c === void 0 ? void 0 : _c.call(_b);
(_b = this.dialog) === null || _b === void 0 ? void 0 : _b.close();
(_d = (_c = this._actions) === null || _c === void 0 ? void 0 : _c.left_button_action) === null || _d === void 0 ? void 0 : _d.call(_c);
}
async _dismiss() {
var _a, _b;
await this.closeDialog();
(_b = (_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) === null || _b === void 0 ? void 0 : _b.call(_a);
async _dismiss(ev) {
var _a, _b, _c;
(_a = this.dialog) === null || _a === void 0 ? void 0 : _a.close();
(_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.dismiss_action) === null || _c === void 0 ? void 0 : _c.call(_b);
}
async _timeout() {
var _a, _b, _c;
var _a, _b, _c, _d;
if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action)
this._actions.dismiss_action = undefined;
await this.closeDialog();
(_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.timeout_action) === null || _c === void 0 ? void 0 : _c.call(_b);
(_b = this.dialog) === null || _b === void 0 ? void 0 : _b.close();
(_d = (_c = this._actions) === null || _c === void 0 ? void 0 : _c.timeout_action) === null || _d === void 0 ? void 0 : _d.call(_c);
}
render() {
if (!this.open)
@ -1235,10 +1275,12 @@ class BrowserModPopup extends s {
return $ `
<ha-dialog
open
@closed=${this.closeDialog}
@closing=${this._dismiss}
.heading=${this.title !== undefined}
?hideActions=${this.actions === undefined}
.scrimClickAction=${this.dismissable ? this._dismiss : ""}
.escapeKeyAction=${this.dismissable ? this._dismiss : ""}
.scrimClickAction=${this.dismissable ? "close" : ""}
.escapeKeyAction=${this.dismissable ? "close" : ""}
>
${this.timeout
? $ ` <div slot="heading" class="progress"></div> `
@ -1289,6 +1331,7 @@ class BrowserModPopup extends s {
static get styles() {
return r$2 `
ha-dialog {
z-index: 10;
--mdc-dialog-min-width: var(--popup-min-width, 400px);
--mdc-dialog-max-width: var(--popup-max-width, 600px);
--mdc-dialog-heading-ink-color: var(--primary-text-color);
@ -2180,7 +2223,7 @@ const BrowserIDMixin = (SuperClass) => {
- Tweaks
- Quickbar tweaks (ctrl+enter)?
x Card-mod preload
- Video player?
x Video player?
x Media_seek
- Screensavers
x IMPORTANT: FIX DEFAULT HIDING OF ENTITIES

View File

@ -16,6 +16,8 @@ from homeassistant.components.media_player.const import (
MEDIA_TYPE_URL,
SUPPORT_BROWSE_MEDIA,
SUPPORT_SEEK,
SUPPORT_TURN_OFF,
SUPPORT_TURN_ON,
)
from homeassistant.const import (
STATE_UNAVAILABLE,
@ -23,6 +25,8 @@ from homeassistant.const import (
STATE_PLAYING,
STATE_IDLE,
STATE_UNKNOWN,
STATE_ON,
STATE_OFF,
)
from homeassistant.util import dt
@ -63,6 +67,8 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
"paused": STATE_PAUSED,
"stopped": STATE_IDLE,
"unavailable": STATE_UNAVAILABLE,
"on": STATE_ON,
"off": STATE_OFF,
}.get(state, STATE_UNKNOWN)
@property
@ -76,6 +82,8 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
| SUPPORT_VOLUME_MUTE
| SUPPORT_BROWSE_MEDIA
| SUPPORT_SEEK
| SUPPORT_TURN_OFF
| SUPPORT_TURN_ON
)
@property
@ -108,21 +116,24 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
async def async_play_media(self, media_type, media_id, **kwargs):
if media_source.is_media_source_id(media_id):
media_type = MEDIA_TYPE_URL
play_item = await media_source.async_resolve_media(
self.hass, media_id, self.entity_id
)
media_type = play_item.mime_type
media_id = play_item.url
media_id = async_process_play_media_url(self.hass, media_id)
if media_type in (MEDIA_TYPE_URL, MEDIA_TYPE_MUSIC):
media_id = async_process_play_media_url(self.hass, media_id)
self.browser.send("player-play", media_content_id=media_id)
self.browser.send(
"player-play", media_content_id=media_id, media_type=media_type, **kwargs
)
async def async_browse_media(self, media_content_type=None, media_content_id=None):
"""Implement the websocket media browsing helper."""
return await media_source.async_browse_media(
self.hass,
media_content_id,
content_filter=lambda item: item.media_content_type.startswith("audio/"),
# content_filter=lambda item: item.media_content_type.startswith("audio/"),
)
def media_play(self):
@ -136,3 +147,9 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
def media_seek(self, position):
self.browser.send("player-seek", position=position)
def turn_off(self):
self.browser.send("player-turn-off")
def turn_on(self, **kwargs):
self.browser.send("player-turn-on", **kwargs)

View File

@ -67,7 +67,7 @@ import { BrowserIDMixin } from "./browserID";
- Tweaks
- Quickbar tweaks (ctrl+enter)?
x Card-mod preload
- Video player?
x Video player?
x Media_seek
- Screensavers
x IMPORTANT: FIX DEFAULT HIDING OF ENTITIES

View File

@ -1,20 +1,34 @@
import { selectTree } from "../helpers";
export const MediaPlayerMixin = (SuperClass) => {
return class MediaPlayerMixinClass extends SuperClass {
public player;
private _audio_player;
private _video_player;
private _player_enabled;
private _player_update_cooldown;
constructor() {
super();
this.player = new Audio();
this._audio_player = new Audio();
this._video_player = document.createElement("video");
this._video_player.controls = true;
this._video_player.style.setProperty("width", "100%");
this.player = this._audio_player;
this._player_enabled = false;
for (const ev of ["play", "pause", "ended", "volumechange"]) {
this.player.addEventListener(ev, () => this._player_update());
this._audio_player.addEventListener(ev, () => this._player_update());
this._video_player.addEventListener(ev, () => this._player_update());
}
for (const ev of ["timeupdate"]) {
this.player.addEventListener(ev, () => this._player_update_choked());
this._audio_player.addEventListener(ev, () =>
this._player_update_choked()
);
this._video_player.addEventListener(ev, () =>
this._player_update_choked()
);
}
this.firstInteraction.then(() => {
@ -23,9 +37,15 @@ export const MediaPlayerMixin = (SuperClass) => {
});
this.addEventListener("command-player-play", (ev) => {
if (this.player.src) this.player.pause();
if (ev.detail?.media_type)
if (ev.detail?.media_type.startsWith("video"))
this.player = this._video_player;
else this.player = this._audio_player;
if (ev.detail?.media_content_id)
this.player.src = ev.detail.media_content_id;
this.player.play();
this._show_video_player();
});
this.addEventListener("command-player-pause", (ev) =>
this.player.pause()
@ -47,10 +67,38 @@ export const MediaPlayerMixin = (SuperClass) => {
this.player.currentTime = ev.detail.position;
setTimeout(() => this._player_update(), 10);
});
this.addEventListener("command-player-turn-off", (ev) => {
if (
this.player === this._video_player &&
this._video_player.isConnected
)
this.closePopup();
else if (this.player.src) this.player.pause();
this.player.src = "";
this._player_update();
});
this.connectionPromise.then(() => this._player_update());
}
private _show_video_player() {
if (this.player === this._video_player && this.player.src) {
selectTree(
document,
"home-assistant $ dialog-media-player-browse"
).then((el) => el?.closeDialog());
this.showPopup(undefined, this._video_player, {
dismiss_action: () => this._video_player.pause(),
size: "wide",
});
} else if (
this.player !== this._video_player &&
this._video_player.isConnected
) {
this.closePopup();
}
}
private _player_update_choked() {
if (this._player_update_cooldown) return;
this._player_update_cooldown = window.setTimeout(
@ -62,13 +110,13 @@ export const MediaPlayerMixin = (SuperClass) => {
private _player_update() {
const state = this._player_enabled
? this.player.src
? this.player.ended
? !this.player.src || this.player.src === window.location.href
? "off"
: this.player.ended
? "stopped"
: this.player.paused
? "paused"
: "playing"
: "stopped"
: "unavailable";
this.sendUpdate({
player: {

View File

@ -114,21 +114,21 @@ class BrowserModPopup extends LitElement {
async _primary() {
if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined;
this.dialog.close();
this.dialog?.close();
this._actions?.right_button_action?.();
}
async _secondary() {
if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined;
this.dialog.close();
this.dialog?.close();
this._actions?.left_button_action?.();
}
async _dismiss(ev?) {
this.dialog.close();
this.dialog?.close();
this._actions?.dismiss_action?.();
}
async _timeout() {
if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined;
this.dialog.close();
this.dialog?.close();
this._actions?.timeout_action?.();
}
@ -195,6 +195,7 @@ class BrowserModPopup extends LitElement {
static get styles() {
return css`
ha-dialog {
z-index: 10;
--mdc-dialog-min-width: var(--popup-min-width, 400px);
--mdc-dialog-max-width: var(--popup-max-width, 600px);
--mdc-dialog-heading-ink-color: var(--primary-text-color);