Summary
During a bulk import (e.g. from Trakt), Ryot fetches all images for every media item from TMDB and stores them as remote URLs in metadata.assets.remote_images. On a library of ~900 items this results in ~200,000 stored image URLs — an average of ~200-700 per title (posters, backdrops, logos, profiles).
Opening any media details page makes the frontend load hundreds of images from TMDB at once, and quickly switching between titles causes severe UI lag / freezing (especially on mobile connections).
Observed behavior
metadataDetails returns 200-700 remoteImages URLs per item (e.g. The Batman 2026: 708, Disclosure Day: 412, Cape Fear: 329).
- GraphQL response itself is fast (~15-500 ms), but the browser then downloads hundreds of images per page view.
- On mobile data, a poster is first shown in English, then swapped to a localized (Russian) one — because the language of each image is not stored.
Root cause (from code)
In crates/providers/tmdb/src/base.rs, save_all_images() fetches GET /{media_type}/{id}/images and pushes every poster, backdrop, logo and profile URL into the assets:
if let Some(imgs) = new_images.posters { for image in imgs { images.push(self.get_image_url(image.file_path)); } }
if let Some(imgs) = new_images.backdrops { for image in imgs { images.push(self.get_image_url(image.file_path)); } }
if let Some(imgs) = new_images.logos { for image in imgs { images.push(self.get_image_url(image.file_path)); } }
if let Some(imgs) = new_images.profiles { for image in imgs { images.push(self.get_image_url(image.file_path)); } }
Additionally, in crates/providers/tmdb/src/models.rs, TmdbImage only keeps file_path — the iso_639_1 language field returned by TMDB is discarded, so Ryot has no way to prefer a localized poster.
Feature request
- Limit stored images per media item (configurable, e.g.
max_images_per_media: 5) — keep the primary poster + backdrop, drop the rest.
- Preserve image language (
iso_639_1) and let users prefer posters matching their languages preference (so Russian users get Russian posters immediately, without a flash of the English one).
- Optional: store images locally — allow downloading posters/backdrops to local storage (e.g. via the existing S3/file-storage integration) instead of hot-linking remote URLs. Benefits:
- No dependency on TMDB's CDN availability (images still show if TMDB is slow or down).
- Much faster loading for self-hosted instances, especially on LAN (no repeated downloads from the internet per page view).
- Reduced external bandwidth usage.
- Consistent behavior for users on slow/metered mobile connections.
- Optionally: skip re-fetching images during background metadata updates if images already exist (avoid re-hydrating hundreds of URLs on every nightly update for monitored items).
Impact
- UI responsiveness on libraries of a few hundred items.
- Mobile data usage (each page view currently triggers hundreds of image downloads).
- Poster language consistency for non-English users.
- Self-hosted instances would benefit from offline/instant image loading.
Thanks for the great project!
Summary
During a bulk import (e.g. from Trakt), Ryot fetches all images for every media item from TMDB and stores them as remote URLs in
metadata.assets.remote_images. On a library of ~900 items this results in ~200,000 stored image URLs — an average of ~200-700 per title (posters, backdrops, logos, profiles).Opening any media details page makes the frontend load hundreds of images from TMDB at once, and quickly switching between titles causes severe UI lag / freezing (especially on mobile connections).
Observed behavior
metadataDetailsreturns 200-700remoteImagesURLs per item (e.g. The Batman 2026: 708, Disclosure Day: 412, Cape Fear: 329).Root cause (from code)
In
crates/providers/tmdb/src/base.rs,save_all_images()fetchesGET /{media_type}/{id}/imagesand pushes every poster, backdrop, logo and profile URL into the assets:Additionally, in
crates/providers/tmdb/src/models.rs,TmdbImageonly keepsfile_path— theiso_639_1language field returned by TMDB is discarded, so Ryot has no way to prefer a localized poster.Feature request
max_images_per_media: 5) — keep the primary poster + backdrop, drop the rest.iso_639_1) and let users prefer posters matching theirlanguagespreference (so Russian users get Russian posters immediately, without a flash of the English one).Impact
Thanks for the great project!