Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/components/media-player/ha-media-player-browse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,19 @@ export class HaMediaPlayerBrowse extends LitElement {
}
}

public async refresh() {
const currentId = this.navigateIds[this.navigateIds.length - 1];
try {
this._currentItem = await this._fetchData(
this.entityId,
currentId.media_content_id,
currentId.media_content_type
);
} catch (err) {
this._setError(err);
}
}

public play(): void {
if (this._currentItem?.can_play) {
this._runAction(this._currentItem);
Expand Down
26 changes: 26 additions & 0 deletions src/data/media_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,29 @@ export const browseLocalMediaPlayer = (
type: "media_source/browse_media",
media_content_id: mediaContentId,
});

export const isLocalMediaSourceContentId = (mediaId: string) =>
mediaId.startsWith("media-source://media_source");

export const uploadLocalMedia = async (
hass: HomeAssistant,
media_content_id: string,
file: File
) => {
const fd = new FormData();
fd.append("media_content_id", media_content_id);
fd.append("file", file);
const resp = await hass.fetchWithAuth(
"/api/media_source/local_source/upload",
{
method: "POST",
body: fd,
}
);
if (resp.status === 413) {
throw new Error("Uploaded image is too large");
} else if (resp.status !== 200) {
throw new Error("Unknown error");
}
return resp.json();
};
80 changes: 67 additions & 13 deletions src/panels/media-browser/ha-panel-media-browser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mdiArrowLeft } from "@mdi/js";
import { mdiArrowLeft, mdiUpload } from "@mdi/js";
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@material/mwc-button";
import {
css,
CSSResultGroup,
Expand All @@ -9,20 +10,28 @@ import {
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, query, state } from "lit/decorators";
import { LocalStorage } from "../../common/decorators/local-storage";
import { HASSDomEvent } from "../../common/dom/fire_event";
import { navigate } from "../../common/navigate";
import "../../components/ha-menu-button";
import "../../components/ha-icon-button";
import "../../components/ha-svg-icon";
import "../../components/media-player/ha-media-player-browse";
import type { MediaPlayerItemId } from "../../components/media-player/ha-media-player-browse";
import type {
HaMediaPlayerBrowse,
MediaPlayerItemId,
} from "../../components/media-player/ha-media-player-browse";
import {
BROWSER_PLAYER,
MediaPickedEvent,
MediaPlayerItem,
} from "../../data/media-player";
import { resolveMediaSource } from "../../data/media_source";
import {
isLocalMediaSourceContentId,
resolveMediaSource,
uploadLocalMedia,
} from "../../data/media_source";
import "../../layouts/ha-app-layout";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant, Route } from "../../types";
Expand All @@ -39,7 +48,7 @@ class PanelMediaBrowser extends LitElement {

@property() public route!: Route;

@property() _currentItem?: MediaPlayerItem;
@state() _currentItem?: MediaPlayerItem;

private _navigateIds: MediaPlayerItemId[] = [
{
Expand All @@ -51,6 +60,8 @@ class PanelMediaBrowser extends LitElement {
@LocalStorage("mediaBrowseEntityId", true, false)
private _entityId = BROWSER_PLAYER;

@query("ha-media-player-browse") private _browser!: HaMediaPlayerBrowse;

protected render(): TemplateResult {
return html`
<ha-app-layout>
Expand All @@ -69,15 +80,28 @@ class PanelMediaBrowser extends LitElement {
.narrow=${this.narrow}
></ha-menu-button>
`}
<div main-title class="heading">
<div>
${!this._currentItem
? this.hass.localize(
"ui.components.media-browser.media-player-browser"
)
: this._currentItem.title}
</div>
<div main-title>
${!this._currentItem
? this.hass.localize(
"ui.components.media-browser.media-player-browser"
)
: this._currentItem.title}
</div>
${this._currentItem &&
isLocalMediaSourceContentId(
this._currentItem.media_content_id || ""
)
? html`
<mwc-button
.label=${this.hass.localize(
"ui.components.media-browser.file_management.add_media"
)}
@click=${this._startUpload}
>
<ha-svg-icon .path=${mdiUpload} slot="icon"></ha-svg-icon>
</mwc-button>
`
: ""}
</app-toolbar>
</app-header>
<ha-media-player-browse
Expand Down Expand Up @@ -200,6 +224,32 @@ class PanelMediaBrowser extends LitElement {
}
}

private async _startUpload() {
const input = document.createElement("input");
input.type = "file";
input.addEventListener("change", async () => {
try {
await uploadLocalMedia(
this.hass,
this._currentItem!.media_content_id!,
input.files![0]
);
} catch (err: any) {
showAlertDialog(this, {
text: this.hass.localize(
"ui.components.media-browser.file_management.upload_failed",
{
reason: err.message || err,
}
),
});
return;
}
await this._browser.refresh();
});
input.click();
}

static get styles(): CSSResultGroup {
return [
haStyle,
Expand All @@ -222,6 +272,10 @@ class PanelMediaBrowser extends LitElement {
left: 0;
right: 0;
}

ha-svg-icon[slot="icon"] {
vertical-align: middle;
}
`,
];
}
Expand Down
4 changes: 4 additions & 0 deletions src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,10 @@
"no_local_media_found": "No local media found",
"no_media_folder": "It looks like you have not yet created a media directory.",
"setup_local_help": "Check the {documentation} on how to setup local media.",
"file_management": {
"upload_failed": "Upload failed: {reason}",
"add_media": "Add Media"
},
"class": {
"album": "Album",
"app": "App",
Expand Down