mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
Add support for accept keyword in media selector (#25808)
This commit is contained in:
parent
fdd6ccf379
commit
589fa75b17
@ -1,6 +1,6 @@
|
||||
import { mdiPlayBox, mdiPlus } from "@mdi/js";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
@ -84,20 +84,30 @@ export class HaMediaSelector extends LitElement {
|
||||
(stateObj &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA));
|
||||
|
||||
return html`<ha-entity-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this.value?.entity_id}
|
||||
.label=${this.label ||
|
||||
this.hass.localize("ui.components.selectors.media.pick_media_player")}
|
||||
.disabled=${this.disabled}
|
||||
.helper=${this.helper}
|
||||
.required=${this.required}
|
||||
include-domains='["media_player"]'
|
||||
allow-custom-entity
|
||||
@value-changed=${this._entityChanged}
|
||||
></ha-entity-picker>
|
||||
const hasAccept = this.selector.media?.accept?.length;
|
||||
|
||||
return html`
|
||||
${hasAccept
|
||||
? nothing
|
||||
: html`
|
||||
<ha-entity-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this.value?.entity_id}
|
||||
.label=${this.label ||
|
||||
this.hass.localize(
|
||||
"ui.components.selectors.media.pick_media_player"
|
||||
)}
|
||||
.disabled=${this.disabled}
|
||||
.helper=${this.helper}
|
||||
.required=${this.required}
|
||||
include-domains='["media_player"]'
|
||||
allow-custom-entity
|
||||
@value-changed=${this._entityChanged}
|
||||
></ha-entity-picker>
|
||||
`}
|
||||
${!supportsBrowse
|
||||
? html`<ha-alert>
|
||||
? html`
|
||||
<ha-alert>
|
||||
${this.hass.localize(
|
||||
"ui.components.selectors.media.browse_not_supported"
|
||||
)}
|
||||
@ -107,62 +117,72 @@ export class HaMediaSelector extends LitElement {
|
||||
.data=${this.value}
|
||||
.schema=${MANUAL_SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
></ha-form>`
|
||||
: html`<ha-card
|
||||
outlined
|
||||
@click=${this._pickMedia}
|
||||
class=${this.disabled || !this.value?.entity_id ? "disabled" : ""}
|
||||
>
|
||||
<div
|
||||
class="thumbnail ${classMap({
|
||||
portrait:
|
||||
!!this.value?.metadata?.media_class &&
|
||||
MediaClassBrowserSettings[
|
||||
this.value.metadata.children_media_class ||
|
||||
this.value.metadata.media_class
|
||||
].thumbnail_ratio === "portrait",
|
||||
})}"
|
||||
></ha-form>
|
||||
`
|
||||
: html`
|
||||
<ha-card
|
||||
outlined
|
||||
@click=${this._pickMedia}
|
||||
class=${this.disabled || (!this.value?.entity_id && !hasAccept)
|
||||
? "disabled"
|
||||
: ""}
|
||||
>
|
||||
${this.value?.metadata?.thumbnail
|
||||
? html`
|
||||
<div
|
||||
class="${classMap({
|
||||
"centered-image":
|
||||
!!this.value.metadata.media_class &&
|
||||
["app", "directory"].includes(
|
||||
this.value.metadata.media_class
|
||||
),
|
||||
})}
|
||||
<div
|
||||
class="thumbnail ${classMap({
|
||||
portrait:
|
||||
!!this.value?.metadata?.media_class &&
|
||||
MediaClassBrowserSettings[
|
||||
this.value.metadata.children_media_class ||
|
||||
this.value.metadata.media_class
|
||||
].thumbnail_ratio === "portrait",
|
||||
})}"
|
||||
>
|
||||
${this.value?.metadata?.thumbnail
|
||||
? html`
|
||||
<div
|
||||
class="${classMap({
|
||||
"centered-image":
|
||||
!!this.value.metadata.media_class &&
|
||||
["app", "directory"].includes(
|
||||
this.value.metadata.media_class
|
||||
),
|
||||
})}
|
||||
image"
|
||||
style=${this._thumbnailUrl
|
||||
? `background-image: url(${this._thumbnailUrl});`
|
||||
: ""}
|
||||
></div>
|
||||
`
|
||||
: html`
|
||||
<div class="icon-holder image">
|
||||
<ha-svg-icon
|
||||
class="folder"
|
||||
.path=${!this.value?.media_content_id
|
||||
? mdiPlus
|
||||
: this.value?.metadata?.media_class
|
||||
? MediaClassBrowserSettings[
|
||||
this.value.metadata.media_class === "directory"
|
||||
? this.value.metadata.children_media_class ||
|
||||
this.value.metadata.media_class
|
||||
: this.value.metadata.media_class
|
||||
].icon
|
||||
: mdiPlayBox}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
<div class="title">
|
||||
${!this.value?.media_content_id
|
||||
? this.hass.localize("ui.components.selectors.media.pick_media")
|
||||
: this.value.metadata?.title || this.value.media_content_id}
|
||||
</div>
|
||||
</ha-card>`}`;
|
||||
style=${this._thumbnailUrl
|
||||
? `background-image: url(${this._thumbnailUrl});`
|
||||
: ""}
|
||||
></div>
|
||||
`
|
||||
: html`
|
||||
<div class="icon-holder image">
|
||||
<ha-svg-icon
|
||||
class="folder"
|
||||
.path=${!this.value?.media_content_id
|
||||
? mdiPlus
|
||||
: this.value?.metadata?.media_class
|
||||
? MediaClassBrowserSettings[
|
||||
this.value.metadata.media_class ===
|
||||
"directory"
|
||||
? this.value.metadata
|
||||
.children_media_class ||
|
||||
this.value.metadata.media_class
|
||||
: this.value.metadata.media_class
|
||||
].icon
|
||||
: mdiPlayBox}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
<div class="title">
|
||||
${!this.value?.media_content_id
|
||||
? this.hass.localize(
|
||||
"ui.components.selectors.media.pick_media"
|
||||
)
|
||||
: this.value.metadata?.title || this.value.media_content_id}
|
||||
</div>
|
||||
</ha-card>
|
||||
`}
|
||||
`;
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
@ -184,8 +204,9 @@ export class HaMediaSelector extends LitElement {
|
||||
private _pickMedia() {
|
||||
showMediaBrowserDialog(this, {
|
||||
action: "pick",
|
||||
entityId: this.value!.entity_id!,
|
||||
navigateIds: this.value!.metadata?.navigateIds,
|
||||
entityId: this.value?.entity_id,
|
||||
navigateIds: this.value?.metadata?.navigateIds,
|
||||
accept: this.selector.media?.accept,
|
||||
mediaPickedCallback: (pickedMedia: MediaPickedEvent) => {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
|
@ -80,7 +80,16 @@ const SELECTOR_SCHEMAS = {
|
||||
] as const,
|
||||
icon: [] as const,
|
||||
location: [] as const,
|
||||
media: [] as const,
|
||||
media: [
|
||||
{
|
||||
name: "accept",
|
||||
selector: {
|
||||
text: {
|
||||
multiple: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const,
|
||||
number: [
|
||||
{
|
||||
name: "min",
|
||||
|
@ -164,6 +164,7 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
.navigateIds=${this._navigateIds}
|
||||
.action=${this._action}
|
||||
.preferredLayout=${this._preferredLayout}
|
||||
.accept=${this._params.accept}
|
||||
@close-dialog=${this.closeDialog}
|
||||
@media-picked=${this._mediaPicked}
|
||||
@media-browsed=${this._mediaBrowsed}
|
||||
|
@ -78,7 +78,7 @@ export interface MediaPlayerItemId {
|
||||
export class HaMediaPlayerBrowse extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public entityId!: string;
|
||||
@property({ attribute: false }) public entityId?: string;
|
||||
|
||||
@property() public action: MediaPlayerBrowseAction = "play";
|
||||
|
||||
@ -89,6 +89,8 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public navigateIds: MediaPlayerItemId[] = [];
|
||||
|
||||
@property({ attribute: false }) public accept?: string[];
|
||||
|
||||
// @todo Consider reworking to eliminate need for attribute since it is manipulated internally
|
||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||
|
||||
@ -250,6 +252,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
});
|
||||
} else if (
|
||||
err.code === "entity_not_found" &&
|
||||
this.entityId &&
|
||||
isUnavailableState(this.hass.states[this.entityId]?.state)
|
||||
) {
|
||||
this._setError({
|
||||
@ -334,7 +337,37 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
const subtitle = this.hass.localize(
|
||||
`ui.components.media-browser.class.${currentItem.media_class}`
|
||||
);
|
||||
const children = currentItem.children || [];
|
||||
let children = currentItem.children || [];
|
||||
const canPlayChildren = new Set<string>();
|
||||
|
||||
// Filter children based on accept property if provided
|
||||
if (this.accept && children.length > 0) {
|
||||
let checks: ((t: string) => boolean)[] = [];
|
||||
|
||||
for (const type of this.accept) {
|
||||
if (type.endsWith("/*")) {
|
||||
const baseType = type.slice(0, -1);
|
||||
checks.push((t) => t.startsWith(baseType));
|
||||
} else if (type === "*") {
|
||||
checks = [() => true];
|
||||
break;
|
||||
} else {
|
||||
checks.push((t) => t === type);
|
||||
}
|
||||
}
|
||||
|
||||
children = children.filter((child) => {
|
||||
const contentType = child.media_content_type.toLowerCase();
|
||||
const canPlay =
|
||||
child.media_content_type &&
|
||||
checks.some((check) => check(contentType));
|
||||
if (canPlay) {
|
||||
canPlayChildren.add(child.media_content_id);
|
||||
}
|
||||
return !child.media_content_type || child.can_expand || canPlay;
|
||||
});
|
||||
}
|
||||
|
||||
const mediaClass = MediaClassBrowserSettings[currentItem.media_class];
|
||||
const childrenMediaClass = currentItem.children_media_class
|
||||
? MediaClassBrowserSettings[currentItem.children_media_class]
|
||||
@ -367,7 +400,12 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
""
|
||||
)}"
|
||||
>
|
||||
${this.narrow && currentItem?.can_play
|
||||
${this.narrow &&
|
||||
currentItem?.can_play &&
|
||||
(!this.accept ||
|
||||
canPlayChildren.has(
|
||||
currentItem.media_content_id
|
||||
))
|
||||
? html`
|
||||
<ha-fab
|
||||
mini
|
||||
@ -748,11 +786,11 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
};
|
||||
|
||||
private async _fetchData(
|
||||
entityId: string,
|
||||
entityId: string | undefined,
|
||||
mediaContentId?: string,
|
||||
mediaContentType?: string
|
||||
): Promise<MediaPlayerItem> {
|
||||
return entityId !== BROWSER_PLAYER
|
||||
return entityId && entityId !== BROWSER_PLAYER
|
||||
? browseMediaPlayer(this.hass, entityId, mediaContentId, mediaContentType)
|
||||
: browseLocalMediaPlayer(this.hass, mediaContentId);
|
||||
}
|
||||
|
@ -7,10 +7,11 @@ import type { MediaPlayerItemId } from "./ha-media-player-browse";
|
||||
|
||||
export interface MediaPlayerBrowseDialogParams {
|
||||
action: MediaPlayerBrowseAction;
|
||||
entityId: string;
|
||||
entityId?: string;
|
||||
mediaPickedCallback: (pickedMedia: MediaPickedEvent) => void;
|
||||
navigateIds?: MediaPlayerItemId[];
|
||||
minimumNavigateLevel?: number;
|
||||
accept?: string[];
|
||||
}
|
||||
|
||||
export const showMediaBrowserDialog = (
|
||||
|
@ -303,7 +303,9 @@ export interface LocationSelectorValue {
|
||||
}
|
||||
|
||||
export interface MediaSelector {
|
||||
media: {} | null;
|
||||
media: {
|
||||
accept?: string[];
|
||||
} | null;
|
||||
}
|
||||
|
||||
export interface MediaSelectorValue {
|
||||
|
Loading…
x
Reference in New Issue
Block a user