Skip to content

Fixed 2 problems : https url images and infinite cycle update of image url#114

Closed
albaintor wants to merge 5 commits into
unfoldedcircle:mainfrom
albaintor:chromecast_plex
Closed

Fixed 2 problems : https url images and infinite cycle update of image url#114
albaintor wants to merge 5 commits into
unfoldedcircle:mainfrom
albaintor:chromecast_plex

Conversation

@albaintor
Copy link
Copy Markdown
Contributor

@albaintor albaintor commented Nov 30, 2025

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

…one and url, and handle of https url images (converted to binary data before sending it to the remote)
@albaintor
Copy link
Copy Markdown
Contributor Author

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)

Comment thread src/tv.py Outdated
Comment on lines +922 to +926
# 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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 :-)

Copy link
Copy Markdown
Contributor Author

@albaintor albaintor Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

@zehnm zehnm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@albaintor
Copy link
Copy Markdown
Contributor Author

albaintor commented Dec 2, 2025

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.

I just created #115 for this
Should I remove this #115 changes from this PR ? Not sure about how the merge will work.
For now #114 includes #115 but I have no doubt that #115 will be merged :-)

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 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 ?
About your thoughts about image size : I agree this is too large for a small screen (what is the resolution by the way ?) but this data is sent occasionally (only when it changes so when starting a movie or switching to another music album)

@albaintor albaintor requested a review from zehnm December 2, 2025 19:49
@zehnm
Copy link
Copy Markdown
Contributor

zehnm commented Dec 2, 2025

I agree, still playing with plex image url sound like a dirty hack.

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.

@albaintor
Copy link
Copy Markdown
Contributor Author

albaintor commented Dec 3, 2025

Ok I will digg this around today.
In the meantime : something should be done on the core/ui to prevent hangs because without any modifications here if an https self-signed url is sent the remote doens't like it and freezes.

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 :

  1. Check if this is Plex behind
  2. If Plex extract the Plex server URL (which can be local like a NAS or Plex servers) and build an API call

I will check after this today

@albaintor
Copy link
Copy Markdown
Contributor Author

albaintor commented Dec 3, 2025

FYI : sample data of Chromecast event from Plex
This is not apparent that this is Plex that sendss this mediadata. The only way is to test the current app.
Next, the API are not accessible without registration, see https://plexapi.dev/api-reference/activities/get-all-activities?playground=open (X-Plex-Token)

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)
}

@zehnm
Copy link
Copy Markdown
Contributor

zehnm commented Dec 3, 2025

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...
The core distributes the entity change events, and the UI downloads the image from the provided URL.

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 👍

@albaintor
Copy link
Copy Markdown
Contributor Author

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... The core distributes the entity change events, and the UI downloads the image from the provided URL.

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

@albaintor
Copy link
Copy Markdown
Contributor Author

albaintor commented Dec 3, 2025

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

Comment thread src/external_metadata.py
Comment on lines +253 to +261
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
Copy link
Copy Markdown
Contributor

@zehnm zehnm Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added Plex URL rewrite in PR #123

@zehnm
Copy link
Copy Markdown
Contributor

zehnm commented Dec 22, 2025

Closing: Plex image URL has been implemented in PR #123
Disabling certificate validation for external app images from play store is not require.

Please reopen or create a new issue if the play store app image also requires resizing.

@zehnm zehnm closed this Dec 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash with some artwork urls and Chromecast enabled Optimize Plex artwork image loading Failed to load artwork image due to certificate error

2 participants