Skip to content

Commit 64b824b

Browse files
authored
refactor(server-media): share rating key cache helper (#159)
Include version bump to 2.0.20 to align packaging metadata with caching updates.
1 parent 59b9d09 commit 64b824b

File tree

6 files changed

+79
-18
lines changed

6 files changed

+79
-18
lines changed

docker/pyproject.deps.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "mcp-plex"
3-
version = "2.0.19"
3+
version = "2.0.20"
44
requires-python = ">=3.11,<3.13"
55
dependencies = [
66
"fastmcp>=2.11.2",

mcp_plex/server/media.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,36 @@ def _collect_cache_keys(
135135
return cache_keys
136136

137137

138+
def _cache_media_artwork(
139+
cache: "MediaCache", cache_keys: set[str], plex_info: PlexMediaMetadata
140+
) -> None:
141+
"""Persist poster and background URLs for each cache key."""
142+
143+
if not cache_keys:
144+
return
145+
146+
thumb = plex_info.get("thumb")
147+
if isinstance(thumb, str) and thumb:
148+
for cache_key in cache_keys:
149+
cache.set_poster(cache_key, thumb)
150+
151+
art = plex_info.get("art")
152+
if isinstance(art, str) and art:
153+
for cache_key in cache_keys:
154+
cache.set_background(cache_key, art)
155+
156+
157+
def _ensure_rating_key_cached(
158+
cache_keys: set[str], plex_info: PlexMediaMetadata
159+
) -> set[str]:
160+
"""Guarantee the Plex rating key is part of the cache key set."""
161+
162+
rating_key = _normalize_identifier(plex_info.get("rating_key"))
163+
if rating_key:
164+
cache_keys.add(rating_key)
165+
return cache_keys
166+
167+
138168
def _identifier_matches_payload(
139169
identifier: str | int | float | None, payload: AggregatedMediaItem
140170
) -> bool:
@@ -183,17 +213,12 @@ async def _get_media_data(server: "PlexServer", identifier: str) -> AggregatedMe
183213

184214
plex_data = _extract_plex_metadata(data)
185215
cache_keys = _collect_cache_keys(data, plex_data, identifier)
186-
rating_key = _normalize_identifier(plex_data.get("rating_key"))
216+
cache_keys = _ensure_rating_key_cached(cache_keys, plex_data)
217+
187218
for cache_key in cache_keys:
188219
server.cache.set_payload(cache_key, cast(dict[str, JSONValue], payload))
189220

190-
if rating_key:
191-
thumb = plex_data.get("thumb")
192-
if isinstance(thumb, str) and thumb:
193-
server.cache.set_poster(rating_key, thumb)
194-
art = plex_data.get("art")
195-
if isinstance(art, str) and art:
196-
server.cache.set_background(rating_key, art)
221+
_cache_media_artwork(server.cache, cache_keys, plex_data)
197222
return payload
198223

199224

mcp_plex/server/tools/media_library.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -931,9 +931,9 @@ async def media_poster(
931931
if not thumb:
932932
raise ValueError("Poster not available")
933933
thumb_str = str(thumb)
934-
rating_key = media_helpers._normalize_identifier(plex_info.get("rating_key"))
935-
if rating_key:
936-
server.cache.set_poster(rating_key, thumb_str)
934+
cache_keys = media_helpers._collect_cache_keys(data, plex_info, identifier)
935+
cache_keys = media_helpers._ensure_rating_key_cached(cache_keys, plex_info)
936+
media_helpers._cache_media_artwork(server.cache, cache_keys, plex_info)
937937
return thumb_str
938938

939939
@server.resource("resource://media-background/{identifier}")
@@ -957,9 +957,9 @@ async def media_background(
957957
if not art:
958958
raise ValueError("Background not available")
959959
art_str = str(art)
960-
rating_key = media_helpers._normalize_identifier(plex_info.get("rating_key"))
961-
if rating_key:
962-
server.cache.set_background(rating_key, art_str)
960+
cache_keys = media_helpers._collect_cache_keys(data, plex_info, identifier)
961+
cache_keys = media_helpers._ensure_rating_key_cached(cache_keys, plex_info)
962+
media_helpers._cache_media_artwork(server.cache, cache_keys, plex_info)
963963
return art_str
964964

965965
@server.prompt("media-info")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "mcp-plex"
7-
version = "2.0.19"
7+
version = "2.0.20"
88

99
description = "Plex-Oriented Model Context Protocol Server"
1010
requires-python = ">=3.11,<3.13"

tests/test_server_media_resources_additional.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import pytest
66

77
from mcp_plex.server import media as media_helpers
8-
from mcp_plex.server import media_background, media_poster
8+
from mcp_plex.server import media_background, media_poster, server as plex_server
99

1010

1111
def test_media_poster_raises_when_missing(monkeypatch):
@@ -32,3 +32,39 @@ async def _fake_get_media(*args, **kwargs):
3232
await media_background.fn(identifier="missing")
3333

3434
asyncio.run(_run())
35+
36+
37+
def test_media_artwork_cached_for_alternate_identifier(monkeypatch):
38+
async def _run() -> None:
39+
plex_server.cache.clear()
40+
41+
calls: list[str] = []
42+
43+
rating_key = "49915"
44+
imdb_id = "tt8367814"
45+
poster_url = "https://example.com/poster.jpg"
46+
background_url = "https://example.com/background.jpg"
47+
48+
async def _fake_get_media(server, identifier):
49+
calls.append(identifier)
50+
return {
51+
"title": "The Gentlemen",
52+
"imdb": {"id": imdb_id},
53+
"plex": {
54+
"rating_key": rating_key,
55+
"thumb": poster_url,
56+
"art": background_url,
57+
},
58+
}
59+
60+
monkeypatch.setattr(media_helpers, "_get_media_data", _fake_get_media)
61+
62+
poster = await media_poster.fn(identifier=rating_key)
63+
assert poster == poster_url
64+
poster_cached = await media_poster.fn(identifier=imdb_id)
65+
assert poster_cached == poster_url
66+
background_cached = await media_background.fn(identifier=imdb_id)
67+
assert background_cached == background_url
68+
assert calls == [rating_key]
69+
70+
asyncio.run(_run())

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)