Fixed 2 problems : https url images and infinite cycle update of image url#114
Fixed 2 problems : https url images and infinite cycle update of image url#114albaintor wants to merge 5 commits into
Conversation
…one and url, and handle of https url images (converted to binary data before sending it to the remote)
…websocket connection crash
|
Another modification that I had to make : I introduced an image resize processing if width or height exceed 900px. Above this value, it makes the websocket connection crash. This will be applied to all image sent as a buffer binary data to the remote (including app icons but unlikely concerned) |
| # The remote does not support https URL images, encode them | ||
| if self._media_image_url.lower().startswith("https"): | ||
| update[MediaAttr.MEDIA_IMAGE_URL] = await encode_image_to_data_uri(self._media_image_url) | ||
| else: | ||
| update[MediaAttr.MEDIA_IMAGE_URL] = self._media_image_url |
There was a problem hiding this comment.
https image urls are supported in the UI! I just verified it again and it works.
What's not supported are invalid and self-signed certificates.
Does httpx.AsyncClient(verify=False) in src/external_metadata.py disable certificate validation?
Base64 embedding is quite an overhead and should be avoided when possible. The image now also gets resized twice, in the integration and again in the UI.
Large base64 data can create WebSocket issues if the image is too large. The entity change events are distributed to every connected client using the Core-API, for example the web-configurator.
If there are problematic URLs that can't be processed in the UI then it should be fixed there, solving it for every integration. I'll check about certificate checks in the UI if they could be disabled with a user option, even though I have quite a strong opinion that one should only use valid certificates :-)
There was a problem hiding this comment.
At start I thought that https wasn't supported but Marton said it was and this narrowed this down to the certificate (self signed). But this is not possible to determine if the https is or not self signed
Yes httpx.AsyncClient(verify=False) disables SSL verification and this change is necessary for some apps such as Plex hosting media files locally (on a NAS...) and exposing https image urls.
But if you disable it at the UI/core level it will indeed work for all. I just assumed that the risk was lower to make it only for AndroidTV
There was a problem hiding this comment.
To test if https works is easy: hardcode a https image url from a public website in tv.py, for example:
update[MediaAttr.MEDIA_IMAGE_URL] = "https://brands.home-assistant.io/_/conversation/logo@2x.png"
There might still be some occasional hickups in the UI showing the loading dot. We'll check that again too.
httpx.AsyncClient makes it too easy disabling certificate checks. No wonder we are still using self signed certificates :-( Let's Encrypt has been around for a while... /rant off
zehnm
left a comment
There was a problem hiding this comment.
A pull request should only address one thing. Please create a separate PR for this issue:
Otherwise there was a bug in the existing code : the media_image_url was erased and re-written at each chromecast update.
The image download and base64 embedding needs more analysis. I see it only as a last resort, if it can't be fixed properly.
An URL rewrite for Plex with changed image size parameters would be a much better solution: put the heavy lifting to Plex, delivering a properly sized artwork. 1800px is just insane for a small screen and embedded device.
I just created #115 for this
I agree, still playing with plex image url sound like a dirty hack. With regex we could parse width and height arguments in the URL and modify them. I don't think we can change these settings from Plex (at least I didn't see such settings in Plex so far). Finally the plan would be to disable SSL check on the core ? |
Plex finally published an official API documentation. I need to check if they have the artwork retrieval in there. If the endpoint and query parameters are documented, it's no hack but a tailored solution for Plex. I'll keep this PR open until further investigation. |
|
Ok I will digg this around today. Just another thing : as you know the media info we received here is sent through chromecast protocol not by Plex directly so I don't know if one could analyze the media data to :
I will check after this today |
|
FYI : sample data of Chromecast event from Plex I think this work should (is already?) be done by the Plex integration https://github.com/JackJPowell/uc-intg-plex Otherwise, what could be done is parsing the URL for "width=" or "height=" and replace them {
"metadata_type": 1,
"title": "",
"series_title": null,
"season": null,
"episode": null,
"artist": null,
"album_name": null,
"album_artist": null,
"track": null,
"subtitle_tracks": [
{
"trackId": 1,
"type": "TEXT",
"trackContentId": "contentId",
"trackContentType": "contentType",
"language": "en",
"customData": {}
}
],
"images": [
MediaImage(url="https://192.168.1.10:32400/photo/:/transcode?height=1800&machineIdentifier=xxx&quality=90&url=http%3A%2F%2F127.0.0.1%3A32400%2Flibrary%2Fmetadata%2F20%2Fthumb%2F1764553003&width=1200&X-Plex-Token=badeYTYz1Eny1htqw_pq",
"height"=0,
"width"=0)
],
"supports_pause": true,
"supports_seek": true,
"supports_stream_volume": true,
"supports_stream_mute": true,
"supports_skip_forward": false,
"supports_skip_backward": false,
"current_time": 0.002,
"content_id": "server://778a48e3267b0e1d36d35e73b7a94ab38a782902/com.plexapp.plugins.library/library/metadata/20",
"content_type": "",
"duration": 0,
"stream_type": "BUFFERED",
"idle_reason": null,
"media_session_id": 2,
"playback_rate": 1,
"player_state": "BUFFERING",
"supported_media_commands": 143,
"volume_level": 0,
"volume_muted": false,
"media_custom_data": {
"vzbVersion": "4.2.4",
"vzbSenderOS": "android_tv",
"guid": "server://778a48e3267b0e1d36d35e73b7a94ab38a782902/com.plexapp.plugins.library/library/metadata/20",
"drmInfo": {
"drmType": "",
"drmLicenseURL": "",
"drmCustomData": ""
},
"customStreamInfo": {},
"customMetadata": {}
},
"media_metadata": {
"metadataType": 1,
"images": [
{
"url": "https://192.168.1.10:32400/photo/:/transcode?height=1800&machineIdentifier=xxx&quality=90&url=http%3A%2F%2F127.0.0.1%3A32400%2Flibrary%2Fmetadata%2F20%2Fthumb%2F1764553003&width=1200&X-Plex-Token=badeYTYz1Eny1htqw_pq",
"width": 0,
"height": 0
}
],
"title": "",
"subtitle": ""
},
"current_subtitle_tracks": [],
"last_updated": datetime.datetime(2025,12,3,10,2,42,236232)
} |
|
It only took several hours to convince Qt in the UI app to ignore self-signed and invalid certificates (e.g. IP address or wrong hostname) and only works on the device, but not on the Mac dev-environment... We try to include it in the next update. See my comment in #113 about the Plex endpoint: it is using the official API endpoint 👍 |
Ok I read it too quickly let me check again if we can optimize this |
|
I have finished applied and testing the URL rewrite logic. I let you review, especially the max size parameter that I fixed to 900pixel (width or height). This value is used to both resize the buffer images in base64 if size exceeded and as the new width or height in the rewrite URL procedure. And yes bypassing ssl certificate check has always been a real pain in all the languages I manipulated...except Python |
| if re.search(r"/photo/:/transcode\?", url, re.IGNORECASE): | ||
| for argument in ["width", "height"]: | ||
| if value := re.search(rf"{argument}=(\d+)", url, re.IGNORECASE): | ||
| try: | ||
| if value and int(value.group(1)) > IMAGE_SIZE_MAX: | ||
| new_size = IMAGE_SIZE_MAX | ||
| url = re.sub(rf"{argument}=(\d+)", f"{argument}={new_size}", url, re.IGNORECASE) | ||
| except ValueError: | ||
| pass |
There was a problem hiding this comment.
Please do not use regex for modifying URLs. This is not recommended and dangerous. Too many things can go wrong.
Either use the built-in urllib.parse module or a 3rd party module like furl.
Quick AI assist:
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
# Your original URL
url = "https://192.168.1.10:32400/photo/:/transcode?height=1800&machineIdentifier=xxx&quality=90&url=http%3A%2F%2F127.0.0.1%3A32400%2Flibrary%2Fmetadata%2F20%2Fthumb%2F1764553003&width=1200&X-Plex-Token=abc"
# 1. Parse the URL into components
parsed_url = urlparse(url)
# 2. Parse the query string into a dictionary
query_dict = parse_qs(parsed_url.query, keep_blank_values=True)
# 3. Define your modification logic
IMAGE_SIZE_MAX = 900
# Convert values from list (from parse_qs) to simple string for logic
if 'height' in query_dict:
height_val = int(query_dict['height'][0])
if height_val > IMAGE_SIZE_MAX:
query_dict['height'] = [str(IMAGE_SIZE_MAX)] # Set back as a list
if 'width' in query_dict:
width_val = int(query_dict['width'][0])
if width_val > IMAGE_SIZE_MAX:
query_dict['width'] = [str(IMAGE_SIZE_MAX)] # Set back as a list
# 4. Re-encode the modified query parameters
new_query_string = urlencode(query_dict, doseq=True)
# 5. Reconstruct the full URL with the new query string
modified_url = urlunparse((
parsed_url.scheme,
parsed_url.netloc,
parsed_url.path,
parsed_url.params,
new_query_string,
parsed_url.fragment
))
print(modified_url)Warning: untested, never trust AI!
|
Closing: Plex image URL has been implemented in PR #123 Please reopen or create a new issue if the play store app image also requires resizing. |
Https url images are not handled on the remote side : so if the media image url starts with "https", it will be converted to binary image data and sent to the remote as a buffer.
Otherwise there was a bug in the existing code : the media_image_url was erased and re-written at each chromecast update. This is resolved now