diff --git a/homeassistant/components/xbox/media_source.py b/homeassistant/components/xbox/media_source.py deleted file mode 100644 index 263aed3504ca5..0000000000000 --- a/homeassistant/components/xbox/media_source.py +++ /dev/null @@ -1,271 +0,0 @@ -"""Xbox Media Source Implementation.""" - -from __future__ import annotations - -from contextlib import suppress -from dataclasses import dataclass - -from pydantic import ValidationError -from xbox.webapi.api.client import XboxLiveClient -from xbox.webapi.api.provider.catalog.models import FieldsTemplate, Image -from xbox.webapi.api.provider.gameclips.models import GameclipsResponse -from xbox.webapi.api.provider.screenshots.models import ScreenshotResponse -from xbox.webapi.api.provider.smartglass.models import InstalledPackage - -from homeassistant.components.media_player import MediaClass -from homeassistant.components.media_source import ( - BrowseMediaSource, - MediaSource, - MediaSourceItem, - PlayMedia, -) -from homeassistant.core import HomeAssistant, callback -from homeassistant.util import dt as dt_util - -from .browse_media import _find_media_image -from .const import DOMAIN -from .coordinator import XboxConfigEntry - -MIME_TYPE_MAP = { - "gameclips": "video/mp4", - "screenshots": "image/png", -} - -MEDIA_CLASS_MAP = { - "gameclips": MediaClass.VIDEO, - "screenshots": MediaClass.IMAGE, -} - - -async def async_get_media_source(hass: HomeAssistant): - """Set up Xbox media source.""" - entry: XboxConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] - client = entry.runtime_data.client - return XboxSource(hass, client) - - -@callback -def async_parse_identifier( - item: MediaSourceItem, -) -> tuple[str, str, str]: - """Parse identifier.""" - identifier = item.identifier or "" - start = ["", "", ""] - items = identifier.lstrip("/").split("~~", 2) - return tuple(items + start[len(items) :]) # type: ignore[return-value] - - -@dataclass -class XboxMediaItem: - """Represents gameclip/screenshot media.""" - - caption: str - thumbnail: str - uri: str - media_class: str - - -class XboxSource(MediaSource): - """Provide Xbox screenshots and gameclips as media sources.""" - - name: str = "Xbox Game Media" - - def __init__(self, hass: HomeAssistant, client: XboxLiveClient) -> None: - """Initialize Xbox source.""" - super().__init__(DOMAIN) - - self.hass: HomeAssistant = hass - self.client: XboxLiveClient = client - - async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia: - """Resolve media to a url.""" - _, category, url = async_parse_identifier(item) - kind = category.split("#", 1)[1] - return PlayMedia(url, MIME_TYPE_MAP[kind]) - - async def async_browse_media(self, item: MediaSourceItem) -> BrowseMediaSource: - """Return media.""" - title, category, _ = async_parse_identifier(item) - - if not title: - return await self._build_game_library() - - if not category: - return _build_categories(title) - - return await self._build_media_items(title, category) - - async def _build_game_library(self): - """Display installed games across all consoles.""" - apps = await self.client.smartglass.get_installed_apps() - games = { - game.one_store_product_id: game - for game in apps.result - if game.is_game and game.title_id - } - - app_details = await self.client.catalog.get_products( - games.keys(), - FieldsTemplate.BROWSE, - ) - - images = { - prod.product_id: prod.localized_properties[0].images - for prod in app_details.products - } - - return BrowseMediaSource( - domain=DOMAIN, - identifier="", - media_class=MediaClass.DIRECTORY, - media_content_type="", - title="Xbox Game Media", - can_play=False, - can_expand=True, - children=[_build_game_item(game, images) for game in games.values()], - children_media_class=MediaClass.GAME, - ) - - async def _build_media_items(self, title, category): - """Fetch requested gameclip/screenshot media.""" - title_id, _, thumbnail = title.split("#", 2) - owner, kind = category.split("#", 1) - - items: list[XboxMediaItem] = [] - with suppress(ValidationError): # Unexpected API response - if kind == "gameclips": - if owner == "my": - response: GameclipsResponse = ( - await self.client.gameclips.get_recent_clips_by_xuid( - self.client.xuid, title_id - ) - ) - elif owner == "community": - response: GameclipsResponse = await self.client.gameclips.get_recent_community_clips_by_title_id( - title_id - ) - else: - return None - items = [ - XboxMediaItem( - item.user_caption - or dt_util.as_local( - dt_util.parse_datetime(item.date_recorded) - ).strftime("%b. %d, %Y %I:%M %p"), - item.thumbnails[0].uri, - item.game_clip_uris[0].uri, - MediaClass.VIDEO, - ) - for item in response.game_clips - ] - elif kind == "screenshots": - if owner == "my": - response: ScreenshotResponse = ( - await self.client.screenshots.get_recent_screenshots_by_xuid( - self.client.xuid, title_id - ) - ) - elif owner == "community": - response: ScreenshotResponse = await self.client.screenshots.get_recent_community_screenshots_by_title_id( - title_id - ) - else: - return None - items = [ - XboxMediaItem( - item.user_caption - or dt_util.as_local(item.date_taken).strftime( - "%b. %d, %Y %I:%M%p" - ), - item.thumbnails[0].uri, - item.screenshot_uris[0].uri, - MediaClass.IMAGE, - ) - for item in response.screenshots - ] - - return BrowseMediaSource( - domain=DOMAIN, - identifier=f"{title}~~{category}", - media_class=MediaClass.DIRECTORY, - media_content_type="", - title=f"{owner.title()} {kind.title()}", - can_play=False, - can_expand=True, - children=[_build_media_item(title, category, item) for item in items], - children_media_class=MEDIA_CLASS_MAP[kind], - thumbnail=thumbnail, - ) - - -def _build_game_item(item: InstalledPackage, images: dict[str, list[Image]]): - """Build individual game.""" - thumbnail = "" - image = _find_media_image(images.get(item.one_store_product_id, [])) - if image is not None: - thumbnail = image.uri - if thumbnail[0] == "/": - thumbnail = f"https:{thumbnail}" - - return BrowseMediaSource( - domain=DOMAIN, - identifier=f"{item.title_id}#{item.name}#{thumbnail}", - media_class=MediaClass.GAME, - media_content_type="", - title=item.name, - can_play=False, - can_expand=True, - children_media_class=MediaClass.DIRECTORY, - thumbnail=thumbnail, - ) - - -def _build_categories(title): - """Build base categories for Xbox media.""" - _, name, thumbnail = title.split("#", 2) - base = BrowseMediaSource( - domain=DOMAIN, - identifier=f"{title}", - media_class=MediaClass.GAME, - media_content_type="", - title=name, - can_play=False, - can_expand=True, - children=[], - children_media_class=MediaClass.DIRECTORY, - thumbnail=thumbnail, - ) - - owners = ["my", "community"] - kinds = ["gameclips", "screenshots"] - for owner in owners: - for kind in kinds: - base.children.append( - BrowseMediaSource( - domain=DOMAIN, - identifier=f"{title}~~{owner}#{kind}", - media_class=MediaClass.DIRECTORY, - media_content_type="", - title=f"{owner.title()} {kind.title()}", - can_play=False, - can_expand=True, - children_media_class=MEDIA_CLASS_MAP[kind], - ) - ) - - return base - - -def _build_media_item(title: str, category: str, item: XboxMediaItem): - """Build individual media item.""" - kind = category.split("#", 1)[1] - return BrowseMediaSource( - domain=DOMAIN, - identifier=f"{title}~~{category}~~{item.uri}", - media_class=item.media_class, - media_content_type=MIME_TYPE_MAP[kind], - title=item.caption, - can_play=True, - can_expand=False, - thumbnail=item.thumbnail, - )