Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Media Browser Panel #6772

Merged
merged 41 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
eac6975
updates
zsarnett Sep 2, 2020
26962f7
Merge branch 'dev' of https://github.com/home-assistant/frontend into…
zsarnett Sep 2, 2020
42b5d5f
local storage
zsarnett Sep 2, 2020
1adf210
Changes
zsarnett Sep 2, 2020
05b6ee1
fix dialog heading
zsarnett Sep 2, 2020
f05d855
console.bye
zsarnett Sep 2, 2020
b4b4e56
add HLS and Exo Player
zsarnett Sep 3, 2020
2b7632d
Merge branch 'dev' of https://github.com/home-assistant/frontend into…
zsarnett Sep 3, 2020
0e8f0d2
comments
zsarnett Sep 3, 2020
ba746be
Update src/panels/media-browser/hui-dialog-browser-media-player.ts
zsarnett Sep 3, 2020
2f2c51c
Update src/panels/media-browser/hui-dialog-select-media-player.ts
zsarnett Sep 3, 2020
3b4df76
Update src/data/media-player.ts
zsarnett Sep 3, 2020
464b034
Update src/data/media-player.ts
zsarnett Sep 3, 2020
b3607b5
Update src/data/media-player.ts
zsarnett Sep 3, 2020
e63b151
Update src/data/media-player.ts
zsarnett Sep 3, 2020
1992825
Update src/data/media-player.ts
zsarnett Sep 3, 2020
e5da468
Update src/data/media-player.ts
zsarnett Sep 3, 2020
f446b4c
Update src/data/media-player.ts
zsarnett Sep 3, 2020
f90c8f4
Update src/data/media-player.ts
zsarnett Sep 3, 2020
9febb2d
Update src/data/media-player.ts
zsarnett Sep 3, 2020
6082ca4
Update src/components/media-player/dialog-media-player-browse.ts
zsarnett Sep 3, 2020
0cff9a6
add imports
zsarnett Sep 3, 2020
5476691
Undo hack for panel
zsarnett Sep 4, 2020
5691bdd
more undo
zsarnett Sep 4, 2020
c6aa013
last undo
zsarnett Sep 4, 2020
5b969b4
remove un used code
zsarnett Sep 4, 2020
144dca4
remove un used translation
zsarnett Sep 4, 2020
a46d4e5
Update src/components/ha-dialog.ts
zsarnett Sep 4, 2020
227db34
comments
zsarnett Sep 4, 2020
16dc8ff
Change from memoize
zsarnett Sep 4, 2020
30a5269
comments
zsarnett Sep 4, 2020
afd40c6
comments
zsarnett Sep 4, 2020
39972ab
more comments
zsarnett Sep 4, 2020
41d5463
convert ha-camera-stream to use new element
zsarnett Sep 4, 2020
36ac286
move to a private function
zsarnett Sep 4, 2020
4b753a5
comment
zsarnett Sep 4, 2020
c4d2f04
Merge branch 'dev' of https://github.com/home-assistant/frontend into…
zsarnett Sep 4, 2020
4ca6b2e
Local storage
zsarnett Sep 4, 2020
a6f6203
fix more info controls
zsarnett Sep 4, 2020
073e749
fix connected
zsarnett Sep 4, 2020
b1e9d4e
attached logic
zsarnett Sep 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/components/ha-dialog.ts
Expand Up @@ -10,7 +10,7 @@ import "./ha-icon-button";
const MwcDialog = customElements.get("mwc-dialog") as Constructor<Dialog>;

export const createCloseHeading = (hass: HomeAssistant, title: string) => html`
${title}
<div class="header_title">${title}</div>
zsarnett marked this conversation as resolved.
Show resolved Hide resolved
<mwc-icon-button
aria-label=${hass.localize("ui.dialogs.generic.close")}
dialogAction="close"
Expand Down Expand Up @@ -77,10 +77,17 @@ export class HaDialog extends MwcDialog {
text-decoration: none;
color: inherit;
}
.header_title {
margin-right: 40px;
}
[dir="rtl"].header_button {
right: auto;
left: 16px;
}
[dir="rtl"].header_title {
margin-left: 40px;
zsarnett marked this conversation as resolved.
Show resolved Hide resolved
margin-right: 0px;
}
`,
];
}
Expand Down
193 changes: 193 additions & 0 deletions src/components/ha-hls-player.ts
@@ -0,0 +1,193 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../common/dom/fire_event";
import { nextRender } from "../common/util/render-status";
import { getExternalConfig } from "../external_app/external_config";
import type { HomeAssistant } from "../types";

type HLSModule = typeof import("hls.js");

@customElement("ha-hls-player")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we also use this in ha-camera-stream?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tbh I didn't want to fuck it up

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a copy/paste right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completed. Twas easy once I could test it :)

Probably could use more testing

class HaHLSPlayer extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@property() public url!: string;

@property({ type: Boolean, attribute: "controls" })
public controls = false;

@property({ type: Boolean, attribute: "muted" })
public muted = false;

@property({ type: Boolean, attribute: "autoplay" })
public autoPlay = false;

@property({ type: Boolean, attribute: "playsinline" })
public playsInline = false;

@query("video") private _videoEl!: HTMLVideoElement;

private _hlsPolyfillInstance?: Hls;

private _useExoPlayer = false;

public disconnectedCallback() {
super.disconnectedCallback();
this._destroyPolyfill();
}

protected render(): TemplateResult {
return html`
zsarnett marked this conversation as resolved.
Show resolved Hide resolved
<video
?autoplay=${this.autoPlay}
?muted=${this.muted}
?playsinline=${this.playsInline}
?controls=${this.controls}
@loadeddata=${this._elementResized}
></video>
`;
}

protected updated(changedProps: PropertyValues) {
super.updated(changedProps);

if (changedProps.has("url")) {
// Tear down existing polyfill, if available
this._destroyPolyfill();
this._startHls();
}
}

private async _getUseExoPlayer(): Promise<boolean> {
if (!this.hass!.auth.external) {
return false;
}
const externalConfig = await getExternalConfig(this.hass!.auth.external);
return externalConfig && externalConfig.hasExoPlayer;
}

private async _startHls(): Promise<void> {
let hls: any;
const videoEl = this._videoEl;
this._useExoPlayer = await this._getUseExoPlayer();
if (!this._useExoPlayer) {
hls = ((await import(/* webpackChunkName: "hls.js" */ "hls.js")) as any)
.default as HLSModule;
let hlsSupported = hls.isSupported();

if (!hlsSupported) {
hlsSupported =
videoEl.canPlayType("application/vnd.apple.mpegurl") !== "";
}

if (!hlsSupported) {
this._videoEl.innerHTML = this.hass.localize(
"ui.components.media-browser.video_not_supported"
);
return;
}
}

const url = this.url;

if (this._useExoPlayer) {
this._renderHLSExoPlayer(url);
} else if (hls.isSupported()) {
this._renderHLSPolyfill(videoEl, hls, url);
} else {
this._renderHLSNative(videoEl, url);
}
}

private async _renderHLSExoPlayer(url: string) {
window.addEventListener("resize", this._resizeExoPlayer);
this.updateComplete.then(() => nextRender()).then(this._resizeExoPlayer);
this._videoEl.style.visibility = "hidden";
await this.hass!.auth.external!.sendMessage({
type: "exoplayer/play_hls",
payload: new URL(url, window.location.href).toString(),
});
}

private _resizeExoPlayer = () => {
const rect = this._videoEl.getBoundingClientRect();
this.hass!.auth.external!.fireMessage({
type: "exoplayer/resize",
payload: {
left: rect.left,
top: rect.top,
right: rect.right,
bottom: rect.bottom,
},
});
};

private async _renderHLSPolyfill(
videoEl: HTMLVideoElement,
Hls: HLSModule,
url: string
) {
const hls = new Hls({
liveBackBufferLength: 60,
fragLoadingTimeOut: 30000,
manifestLoadingTimeOut: 30000,
levelLoadingTimeOut: 30000,
});
this._hlsPolyfillInstance = hls;
hls.attachMedia(videoEl);
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
hls.loadSource(url);
});
}

private async _renderHLSNative(videoEl: HTMLVideoElement, url: string) {
videoEl.src = url;
await new Promise((resolve) =>
videoEl.addEventListener("loadedmetadata", resolve)
);
videoEl.play();
}

private _elementResized() {
fireEvent(this, "iron-resize");
}

private _destroyPolyfill() {
if (this._hlsPolyfillInstance) {
this._hlsPolyfillInstance.destroy();
this._hlsPolyfillInstance = undefined;
}
if (this._useExoPlayer) {
window.removeEventListener("resize", this._resizeExoPlayer);
this.hass!.auth.external!.fireMessage({ type: "exoplayer/stop" });
}
}

static get styles(): CSSResult {
return css`
:host,
video {
display: block;
}

video {
width: 100%;
}
`;
}
}

declare global {
interface HTMLElementTagNameMap {
"ha-hls-player": HaHLSPlayer;
}
}
29 changes: 9 additions & 20 deletions src/components/media-player/dialog-media-player-browse.ts
Expand Up @@ -8,7 +8,7 @@ import {
property,
TemplateResult,
} from "lit-element";
import { HASSDomEvent } from "../../common/dom/fire_event";
import { fireEvent, HASSDomEvent } from "../../common/dom/fire_event";
import type {
MediaPickedEvent,
MediaPlayerBrowseAction,
Expand All @@ -33,16 +33,17 @@ class DialogMediaPlayerBrowse extends LitElement {

@internalProperty() private _params?: MediaPlayerBrowseDialogParams;

public async showDialog(
params: MediaPlayerBrowseDialogParams
): Promise<void> {
public showDialog(params: MediaPlayerBrowseDialogParams): void {
this._params = params;
this._entityId = this._params.entityId;
this._mediaContentId = this._params.mediaContentId;
this._mediaContentType = this._params.mediaContentType;
this._action = this._params.action || "play";
}

await this.updateComplete;
public closeDialog() {
this._params = undefined;
fireEvent(this, "dialog-closed", {dialog: this.localName});
}

protected render(): TemplateResult {
Expand All @@ -57,7 +58,7 @@ class DialogMediaPlayerBrowse extends LitElement {
escapeKeyAction
hideActions
flexContent
@closed=${this._closeDialog}
@closed=${this.closeDialog}
>
<ha-media-player-browse
dialog
Expand All @@ -66,21 +67,17 @@ class DialogMediaPlayerBrowse extends LitElement {
.action=${this._action!}
.mediaContentId=${this._mediaContentId}
.mediaContentType=${this._mediaContentType}
@close-dialog=${this._closeDialog}
@close-dialog=${this.closeDialog}
@media-picked=${this._mediaPicked}
></ha-media-player-browse>
</ha-dialog>
`;
}

private _closeDialog() {
this._params = undefined;
}

private _mediaPicked(ev: HASSDomEvent<MediaPickedEvent>): void {
this._params!.mediaPickedCallback(ev.detail);
if (this._action !== "play") {
this._closeDialog();
this.closeDialog();
}
}

Expand All @@ -93,14 +90,6 @@ class DialogMediaPlayerBrowse extends LitElement {
--dialog-content-padding: 0;
}

ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
border-bottom: 1px solid
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
}

@media (min-width: 800px) {
ha-dialog {
--mdc-dialog-max-width: 800px;
Expand Down