Fix image selection to prefer promotional posters over screengrabs for VR and other scenes - #5
Conversation
…pecially for VR scenes) - Add _POSTER_KIND_SCORES, _ART_KIND_SCORES, _SCREENGRAB_SCORE_THRESHOLD constants - Add _image_kind_from_key() to classify field names/labels to image kinds - Add _collect_image_candidates() that inspects all image fields with type hints, returning (poster_score, art_score, url) tuples - Refactor _get_scene_poster() and _get_scene_art() to pick highest-scored candidate instead of blindly taking the first string found - Update extract_scene_images() to return list[dict] with multiple candidates, enabling Plex to offer alternative image selections - Update map_scene_to_images() for new list-based extract_scene_images() return - Add 10 new tests covering: VR screengrab ordering, list type hints, deduplication, multiple candidates, screengrab fallback, legacy strings
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bb57b2bfd4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for key, value in images.items(): | ||
| url = _extract_string(value) | ||
| _add(url, _image_kind_from_key(key)) |
There was a problem hiding this comment.
Handle single-object image dictionaries before iterating keys
When scene["images"] is a single image object such as {"url": "https://...", "type": "poster"}, this loop treats both url and type as image entries. The type value ("poster") is then added as a generic candidate; after the real URL is selected as the primary poster, extract_scene_images() emits the literal string "poster" as an art URL. This makes the images endpoint return an invalid image entry for that valid dict-shaped payload.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR improves TPDB → Plex image selection so VR (and similar) scenes prefer promotional posters/background art over early screengrabs/stills, and expands the images payload to include multiple high-quality alternatives for Plex’s image picker.
Changes:
- Introduces kind-based scoring and candidate collection to rank posters/art higher than screengrabs.
- Updates scene image extraction to return a prioritized list of image entries (primary poster/art first, then additional candidates).
- Adds targeted tests covering VR ordering, type-hint handling, fallback behavior, URL deduplication, and the new list-return contract.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| provider/mappers/tpdb_to_plex.py | Adds scoring + candidate collection and updates extraction/mapping to prefer posters/art over screengrabs and emit multiple image candidates. |
| tests/test_tpdb_enrichment.py | Adds test coverage for the new image ranking behavior and the updated extract_scene_images() return format. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for entry in scene_images: | ||
| image_type = entry["type"] | ||
| image_url = entry["url"] | ||
| if image_url in seen_urls: | ||
| logger.debug( |
_get_scene_poster()and_get_scene_art()blindly returned the first extractable string fromscene["images"], causing VR scenes (where the first list item is often a screengrab/still) to display frame captures instead of promotional artwork.Changes
provider/mappers/tpdb_to_plex.py_POSTER_KIND_SCORES/_ART_KIND_SCORESrank image kinds —poster/coverscore 90–100 for the poster slot;screengrab/still/frame/galleryscore 10; unknowns score 50._image_kind_from_key(key): Normalizes field names and inline type/category/label hint strings from TPDB payloads to a canonical kind._collect_image_candidates(scene): Replaces the scattered_get_first_image+_extract_stringcalls. Walks top-level fields andscene["images"](dict or list), inspectstype/category/name/labelhints on list-of-dict payloads, deduplicates URLs, and returns(poster_score, art_score, url)tuples._get_scene_poster()/_get_scene_art(): Nowmax()over scored candidates instead of taking the first string — a{"url": "...", "type": "screengrab"}item can no longer displace a later{"url": "...", "type": "poster"}.extract_scene_images(): Return type changed fromdict[str, str]tolist[dict]. Primary poster and art come first; additional candidates with score above the screengrab threshold are appended so Plex's image picker can surface alternatives.map_scene_to_images(): Updated to iterate the new list return fromextract_scene_images().Example — VR payload that previously selected the wrong image:
tests/test_tpdb_enrichment.py10 new test cases: VR screengrab ordering, explicit poster/background preference over stills, plain-string list fallback, top-level field beats list screengrab, multiple candidates emitted, URL deduplication, screengrab-only last-resort behaviour, legacy string fields, and
extract_scene_imageslist-format contract.