|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | | -from typing import NamedTuple |
| 5 | +from typing import TYPE_CHECKING, NamedTuple |
6 | 6 |
|
7 | 7 | from pythonxbox.api.client import XboxLiveClient |
8 | 8 | from pythonxbox.api.provider.catalog.const import HOME_APP_IDS |
@@ -42,118 +42,114 @@ class MediaTypeDetails(NamedTuple): |
42 | 42 | async def build_item_response( |
43 | 43 | client: XboxLiveClient, |
44 | 44 | device_id: str, |
45 | | - media_content_type: str, |
46 | | - media_content_id: str, |
47 | | -) -> BrowseMedia | None: |
| 45 | + media_content_type: MediaType | str | None = None, |
| 46 | + media_content_id: str | None = None, |
| 47 | +) -> BrowseMedia: |
48 | 48 | """Create response payload for the provided media query.""" |
49 | 49 | apps: InstalledPackagesList = await client.smartglass.get_installed_apps(device_id) |
50 | 50 |
|
51 | | - if not media_content_type or media_content_type == "library": |
52 | | - children: list[BrowseMedia] = [] |
53 | | - library_info = BrowseMedia( |
| 51 | + if media_content_type is not None and media_content_id is not None: |
| 52 | + app_details = await client.catalog.get_products( |
| 53 | + [ |
| 54 | + app.one_store_product_id |
| 55 | + for app in apps.result |
| 56 | + if app.content_type == media_content_id and app.one_store_product_id |
| 57 | + ], |
| 58 | + FieldsTemplate.BROWSE, |
| 59 | + ) |
| 60 | + |
| 61 | + images = { |
| 62 | + prod.product_id: prod.localized_properties[0].images |
| 63 | + for prod in app_details.products |
| 64 | + } |
| 65 | + |
| 66 | + return BrowseMedia( |
54 | 67 | media_class=MediaClass.DIRECTORY, |
55 | | - media_content_id="library", |
56 | | - media_content_type="library", |
57 | | - title="Installed Applications", |
| 68 | + media_content_id=media_content_id, |
| 69 | + media_content_type=media_content_type, |
| 70 | + title=f"{media_content_id}s", |
58 | 71 | can_play=False, |
59 | 72 | can_expand=True, |
60 | | - children=children, |
| 73 | + children=[ |
| 74 | + item_payload(app, images) |
| 75 | + for app in apps.result |
| 76 | + if app.content_type == media_content_id and app.one_store_product_id |
| 77 | + ], |
| 78 | + children_media_class=TYPE_MAP[media_content_id].cls, |
61 | 79 | ) |
62 | 80 |
|
63 | | - # Add Home |
64 | | - id_type = AlternateIdType.LEGACY_XBOX_PRODUCT_ID |
65 | | - home_catalog: CatalogResponse = ( |
66 | | - await client.catalog.get_product_from_alternate_id( |
67 | | - HOME_APP_IDS[id_type], id_type |
68 | | - ) |
69 | | - ) |
70 | | - home_thumb = _find_media_image( |
71 | | - home_catalog.products[0].localized_properties[0].images |
72 | | - ) |
73 | | - children.append( |
74 | | - BrowseMedia( |
75 | | - media_class=MediaClass.APP, |
76 | | - media_content_id="Home", |
77 | | - media_content_type=MediaType.APP, |
78 | | - title="Home", |
79 | | - can_play=True, |
80 | | - can_expand=False, |
81 | | - thumbnail=None if home_thumb is None else home_thumb.uri, |
82 | | - ) |
83 | | - ) |
| 81 | + children: list[BrowseMedia] = [] |
| 82 | + library_info = BrowseMedia( |
| 83 | + media_class=MediaClass.DIRECTORY, |
| 84 | + media_content_id="library", |
| 85 | + media_content_type="library", |
| 86 | + title="Installed Applications", |
| 87 | + can_play=False, |
| 88 | + can_expand=True, |
| 89 | + children=children, |
| 90 | + ) |
84 | 91 |
|
85 | | - content_types = sorted( |
86 | | - {app.content_type for app in apps.result if app.content_type in TYPE_MAP} |
87 | | - ) |
88 | | - children.extend( |
89 | | - BrowseMedia( |
90 | | - media_class=MediaClass.DIRECTORY, |
91 | | - media_content_id=c_type, |
92 | | - media_content_type=TYPE_MAP[c_type].type, |
93 | | - title=f"{c_type}s", |
94 | | - can_play=False, |
95 | | - can_expand=True, |
96 | | - children_media_class=TYPE_MAP[c_type].cls, |
97 | | - ) |
98 | | - for c_type in content_types |
| 92 | + # Add Home |
| 93 | + id_type = AlternateIdType.LEGACY_XBOX_PRODUCT_ID |
| 94 | + home_catalog: CatalogResponse = await client.catalog.get_product_from_alternate_id( |
| 95 | + HOME_APP_IDS[id_type], id_type |
| 96 | + ) |
| 97 | + home_thumb = _find_media_image( |
| 98 | + home_catalog.products[0].localized_properties[0].images |
| 99 | + ) |
| 100 | + children.append( |
| 101 | + BrowseMedia( |
| 102 | + media_class=MediaClass.APP, |
| 103 | + media_content_id="Home", |
| 104 | + media_content_type=MediaType.APP, |
| 105 | + title="Home", |
| 106 | + can_play=True, |
| 107 | + can_expand=False, |
| 108 | + thumbnail=home_thumb, |
99 | 109 | ) |
100 | | - |
101 | | - return library_info |
102 | | - |
103 | | - app_details = await client.catalog.get_products( |
104 | | - [ |
105 | | - app.one_store_product_id |
106 | | - for app in apps.result |
107 | | - if app.content_type == media_content_id and app.one_store_product_id |
108 | | - ], |
109 | | - FieldsTemplate.BROWSE, |
110 | 110 | ) |
111 | 111 |
|
112 | | - images = { |
113 | | - prod.product_id: prod.localized_properties[0].images |
114 | | - for prod in app_details.products |
115 | | - } |
116 | | - |
117 | | - return BrowseMedia( |
118 | | - media_class=MediaClass.DIRECTORY, |
119 | | - media_content_id=media_content_id, |
120 | | - media_content_type=media_content_type, |
121 | | - title=f"{media_content_id}s", |
122 | | - can_play=False, |
123 | | - can_expand=True, |
124 | | - children=[ |
125 | | - item_payload(app, images) |
126 | | - for app in apps.result |
127 | | - if app.content_type == media_content_id and app.one_store_product_id |
128 | | - ], |
129 | | - children_media_class=TYPE_MAP[media_content_id].cls, |
| 112 | + content_types = sorted( |
| 113 | + {app.content_type for app in apps.result if app.content_type in TYPE_MAP} |
130 | 114 | ) |
| 115 | + children.extend( |
| 116 | + BrowseMedia( |
| 117 | + media_class=MediaClass.DIRECTORY, |
| 118 | + media_content_id=c_type, |
| 119 | + media_content_type=TYPE_MAP[c_type].type, |
| 120 | + title=f"{c_type}s", |
| 121 | + can_play=False, |
| 122 | + can_expand=True, |
| 123 | + children_media_class=TYPE_MAP[c_type].cls, |
| 124 | + ) |
| 125 | + for c_type in content_types |
| 126 | + ) |
| 127 | + |
| 128 | + return library_info |
131 | 129 |
|
132 | 130 |
|
133 | 131 | def item_payload(item: InstalledPackage, images: dict[str, list[Image]]) -> BrowseMedia: |
134 | 132 | """Create response payload for a single media item.""" |
135 | | - thumbnail = None |
136 | | - image = _find_media_image(images.get(item.one_store_product_id, [])) # type: ignore[arg-type] |
137 | | - if image is not None: |
138 | | - thumbnail = image.uri |
139 | | - if thumbnail[0] == "/": |
140 | | - thumbnail = f"https:{thumbnail}" |
| 133 | + if TYPE_CHECKING: |
| 134 | + assert item.one_store_product_id |
| 135 | + assert item.name |
141 | 136 |
|
142 | 137 | return BrowseMedia( |
143 | 138 | media_class=TYPE_MAP[item.content_type].cls, |
144 | | - media_content_id=item.one_store_product_id, # type: ignore[arg-type] |
| 139 | + media_content_id=item.one_store_product_id, |
145 | 140 | media_content_type=TYPE_MAP[item.content_type].type, |
146 | | - title=item.name, # type: ignore[arg-type] |
| 141 | + title=item.name, |
147 | 142 | can_play=True, |
148 | 143 | can_expand=False, |
149 | | - thumbnail=thumbnail, |
| 144 | + thumbnail=_find_media_image(images.get(item.one_store_product_id, [])), |
150 | 145 | ) |
151 | 146 |
|
152 | 147 |
|
153 | | -def _find_media_image(images: list[Image]) -> Image | None: |
154 | | - purpose_order = ["Poster", "Tile", "Logo", "BoxArt"] |
| 148 | +def _find_media_image(images: list[Image]) -> str | None: |
| 149 | + purpose_order = ["BrandedKeyArt", "Poster", "BoxArt", "Tile"] |
155 | 150 | for purpose in purpose_order: |
156 | | - for image in images: |
157 | | - if image.image_purpose == purpose and image.width >= 300: |
158 | | - return image |
| 151 | + if match := next( |
| 152 | + (image for image in images if image.image_purpose == purpose), None |
| 153 | + ): |
| 154 | + return f"https:{match.uri}" if match.uri.startswith("/") else match.uri |
159 | 155 | return None |
0 commit comments