Enhance TPDB metadata hydration and Plex mapping fidelity - #2
Open
adeze wants to merge 16 commits into
Open
Conversation
Hydrate TPDB scene entities and expand Plex metadata mapping fidelity
…or identity, external GUIDs) (#2) * Initial plan * Add Plex metadata images endpoint support * Fix legacy TPDB image field mapping * Polish image dedupe and debug logging * Add adult, Guid, and performer ID mappings * Hardcode adult flag for TPDB content * Add coverage for empty images and role ID priority --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* Initial plan * feat: include studio in plex collections mapping --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds richer TPDB→Plex enrichment (images, GUIDs, directors/collections), hydrates sparse TPDB payloads via cached lookups, and exposes a new /images metadata route.
Changes:
- Introduces scene hydration with in-memory LRU-style caching for performers and sites (metadata + match flows).
- Expands the TPDB→Plex mapper to normalize image extraction, roles, directors, collections, and Guid entries.
- Adds a new metadata images endpoint and associated tests + manifest/README updates.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_tpdb_enrichment.py | Adds unit tests covering new mapper enrichment and hydration/caching behavior. |
| tests/test_metadata_routes.py | Adds route tests for the new /library/metadata/{id}/images endpoint. |
| provider/services/metadata_service.py | Adds hydration + caching and new get_images() API. |
| provider/services/match_service.py | Adds hydration + caching during search results mapping. |
| provider/routes/metadata.py | Adds /library/metadata/{rating_key}/images route. |
| provider/routes/manifest.py | Advertises the new images feature in the manifest. |
| provider/mappers/tpdb_to_plex.py | Implements robust image extraction, collections/directors/roles GUID mapping, and image entries mapping. |
| README.md | Documents the new images endpoint. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+41
to
+43
| def _has_image(payload: dict) -> bool: | ||
| """Check if payload already includes any image-like field.""" | ||
| return any(payload.get(key) for key in ("image", "poster", "thumb", "photo", "avatar", "face")) |
Comment on lines
+41
to
+43
| def _has_image(payload: dict) -> bool: | ||
| """Check if payload already includes any image-like field.""" | ||
| return any(payload.get(key) for key in ("image", "poster", "thumb", "photo", "avatar", "face")) |
Comment on lines
+91
to
+98
| site = scene.get("site") | ||
| site_identifier = "" | ||
| if isinstance(site, dict): | ||
| site_identifier = self._first_identifier(site, ("id", "slug")) | ||
| if not site_identifier: | ||
| site_identifier = self._first_identifier(scene, ("site_id", "site_slug")) | ||
|
|
||
| hydrated_site = self._get_cached_site(site_identifier) |
| class MetadataService: | ||
| """Service for fetching full metadata from TPDB.""" | ||
|
|
||
| _CACHE_LIMIT = 512 |
Comment on lines
+28
to
+32
| self._performer_cache: OrderedDict[str, dict | None] = OrderedDict() | ||
| self._site_cache: OrderedDict[str, dict | None] = OrderedDict() | ||
|
|
||
| @staticmethod | ||
| def _first_identifier(payload: dict, keys: tuple[str, ...]) -> str: |
| self._performer_cache[performer_identifier] = self.client.get_performer(performer_identifier) | ||
| return self._performer_cache[performer_identifier] | ||
|
|
||
| def _get_cached_site(self, site_identifier: str) -> Optional[dict]: |
| self._site_cache[site_identifier] = self.client.get_site(site_identifier) | ||
| return self._site_cache[site_identifier] | ||
|
|
||
| def _hydrate_scene(self, scene: dict) -> dict: |
Comment on lines
+148
to
+152
| try: | ||
| return map_scene_to_images(scene) | ||
| except Exception as e: | ||
| logger.error("Failed to map images for scene %s: %s", rating_key, e) | ||
| return [] |
Comment on lines
+100
to
+107
| if poster: | ||
| images["poster"] = poster | ||
| images["thumb"] = poster | ||
|
|
||
| art = _get_scene_art(scene) | ||
| if art: | ||
| images["art"] = art | ||
| images["background"] = art |
Comment on lines
+45
to
+54
| def _get_cached_performer(self, performer_identifier: str) -> Optional[dict]: | ||
| """Get performer details with lightweight in-memory cache.""" | ||
| if not performer_identifier: | ||
| return None | ||
| if performer_identifier in self._performer_cache: | ||
| self._performer_cache.move_to_end(performer_identifier) | ||
| else: | ||
| if len(self._performer_cache) >= self._CACHE_LIMIT: | ||
| self._performer_cache.popitem(last=False) | ||
| self._performer_cache[performer_identifier] = self.client.get_performer(performer_identifier) |
…r VR and other scenes (#5) * Initial plan * fix: prefer poster/background over screengrabs in image selection (especially 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 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.