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 { mdiPlayBox, mdiPlus } from "@mdi/js";
|
||||||
import type { PropertyValues } from "lit";
|
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 { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
@ -84,11 +84,19 @@ export class HaMediaSelector extends LitElement {
|
|||||||
(stateObj &&
|
(stateObj &&
|
||||||
supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA));
|
supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA));
|
||||||
|
|
||||||
return html`<ha-entity-picker
|
const hasAccept = this.selector.media?.accept?.length;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
${hasAccept
|
||||||
|
? nothing
|
||||||
|
: html`
|
||||||
|
<ha-entity-picker
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this.value?.entity_id}
|
.value=${this.value?.entity_id}
|
||||||
.label=${this.label ||
|
.label=${this.label ||
|
||||||
this.hass.localize("ui.components.selectors.media.pick_media_player")}
|
this.hass.localize(
|
||||||
|
"ui.components.selectors.media.pick_media_player"
|
||||||
|
)}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.helper=${this.helper}
|
.helper=${this.helper}
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
@ -96,8 +104,10 @@ export class HaMediaSelector extends LitElement {
|
|||||||
allow-custom-entity
|
allow-custom-entity
|
||||||
@value-changed=${this._entityChanged}
|
@value-changed=${this._entityChanged}
|
||||||
></ha-entity-picker>
|
></ha-entity-picker>
|
||||||
|
`}
|
||||||
${!supportsBrowse
|
${!supportsBrowse
|
||||||
? html`<ha-alert>
|
? html`
|
||||||
|
<ha-alert>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.components.selectors.media.browse_not_supported"
|
"ui.components.selectors.media.browse_not_supported"
|
||||||
)}
|
)}
|
||||||
@ -107,11 +117,15 @@ export class HaMediaSelector extends LitElement {
|
|||||||
.data=${this.value}
|
.data=${this.value}
|
||||||
.schema=${MANUAL_SCHEMA}
|
.schema=${MANUAL_SCHEMA}
|
||||||
.computeLabel=${this._computeLabelCallback}
|
.computeLabel=${this._computeLabelCallback}
|
||||||
></ha-form>`
|
></ha-form>
|
||||||
: html`<ha-card
|
`
|
||||||
|
: html`
|
||||||
|
<ha-card
|
||||||
outlined
|
outlined
|
||||||
@click=${this._pickMedia}
|
@click=${this._pickMedia}
|
||||||
class=${this.disabled || !this.value?.entity_id ? "disabled" : ""}
|
class=${this.disabled || (!this.value?.entity_id && !hasAccept)
|
||||||
|
? "disabled"
|
||||||
|
: ""}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="thumbnail ${classMap({
|
class="thumbnail ${classMap({
|
||||||
@ -147,8 +161,10 @@ export class HaMediaSelector extends LitElement {
|
|||||||
? mdiPlus
|
? mdiPlus
|
||||||
: this.value?.metadata?.media_class
|
: this.value?.metadata?.media_class
|
||||||
? MediaClassBrowserSettings[
|
? MediaClassBrowserSettings[
|
||||||
this.value.metadata.media_class === "directory"
|
this.value.metadata.media_class ===
|
||||||
? this.value.metadata.children_media_class ||
|
"directory"
|
||||||
|
? this.value.metadata
|
||||||
|
.children_media_class ||
|
||||||
this.value.metadata.media_class
|
this.value.metadata.media_class
|
||||||
: this.value.metadata.media_class
|
: this.value.metadata.media_class
|
||||||
].icon
|
].icon
|
||||||
@ -159,10 +175,14 @@ export class HaMediaSelector extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
${!this.value?.media_content_id
|
${!this.value?.media_content_id
|
||||||
? this.hass.localize("ui.components.selectors.media.pick_media")
|
? this.hass.localize(
|
||||||
|
"ui.components.selectors.media.pick_media"
|
||||||
|
)
|
||||||
: this.value.metadata?.title || this.value.media_content_id}
|
: this.value.metadata?.title || this.value.media_content_id}
|
||||||
</div>
|
</div>
|
||||||
</ha-card>`}`;
|
</ha-card>
|
||||||
|
`}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeLabelCallback = (
|
private _computeLabelCallback = (
|
||||||
@ -184,8 +204,9 @@ export class HaMediaSelector extends LitElement {
|
|||||||
private _pickMedia() {
|
private _pickMedia() {
|
||||||
showMediaBrowserDialog(this, {
|
showMediaBrowserDialog(this, {
|
||||||
action: "pick",
|
action: "pick",
|
||||||
entityId: this.value!.entity_id!,
|
entityId: this.value?.entity_id,
|
||||||
navigateIds: this.value!.metadata?.navigateIds,
|
navigateIds: this.value?.metadata?.navigateIds,
|
||||||
|
accept: this.selector.media?.accept,
|
||||||
mediaPickedCallback: (pickedMedia: MediaPickedEvent) => {
|
mediaPickedCallback: (pickedMedia: MediaPickedEvent) => {
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: {
|
value: {
|
||||||
|
@ -80,7 +80,16 @@ const SELECTOR_SCHEMAS = {
|
|||||||
] as const,
|
] as const,
|
||||||
icon: [] as const,
|
icon: [] as const,
|
||||||
location: [] as const,
|
location: [] as const,
|
||||||
media: [] as const,
|
media: [
|
||||||
|
{
|
||||||
|
name: "accept",
|
||||||
|
selector: {
|
||||||
|
text: {
|
||||||
|
multiple: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as const,
|
||||||
number: [
|
number: [
|
||||||
{
|
{
|
||||||
name: "min",
|
name: "min",
|
||||||
|
@ -164,6 +164,7 @@ class DialogMediaPlayerBrowse extends LitElement {
|
|||||||
.navigateIds=${this._navigateIds}
|
.navigateIds=${this._navigateIds}
|
||||||
.action=${this._action}
|
.action=${this._action}
|
||||||
.preferredLayout=${this._preferredLayout}
|
.preferredLayout=${this._preferredLayout}
|
||||||
|
.accept=${this._params.accept}
|
||||||
@close-dialog=${this.closeDialog}
|
@close-dialog=${this.closeDialog}
|
||||||
@media-picked=${this._mediaPicked}
|
@media-picked=${this._mediaPicked}
|
||||||
@media-browsed=${this._mediaBrowsed}
|
@media-browsed=${this._mediaBrowsed}
|
||||||
|
@ -78,7 +78,7 @@ export interface MediaPlayerItemId {
|
|||||||
export class HaMediaPlayerBrowse extends LitElement {
|
export class HaMediaPlayerBrowse extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public entityId!: string;
|
@property({ attribute: false }) public entityId?: string;
|
||||||
|
|
||||||
@property() public action: MediaPlayerBrowseAction = "play";
|
@property() public action: MediaPlayerBrowseAction = "play";
|
||||||
|
|
||||||
@ -89,6 +89,8 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public navigateIds: MediaPlayerItemId[] = [];
|
@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
|
// @todo Consider reworking to eliminate need for attribute since it is manipulated internally
|
||||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||||
|
|
||||||
@ -250,6 +252,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
err.code === "entity_not_found" &&
|
err.code === "entity_not_found" &&
|
||||||
|
this.entityId &&
|
||||||
isUnavailableState(this.hass.states[this.entityId]?.state)
|
isUnavailableState(this.hass.states[this.entityId]?.state)
|
||||||
) {
|
) {
|
||||||
this._setError({
|
this._setError({
|
||||||
@ -334,7 +337,37 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
const subtitle = this.hass.localize(
|
const subtitle = this.hass.localize(
|
||||||
`ui.components.media-browser.class.${currentItem.media_class}`
|
`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 mediaClass = MediaClassBrowserSettings[currentItem.media_class];
|
||||||
const childrenMediaClass = currentItem.children_media_class
|
const childrenMediaClass = currentItem.children_media_class
|
||||||
? MediaClassBrowserSettings[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`
|
? html`
|
||||||
<ha-fab
|
<ha-fab
|
||||||
mini
|
mini
|
||||||
@ -748,11 +786,11 @@ export class HaMediaPlayerBrowse extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private async _fetchData(
|
private async _fetchData(
|
||||||
entityId: string,
|
entityId: string | undefined,
|
||||||
mediaContentId?: string,
|
mediaContentId?: string,
|
||||||
mediaContentType?: string
|
mediaContentType?: string
|
||||||
): Promise<MediaPlayerItem> {
|
): Promise<MediaPlayerItem> {
|
||||||
return entityId !== BROWSER_PLAYER
|
return entityId && entityId !== BROWSER_PLAYER
|
||||||
? browseMediaPlayer(this.hass, entityId, mediaContentId, mediaContentType)
|
? browseMediaPlayer(this.hass, entityId, mediaContentId, mediaContentType)
|
||||||
: browseLocalMediaPlayer(this.hass, mediaContentId);
|
: browseLocalMediaPlayer(this.hass, mediaContentId);
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,11 @@ import type { MediaPlayerItemId } from "./ha-media-player-browse";
|
|||||||
|
|
||||||
export interface MediaPlayerBrowseDialogParams {
|
export interface MediaPlayerBrowseDialogParams {
|
||||||
action: MediaPlayerBrowseAction;
|
action: MediaPlayerBrowseAction;
|
||||||
entityId: string;
|
entityId?: string;
|
||||||
mediaPickedCallback: (pickedMedia: MediaPickedEvent) => void;
|
mediaPickedCallback: (pickedMedia: MediaPickedEvent) => void;
|
||||||
navigateIds?: MediaPlayerItemId[];
|
navigateIds?: MediaPlayerItemId[];
|
||||||
minimumNavigateLevel?: number;
|
minimumNavigateLevel?: number;
|
||||||
|
accept?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const showMediaBrowserDialog = (
|
export const showMediaBrowserDialog = (
|
||||||
|
@ -303,7 +303,9 @@ export interface LocationSelectorValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MediaSelector {
|
export interface MediaSelector {
|
||||||
media: {} | null;
|
media: {
|
||||||
|
accept?: string[];
|
||||||
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MediaSelectorValue {
|
export interface MediaSelectorValue {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user