Skip to content

Commit e7dd013

Browse files
authored
Merge pull request #3580 from rommapp/claude/youthful-wozniak-ocjhc0
feat(v2): interactive 3D box art on the game detail hero
2 parents 2631f0e + 111fc96 commit e7dd013

29 files changed

Lines changed: 897 additions & 1 deletion

File tree

backend/config/config_manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class MetadataMediaType(enum.StrEnum):
7373
BEZEL = "bezel"
7474
BOX2D = "box2d"
7575
BOX2D_BACK = "box2d_back"
76+
BOX2D_SIDE = "box2d_side"
7677
BOX3D = "box3d"
7778
MIXIMAGE = "miximage"
7879
MIXIMAGE_V2 = "miximage_v2"

backend/handler/metadata/ss_handler.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ class SSMetadataMedia(TypedDict):
183183
# Resources stored in filesystem
184184
bezel_path: str | None
185185
box2d_back_path: str | None
186+
box2d_side_path: str | None
186187
box3d_path: str | None
187188
fanart_path: str | None
188189
miximage_path: str | None
@@ -244,6 +245,7 @@ def extract_media_from_ss_game(rom: Rom, game: SSGame) -> SSMetadataMedia:
244245
video_normalized_url=None,
245246
bezel_path=None,
246247
box2d_back_path=None,
248+
box2d_side_path=None,
247249
box3d_path=None,
248250
fanart_path=None,
249251
miximage_path=None,
@@ -358,6 +360,10 @@ def extract_media_from_ss_game(rom: Rom, game: SSGame) -> SSMetadataMedia:
358360
ss_media["box2d_side_url"] = strip_sensitive_query_params(
359361
media["url"], SENSITIVE_KEYS
360362
)
363+
if MetadataMediaType.BOX2D_SIDE in preferred_media_types:
364+
ss_media["box2d_side_path"] = (
365+
f"{fs_resource_handler.get_media_resources_path(rom.platform_id, rom.id, MetadataMediaType.BOX2D_SIDE)}/box2d_side.png"
366+
)
361367
elif media.get("type") == "steamgrid" and not ss_media["steamgrid_url"]:
362368
ss_media["steamgrid_url"] = strip_sensitive_query_params(
363369
media["url"], SENSITIVE_KEYS

backend/tests/handler/metadata/test_ss_handler.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,86 @@ def test_preferred_region_wins_over_cus(self):
208208
assert result["box2d_url"] is not None
209209
assert "box-2D(us)" in result["box2d_url"]
210210

211+
def _make_game_with_box_faces(self) -> SSGame:
212+
"""A game exposing all three box faces: front, back and spine."""
213+
return cast(
214+
SSGame,
215+
{
216+
"medias": [
217+
{
218+
"type": "box-2D",
219+
"parent": "jeu",
220+
"region": "us",
221+
"url": "https://screenscraper.example.com/box-2D",
222+
"crc": "aabbccdd",
223+
"md5": "deadbeef",
224+
"sha1": "cafebabe",
225+
"size": "12345",
226+
"format": "png",
227+
},
228+
{
229+
"type": "box-2D-back",
230+
"parent": "jeu",
231+
"region": "us",
232+
"url": "https://screenscraper.example.com/box-2D-back",
233+
"crc": "11223344",
234+
"md5": "feedface",
235+
"sha1": "baadf00d",
236+
"size": "23456",
237+
"format": "png",
238+
},
239+
{
240+
"type": "box-2D-side",
241+
"parent": "jeu",
242+
"region": "us",
243+
"url": "https://screenscraper.example.com/box-2D-side",
244+
"crc": "55667788",
245+
"md5": "0ddba11",
246+
"sha1": "f00dcafe",
247+
"size": "34567",
248+
"format": "png",
249+
},
250+
]
251+
},
252+
)
253+
254+
def test_box2d_side_path_set_when_in_config(self):
255+
"""When 'box2d_side' is in SCAN_MEDIA the spine is persisted locally."""
256+
config = _make_config(scan_media=["box2d", "box2d_back", "box2d_side"])
257+
rom = self._make_rom()
258+
game = self._make_game_with_box_faces()
259+
260+
with (
261+
patch("handler.metadata.ss_handler.cm.get_config", return_value=config),
262+
patch(
263+
"handler.metadata.ss_handler.fs_resource_handler.get_media_resources_path",
264+
side_effect=lambda pid, rid, mt: f"roms/{pid}/{rid}/{mt.value}",
265+
),
266+
):
267+
result = extract_media_from_ss_game(rom, game)
268+
269+
assert result["box2d_side_url"] is not None
270+
assert "box-2D-side" in result["box2d_side_url"]
271+
assert result["box2d_side_path"] == "roms/1/100/box2d_side/box2d_side.png"
272+
273+
def test_box2d_side_path_not_set_when_absent_from_config(self):
274+
"""Without 'box2d_side' in SCAN_MEDIA the spine URL is kept but not stored."""
275+
config = _make_config(scan_media=["box2d", "box2d_back"])
276+
rom = self._make_rom()
277+
game = self._make_game_with_box_faces()
278+
279+
with (
280+
patch("handler.metadata.ss_handler.cm.get_config", return_value=config),
281+
patch(
282+
"handler.metadata.ss_handler.fs_resource_handler.get_media_resources_path",
283+
side_effect=lambda pid, rid, mt: f"roms/{pid}/{rid}/{mt.value}",
284+
),
285+
):
286+
result = extract_media_from_ss_game(rom, game)
287+
288+
assert result["box2d_side_url"] is not None
289+
assert result["box2d_side_path"] is None
290+
211291
def _make_game_with_both_miximage_versions(self) -> SSGame:
212292
"""A game that has both mixrbv1 and mixrbv2 (v1 listed first, matching SS API order)."""
213293
return cast(

examples/config.example.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
# - video_normalized # Normalized video (smaller file size than video)
134134
# # Media used for batocera gamelist.xml export
135135
# - box2d_back # Back cover art
136+
# - box2d_side # Box spine (enables the interactive 3D box on detail pages)
136137
# - logo # Transparent logo
137138
# # Other media assets (might be used in the future)
138139
# - marquee # Custom marquee

frontend/src/__generated__/models/RomSSMetadata.ts

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

frontend/src/locales/bg_BG/rom.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"age-rating": "Възрастова оценка",
99
"all-styles": "Всички стилове",
1010
"backlogged": "В списъка",
11+
"box3d-alt": "Въртяща се 3D кутия за {title}",
1112
"by": "от",
1213
"cant-copy-link": "Линкът не може да бъде копиран, копирай го ръчно",
1314
"collections": "Колекции",

frontend/src/locales/cs_CZ/rom.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"age-rating": "Hodnocení věku",
99
"all-styles": "Všechny styly",
1010
"backlogged": "Odloženo",
11+
"box3d-alt": "Otočná 3D krabice pro {title}",
1112
"by": "od",
1213
"cant-copy-link": "Nelze zkopírovat odkaz do schránky, zkopírujte ručně",
1314
"collections": "Kolekce",

frontend/src/locales/de_DE/rom.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"age-rating": "Altersfreigabe",
99
"all-styles": "Alle Stile",
1010
"backlogged": "Vorgemerkt",
11+
"box3d-alt": "Drehbares 3D-Cover von {title}",
1112
"by": "nach",
1213
"cant-copy-link": "Link kann nicht in Zwischenablage kopiert werden. Bitte manuell kopieren.",
1314
"collections": "Sammlungen",

frontend/src/locales/en_GB/rom.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"age-rating": "Age rating",
99
"all-styles": "All Styles",
1010
"backlogged": "Backlogged",
11+
"box3d-alt": "Rotatable 3D box art for {title}",
1112
"by": "by",
1213
"cant-copy-link": "Can't copy link to clipboard, copy it manually",
1314
"collections": "Collections",

frontend/src/locales/en_US/rom.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"age-rating": "Age rating",
99
"all-styles": "All Styles",
1010
"backlogged": "Backlogged",
11+
"box3d-alt": "Rotatable 3D box art for {title}",
1112
"by": "by",
1213
"cant-copy-link": "Can't copy link to clipboard, copy it manually",
1314
"collections": "Collections",

0 commit comments

Comments
 (0)