1 """Xbox Media Source Implementation."""
3 from __future__
import annotations
5 from contextlib
import suppress
6 from dataclasses
import dataclass
8 from pydantic
import ValidationError
9 from xbox.webapi.api.client
import XboxLiveClient
10 from xbox.webapi.api.provider.catalog.models
import FieldsTemplate, Image
11 from xbox.webapi.api.provider.gameclips.models
import GameclipsResponse
12 from xbox.webapi.api.provider.screenshots.models
import ScreenshotResponse
13 from xbox.webapi.api.provider.smartglass.models
import InstalledPackage
25 from .browse_media
import _find_media_image
26 from .const
import DOMAIN
29 "gameclips":
"video/mp4",
30 "screenshots":
"image/png",
34 "gameclips": MediaClass.VIDEO,
35 "screenshots": MediaClass.IMAGE,
40 """Set up Xbox media source."""
41 entry = hass.config_entries.async_entries(DOMAIN)[0]
42 client = hass.data[DOMAIN][entry.entry_id][
"client"]
48 item: MediaSourceItem,
49 ) -> tuple[str, str, str]:
50 """Parse identifier."""
51 identifier = item.identifier
or ""
53 items = identifier.lstrip(
"/").split(
"~~", 2)
54 return tuple(items + start[len(items) :])
59 """Represents gameclip/screenshot media."""
68 """Provide Xbox screenshots and gameclips as media sources."""
70 name: str =
"Xbox Game Media"
72 def __init__(self, hass: HomeAssistant, client: XboxLiveClient) ->
None:
73 """Initialize Xbox source."""
76 self.hass: HomeAssistant = hass
77 self.client: XboxLiveClient = client
80 """Resolve media to a url."""
82 kind = category.split(
"#", 1)[1]
83 return PlayMedia(url, MIME_TYPE_MAP[kind])
98 """Display installed games across all consoles."""
99 apps = await self.client.smartglass.get_installed_apps()
101 game.one_store_product_id: game
102 for game
in apps.result
103 if game.is_game
and game.title_id
106 app_details = await self.client.catalog.get_products(
108 FieldsTemplate.BROWSE,
112 prod.product_id: prod.localized_properties[0].images
113 for prod
in app_details.products
119 media_class=MediaClass.DIRECTORY,
120 media_content_type=
"",
121 title=
"Xbox Game Media",
125 children_media_class=MediaClass.GAME,
129 """Fetch requested gameclip/screenshot media."""
130 title_id, _, thumbnail = title.split(
"#", 2)
131 owner, kind = category.split(
"#", 1)
133 items: list[XboxMediaItem] = []
134 with suppress(ValidationError):
135 if kind ==
"gameclips":
137 response: GameclipsResponse = (
138 await self.client.gameclips.get_recent_clips_by_xuid(
139 self.client.xuid, title_id
142 elif owner ==
"community":
143 response: GameclipsResponse = await self.client.gameclips.get_recent_community_clips_by_title_id(
152 dt_util.parse_datetime(item.date_recorded)
153 ).strftime(
"%b. %d, %Y %I:%M %p"),
154 item.thumbnails[0].uri,
155 item.game_clip_uris[0].uri,
158 for item
in response.game_clips
160 elif kind ==
"screenshots":
162 response: ScreenshotResponse = (
163 await self.client.screenshots.get_recent_screenshots_by_xuid(
164 self.client.xuid, title_id
167 elif owner ==
"community":
168 response: ScreenshotResponse = await self.client.screenshots.get_recent_community_screenshots_by_title_id(
176 or dt_util.as_local(item.date_taken).strftime(
179 item.thumbnails[0].uri,
180 item.screenshot_uris[0].uri,
183 for item
in response.screenshots
188 identifier=f
"{title}~~{category}",
189 media_class=MediaClass.DIRECTORY,
190 media_content_type=
"",
191 title=f
"{owner.title()} {kind.title()}",
195 children_media_class=MEDIA_CLASS_MAP[kind],
201 """Build individual game."""
204 if image
is not None:
205 thumbnail = image.uri
206 if thumbnail[0] ==
"/":
207 thumbnail = f
"https:{thumbnail}"
211 identifier=f
"{item.title_id}#{item.name}#{thumbnail}",
212 media_class=MediaClass.GAME,
213 media_content_type=
"",
217 children_media_class=MediaClass.DIRECTORY,
223 """Build base categories for Xbox media."""
224 _, name, thumbnail = title.split(
"#", 2)
227 identifier=f
"{title}",
228 media_class=MediaClass.GAME,
229 media_content_type=
"",
234 children_media_class=MediaClass.DIRECTORY,
238 owners = [
"my",
"community"]
239 kinds = [
"gameclips",
"screenshots"]
242 base.children.append(
245 identifier=f
"{title}~~{owner}#{kind}",
246 media_class=MediaClass.DIRECTORY,
247 media_content_type=
"",
248 title=f
"{owner.title()} {kind.title()}",
251 children_media_class=MEDIA_CLASS_MAP[kind],
259 """Build individual media item."""
260 kind = category.split(
"#", 1)[1]
263 identifier=f
"{title}~~{category}~~{item.uri}",
264 media_class=item.media_class,
265 media_content_type=MIME_TYPE_MAP[kind],
269 thumbnail=item.thumbnail,