diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 6ed5c04a6f..939cb78b82 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -114,6 +114,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); diff --git a/src/data/media_source.ts b/src/data/media_source.ts index 759be2a7d3..dfcdbb37ab 100644 --- a/src/data/media_source.ts +++ b/src/data/media_source.ts @@ -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(); +}; diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index d6cfa92ffc..ff996a122f 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -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, @@ -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 { fireEvent, 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"; @@ -43,7 +52,7 @@ class PanelMediaBrowser extends LitElement { @property() public route!: Route; - @property() _currentItem?: MediaPlayerItem; + @state() _currentItem?: MediaPlayerItem; private _navigateIds: MediaPlayerItemId[] = [ { @@ -55,6 +64,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` @@ -73,15 +84,28 @@ class PanelMediaBrowser extends LitElement { .narrow=${this.narrow} > `} -
-
- ${!this._currentItem - ? this.hass.localize( - "ui.components.media-browser.media-player-browser" - ) - : this._currentItem.title} -
+
+ ${!this._currentItem + ? this.hass.localize( + "ui.components.media-browser.media-player-browser" + ) + : this._currentItem.title}
+ ${this._currentItem && + isLocalMediaSourceContentId( + this._currentItem.media_content_id || "" + ) + ? html` + + + + ` + : ""} { + 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, @@ -237,6 +287,10 @@ class PanelMediaBrowser extends LitElement { left: 0; right: 0; } + + ha-svg-icon[slot="icon"] { + vertical-align: middle; + } `, ]; } diff --git a/src/translations/en.json b/src/translations/en.json index 2583b4d528..547d8ff918 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -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",