Hydrate TPDB scene entities and expand Plex metadata mapping fidelity - #1
Merged
Conversation
Copilot
AI
changed the title
[WIP] Enhance TPDB-backed Plex metadata proxy with image hydration and director support
Hydrate TPDB scene entities and expand Plex metadata mapping fidelity
Jun 4, 2026
adeze
marked this pull request as ready for review
June 4, 2026 06:45
There was a problem hiding this comment.
Pull request overview
This PR enhances the TPDB-backed Plex metadata provider by enriching (hydrating) sparse scene payloads with cached performer/site lookups and by expanding TPDB→Plex field mapping (notably image fallbacks, Director, and Collection mapping) to produce more complete metadata responses.
Changes:
- Added lightweight in-memory caches and scene hydration logic for performers and sites in both metadata and match services.
- Centralized defensive image extraction and improved poster/art selection fallbacks; added Director and Collection/series/franchise mapping.
- Added unit tests covering image fallback behavior, director/collection mapping, and hydration caching behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
provider/services/metadata_service.py |
Adds performer/site hydration with in-memory caching prior to mapping full metadata. |
provider/services/match_service.py |
Adds hydration (and caching) during match search flow before mapping match results. |
provider/mappers/tpdb_to_plex.py |
Implements robust image extraction + poster/art selection and maps Director/Collection fields. |
tests/test_tpdb_enrichment.py |
Adds unit tests validating new mapping and hydration/caching behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+45
to
+55
| 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) | ||
| return self._performer_cache[performer_identifier] |
Comment on lines
+57
to
+67
| def _get_cached_site(self, site_identifier: str) -> Optional[dict]: | ||
| """Get site details with lightweight in-memory cache.""" | ||
| if not site_identifier: | ||
| return None | ||
| if site_identifier in self._site_cache: | ||
| self._site_cache.move_to_end(site_identifier) | ||
| else: | ||
| if len(self._site_cache) >= self._CACHE_LIMIT: | ||
| self._site_cache.popitem(last=False) | ||
| self._site_cache[site_identifier] = self.client.get_site(site_identifier) | ||
| return self._site_cache[site_identifier] |
Comment on lines
+45
to
+55
| 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) | ||
| return self._performer_cache[performer_identifier] |
Comment on lines
+57
to
+67
| def _get_cached_site(self, site_identifier: str) -> Optional[dict]: | ||
| """Get site details with lightweight in-memory cache.""" | ||
| if not site_identifier: | ||
| return None | ||
| if site_identifier in self._site_cache: | ||
| self._site_cache.move_to_end(site_identifier) | ||
| else: | ||
| if len(self._site_cache) >= self._CACHE_LIMIT: | ||
| self._site_cache.popitem(last=False) | ||
| self._site_cache[site_identifier] = self.client.get_site(site_identifier) | ||
| return self._site_cache[site_identifier] |
Comment on lines
131
to
134
| for i, scene in enumerate(results): | ||
| try: | ||
| scene = self._hydrate_scene(scene) | ||
| # Decrease score for each subsequent result |
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.
This PR makes TPDB-backed metadata responses more complete by hydrating sparse scene payloads and broadening Plex field mapping. It improves performer imagery, studio normalization, image fallback quality, and adds director/collection support while preserving existing response shape when enrichment data is absent.
Performer hydration (with lightweight cache)
/performers/{id|slug}.Studio/site hydration (with lightweight cache)
/sites/{id|slug}enrichment in match + metadata flows.siteobjects using hydrated site data.siteand preservessite_hydratedfor mapper compatibility.Defensive multi-image fallback selection
Director mapping
director/directorspayload variants into PlexDirectorentries.Collection/series/franchise mapping
collection(s),series, andfranchise(s)variants into PlexCollectionentries.Original prompt
Enhance the repository
adeze/plex-tpdb-scannerto make the TPDB-backed Plex metadata proxy more complete.Implement the following improvements:
/performers/{id}via the existing TPDB client and use the returned details to populate richer role metadata.Directorstructure.Collectionstructure.Implementation guidance:
adeze/plex-tpdb-scannerrequests-based TPDB client.Files likely involved:
metadata_tool/api.pyprovider/services/metadata_service.pyprovider/services/match_service.pyprovider/mappers/tpdb_to_plex.pyExpected outcome:
The following is the prior conversation context from the user's chat exploration (may be truncated):
User: are there any enhancements we can make to request more details from thepdb api https://api.theporndb.net/docs that would make this metadata proxy more complete? actress image etc?
Assistant: Yes — probably. The biggest gap in your proxy right now is not the basic TPDB fetch itself, but that you only map a fairly small subset of the scene payload into Plex metadata. Your client already calls
GET /scenes/{id}and also has helper methods forGET /performers/{id}andGET /sites/{id}, but the mapper only emits title, summary, poster, background, studio, date, duration, performers, and tags. (api.theporndb.net)From the repo, the current metadata mapping is here:
and the client already exposes richer lookup points:
So the enhancement path I’d recommend is:
1. Enrich performer data
Yes, actress/performer images are the most obvious win. You already map performer thumbs when they are present inline on the scene response, but you do not curr...
This pull request was created from Copilot chat.