媒体源
media source platform 允许集成为 Home Assistant 暴露可浏览和可播放的媒体。Media sources 出现在 media browser UI 中,让用户可以浏览层级化的媒体库并在 media player 设备上播放内容。
实现媒体源
要添加 media source 支持,请在集成目录中创建 media_source.py 文件。无需更改 manifest.json——Home Assistant 通过 integration platform 机制自动发现 media_source.py 模块。
该模块必须定义一个 async_get_media_source 函数,返回你的 MediaSource 子类实例:
from homeassistant.components.media_player import BrowseError, MediaClass, MediaType
from homeassistant.components.media_source import (
BrowseMediaSource,
MediaSource,
MediaSourceItem,
PlayMedia,
Unresolvable,
)
from homeassistant.core import HomeAssistant
from .const import DOMAIN
async def async_get_media_source(hass: HomeAssistant) -> MyMediaSource:
"""Set up my media source."""
return MyMediaSource(hass)
class MyMediaSource(MediaSource):
"""Provide media from my integration."""
name = "My Service"
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the media source."""
super().__init__(DOMAIN)
self.hass = hass
async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
"""Resolve a media item to a playable URL."""
...
async def async_browse_media(self, item: MediaSourceItem) -> BrowseMediaSource:
"""Browse media."""
...
你的 media source 必须继承 MediaSource 并实现 async_resolve_media 和 async_browse_media。可以可选地实现 async_search_media 以允许用户搜索你的媒体。
将类属性 name 设置为你 source 的人类可读名称。如果未设置,则默认为集成 domain。
浏览媒体
async_browse_media 方法接收一个 MediaSourceItem 并必须返回 BrowseMediaSource 树。当 item.identifier 为空时,返回媒体层级的根。对于非空 identifiers,返回该项目下的子项。
async def async_browse_media(self, item: MediaSourceItem) -> BrowseMediaSource:
"""Browse media."""
if item.identifier:
raise BrowseError("Unknown item")
children = [
BrowseMediaSource(
domain=DOMAIN,
identifier=entity.entity_id,
media_class=MediaClass.VIDEO,
media_content_type=entity.content_type,
title=entity.name,
thumbnail=f"/api/my_proxy/{entity.entity_id}",
can_play=True,
can_expand=False,
)
for entity in self.hass.data[DATA_COMPONENT].entities
]
return BrowseMediaSource(
domain=DOMAIN,
identifier=None,
media_class=MediaClass.APP,
media_content_type="",
title="My Service",
can_play=False,
can_expand=True,
children_media_class=MediaClass.VIDEO,
children=children,
)
BrowseMediaSource 扩展了 media player 的 BrowseMedia,并自动从 domain 和 identifier 构造 media_content_id。
分层浏览
对于深层媒体层级,使用 identifier 编码路径。一种常见的模式是在 identifier 内使用 / 作为分隔符:
async def async_browse_media(self, item: MediaSourceItem) -> BrowseMediaSource:
"""Browse media."""
category, _, sub_id = (item.identifier or "").partition("/")
if category == "albums" and sub_id:
return await self._async_browse_album(sub_id)
if category == "albums":
return await self._async_browse_albums()
# 根
return BrowseMediaSource(
domain=DOMAIN,
identifier=None,
media_class=MediaClass.APP,
media_content_type="",
title="My Service",
can_play=False,
can_expand=True,
children=[
BrowseMediaSource(
domain=DOMAIN,
identifier="albums",
media_class=MediaClass.DIRECTORY,
media_content_type=MediaType.MUSIC,
title="Albums",
can_play=False,
can_expand=True,
),
],
)
搜索媒体
搜索是可选的。实现 async_search_media 以允许用户从 media browser 搜索你的 media source。它接收正在搜索的 MediaSourceItem(搜索开始的位置)和一个 SearchMediaQuery,并返回 SearchMedia 结果:
from homeassistant.components.media_player import SearchMedia, SearchMediaQuery
async def async_search_media(
self, item: MediaSourceItem, query: SearchMediaQuery
) -> SearchMedia:
"""Search media."""
tracks = await self.api.search(query.search_query)
results = [
BrowseMediaSource(
domain=DOMAIN,
identifier=track.id,
media_class=MediaClass.MUSIC,
media_content_type=track.mime_type,
title=track.title,
can_play=True,
can_expand=False,
)
for track in tracks
]
return SearchMedia(result=results)
item 参数告诉你搜索从哪里开始,因此你可以将结果范围限定为该部分层级。当 query.media_filter_classes 被设置时,仅返回 media_class 在列表中的项目。
传递给 async_search_media 的 SearchMediaQuery 具有以下属性:
SearchMedia 包装匹配项目列表。
宣传搜索支持
media browser 仅为宣传了搜索的项目显示搜索栏。将 can_search=True 设置在 async_browse_media() 返回的 BrowseMediaSource 项目上,以在浏览时支持搜索:
async def async_browse_media(self, item: MediaSourceItem) -> BrowseMediaSource:
"""Browse media."""
return BrowseMediaSource(
domain=DOMAIN,
identifier=item.identifier,
media_class=MediaClass.APP,
media_content_type="",
title="My Service",
can_play=False,
can_expand=True,
can_search=True,
children=[BrowseMediaSource(...), BrowseMediaSource(...)],
)
如果只想在特定路径启用搜索支持,可以在浏览时基于当前 MediaSourceItem(通常是可浏览的目录)将 can_search=True 设置在 async_browse_media() 返回的 BrowseMediaSource 项目上:
async def async_browse_media(self, item: MediaSourceItem) -> BrowseMediaSource:
"""Browse media."""
can_search = False
# 根节点的 item.identifier 将为 None
if item.identifier:
can_search = is_searchable(item.identifier)
return BrowseMediaSource(
domain=DOMAIN,
identifier=item.identifier,
media_class=MediaClass.APP,
media_content_type="",
title="My Service",
can_play=False,
can_expand=True,
can_search=can_search,
children=[BrowseMediaSource(...), BrowseMediaSource(...)],
)
宣传搜索过滤器
要让用户缩小搜索范围,在可搜索项目上将 search_media_classes 设置为该位置有意义的 MediaClass 值列表。media browser 将这些作为过滤器选项提供,用户选择的 class 作为 SearchMediaQuery 中的 media_filter_classes 传回。在 async_search_media 中遵循该列表,仅返回 media_class 被包含的项目:
from homeassistant.components.media_player import MediaClass
async def async_browse_media(self, item: MediaSourceItem) -> BrowseMediaSource:
"""Browse media."""
return BrowseMediaSource(
domain=DOMAIN,
identifier=item.identifier,
media_class=MediaClass.APP,
media_content_type="",
title="My Service",
can_play=False,
can_expand=True,
can_search=True,
search_media_classes=[MediaClass.ALBUM, MediaClass.ARTIST, MediaClass.TRACK],
children=[BrowseMediaSource(...), BrowseMediaSource(...)],
)
解析媒体
async_resolve_media 方法将媒体项目解析为可播放 URL。它接收一个 MediaSourceItem 并返回 PlayMedia 实例:
async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
"""Resolve a media item to a playable URL."""
track = await self.api.get_track(item.identifier)
if not track:
raise Unresolvable(f"Could not resolve: {item.identifier}")
return PlayMedia(track.stream_url, track.mime_type)
传递给这两个方法的 MediaSourceItem 具有以下属性:
URI 方案
Media sources 使用 media-source:// URI scheme:
media-source://domain/identifier
例如:
media-source://radio_browser/popular — radio_browser 的 "popular" 类别
media-source://tts/message — TTS 消息
使用 generate_media_source_id helper 构造 URI:
from homeassistant.components.media_source import generate_media_source_id
media_id = generate_media_source_id(DOMAIN, "my_track_123")
# 返回: "media-source://my_integration/my_track_123"
使用 is_media_source_id 检查字符串是否为有效的 media source URI:
from homeassistant.components.media_source import is_media_source_id
if is_media_source_id(media_content_id):
# 作为 media source 处理
...
错误处理
出错时抛出适当的异常:
两个异常都支持 translations:
raise Unresolvable(
translation_domain=DOMAIN,
translation_key="item_not_found",
translation_placeholders={"item": item.identifier},
)
在其他集成中使用媒体源
其他集成(如 media players)可以使用 media source helpers 浏览和解析媒体:
from homeassistant.components.media_player import SearchMediaQuery
from homeassistant.components.media_source import (
async_browse_media,
async_resolve_media,
async_search_media,
is_media_source_id,
)
# 浏览 media sources
result = await async_browse_media(hass, "media-source://my_domain")
# 搜索 media source
search_result = await async_search_media(
hass,
"media-source://my_domain",
SearchMediaQuery(search_query="jazz"),
)
# 将媒体项目解析为可播放 URL
play_media = await async_resolve_media(
hass,
"media-source://my_domain/track_123",
target_media_player="media_player.living_room",
)