Skip to content

Commit d917268

Browse files
committed
start mapping platforms slugs to lb platforms
1 parent d2b201f commit d917268

7 files changed

Lines changed: 371 additions & 2 deletions

File tree

backend/endpoints/heartbeat.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from handler.database import db_user_handler
1919
from handler.filesystem import fs_platform_handler
2020
from handler.metadata.igdb_handler import IGDB_API_ENABLED
21+
from handler.metadata.launchbox_handler import LAUNCHBOX_API_ENABLED
2122
from handler.metadata.moby_handler import MOBY_API_ENABLED
2223
from handler.metadata.ra_handler import RA_API_ENABLED
2324
from handler.metadata.sgdb_handler import STEAMGRIDDB_API_ENABLED
@@ -47,11 +48,13 @@ def heartbeat() -> HeartbeatResponse:
4748
"ANY_SOURCE_ENABLED": IGDB_API_ENABLED
4849
or SS_API_ENABLED
4950
or MOBY_API_ENABLED
50-
or RA_API_ENABLED,
51+
or RA_API_ENABLED
52+
or LAUNCHBOX_API_ENABLED,
5153
"IGDB_API_ENABLED": IGDB_API_ENABLED,
5254
"SS_API_ENABLED": SS_API_ENABLED,
5355
"MOBY_API_ENABLED": MOBY_API_ENABLED,
5456
"STEAMGRIDDB_ENABLED": STEAMGRIDDB_API_ENABLED,
57+
"LAUNCHBOX_ENABLED": LAUNCHBOX_API_ENABLED,
5558
"RA_ENABLED": RA_API_ENABLED,
5659
},
5760
"FILESYSTEM": {

backend/endpoints/responses/heartbeat.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class MetadataSourcesDict(TypedDict):
2828
MOBY_API_ENABLED: bool
2929
SS_API_ENABLED: bool
3030
STEAMGRIDDB_ENABLED: bool
31+
LAUNCHBOX_ENABLED: bool
3132
RA_ENABLED: bool
3233

3334

backend/handler/metadata/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .igdb_handler import IGDBHandler
2+
from .launchbox_handler import LaunchboxHandler
23
from .moby_handler import MobyGamesHandler
34
from .ra_handler import RAHandler
45
from .sgdb_handler import SGDBBaseHandler
@@ -9,3 +10,4 @@
910
meta_ss_handler = SSHandler()
1011
meta_sgdb_handler = SGDBBaseHandler()
1112
meta_ra_handler = RAHandler()
13+
meta_launchbox_handler = LaunchboxHandler()
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
import json
2+
from typing import Final, NotRequired, TypedDict
3+
4+
from handler.redis_handler import async_cache
5+
from tasks.update_launchbox_metadata import ( # LAUNCHBOX_PLATFORMS_KEY,; LAUNCHBOX_METADATA_IMAGE_KEY,; LAUNCHBOX_MAME_KEY,; update_launchbox_metadata_task,
6+
LAUNCHBOX_FILES_KEY,
7+
LAUNCHBOX_METADATA_DATABASE_ID_KEY,
8+
LAUNCHBOX_METADATA_NAME_KEY,
9+
)
10+
11+
from .base_hander import MetadataHandler
12+
13+
LAUNCHBOX_API_ENABLED: Final = True
14+
15+
16+
class LaunchboxPlatform(TypedDict):
17+
slug: str
18+
name: NotRequired[str]
19+
20+
21+
class LaunchboxMetadata(TypedDict):
22+
release_date: NotRequired[str]
23+
max_players: NotRequired[int]
24+
release_type: NotRequired[str]
25+
cooperative: NotRequired[bool]
26+
video_url: NotRequired[str]
27+
community_rating: NotRequired[float]
28+
community_rating_count: NotRequired[int]
29+
wikipedia_url: NotRequired[str]
30+
esrb: NotRequired[str]
31+
genres: NotRequired[list[str]]
32+
developer: NotRequired[str]
33+
publisher: NotRequired[str]
34+
35+
36+
class LaunchboxRom(TypedDict):
37+
name: NotRequired[str]
38+
summary: NotRequired[str]
39+
url_cover: NotRequired[str]
40+
url_screenshots: NotRequired[list[str]]
41+
launchbox_metadata: NotRequired[LaunchboxMetadata]
42+
43+
44+
def extract_metadata_from_launchbox_rom(index_entry: dict) -> LaunchboxMetadata:
45+
return LaunchboxMetadata(
46+
{
47+
"release_date": index_entry.get("ReleaseDate", ""),
48+
"max_players": index_entry.get("MaxPlayers", 0),
49+
"release_type": index_entry.get("ReleaseType", ""),
50+
"cooperative": index_entry.get("Cooperative", False),
51+
"video_url": index_entry.get("VideoURL", ""),
52+
"community_rating": index_entry.get("CommunityRating", 0),
53+
"community_rating_count": index_entry.get("CommunityRatingCount", 0),
54+
"wikipedia_url": index_entry.get("WikipediaURL", ""),
55+
"esrb": index_entry.get("ESRB", ""),
56+
"genres": index_entry.get("Genres", []),
57+
"developer": index_entry.get("Developer", ""),
58+
"publisher": index_entry.get("Publisher", ""),
59+
}
60+
)
61+
62+
63+
class LaunchboxHandler(MetadataHandler):
64+
async def _search_rom(self, file_name: str) -> dict | None:
65+
file_index_entry = await async_cache.hget(LAUNCHBOX_FILES_KEY, file_name)
66+
if not file_index_entry:
67+
return None
68+
69+
file_index_entry = json.loads(file_index_entry)
70+
metadata_name_index_entry = await async_cache.hget(
71+
LAUNCHBOX_METADATA_NAME_KEY, file_index_entry["name"]
72+
)
73+
74+
if not metadata_name_index_entry:
75+
return None
76+
77+
return json.loads(metadata_name_index_entry)
78+
79+
def get_platform(self, slug: str) -> LaunchboxPlatform:
80+
platform = SLUG_TO_LAUNCHBOX_PLATFORM_NAME.get(slug, None)
81+
82+
if not platform:
83+
return LaunchboxPlatform(slug=slug)
84+
85+
return LaunchboxPlatform(
86+
slug=slug,
87+
name=platform["name"],
88+
)
89+
90+
async def get_rom(self, fs_name: str) -> LaunchboxRom:
91+
from handler.filesystem import fs_rom_handler
92+
93+
if not LAUNCHBOX_API_ENABLED:
94+
return LaunchboxRom()
95+
96+
search_term = fs_rom_handler.get_file_name_with_no_extension(fs_name)
97+
fallback_rom = LaunchboxRom()
98+
99+
index_entry = await self._search_rom(search_term)
100+
if not index_entry:
101+
return fallback_rom
102+
103+
rom = {
104+
"name": index_entry["Name"],
105+
"summary": index_entry.get("Overview", ""),
106+
"launchbox_metadata": extract_metadata_from_launchbox_rom(index_entry),
107+
}
108+
109+
return LaunchboxRom({k: v for k, v in rom.items() if v}) # type: ignore[misc]
110+
111+
async def get_rom_by_id(self, database_id: int) -> LaunchboxRom:
112+
if not LAUNCHBOX_API_ENABLED:
113+
return LaunchboxRom()
114+
115+
metadata_database_index_entry = await async_cache.hget(
116+
LAUNCHBOX_METADATA_DATABASE_ID_KEY, str(database_id)
117+
)
118+
119+
if not metadata_database_index_entry:
120+
return LaunchboxRom()
121+
122+
rom = {
123+
"name": metadata_database_index_entry["Name"],
124+
"summary": metadata_database_index_entry.get("Overview", ""),
125+
"launchbox_metadata": extract_metadata_from_launchbox_rom(
126+
metadata_database_index_entry
127+
),
128+
}
129+
130+
return LaunchboxRom({k: v for k, v in rom.items() if v}) # type: ignore[misc]
131+
132+
async def get_matched_rom_by_id(self, database_id: int) -> LaunchboxRom | None:
133+
if not LAUNCHBOX_API_ENABLED:
134+
return None
135+
136+
return await self.get_rom_by_id(database_id)
137+
138+
139+
class SlugToLaunchboxPlatformName(TypedDict):
140+
id: int
141+
name: str
142+
143+
144+
SLUG_TO_LAUNCHBOX_PLATFORM_NAME = {
145+
"3do": "3DO Interactive Multiplayer",
146+
"apf": "APF Imagination Machine",
147+
"pegasus": "Aamber Pegasus",
148+
"acorn-archimedes": "Acorn Archimedes",
149+
"atom": "Acorn Atom",
150+
"acorn-electron": "Acorn Electron",
151+
"acpc": "Amstrad CPC",
152+
"gx4000": "Amstrad GX4000",
153+
"android": "Android",
154+
"bk-01": "Apogee BK-01",
155+
"apple2": "Apple II",
156+
"apple2gs": "Apple IIGS",
157+
"mac": "Apple Mac OS",
158+
"ios": "Apple iOS",
159+
"arcade": "Arcade",
160+
"atari2600": "Atari 2600",
161+
"atari5200": "Atari 5200",
162+
"atari7800": "Atari 7800",
163+
"atari800": "Atari 800",
164+
"jaguar": "Atari Jaguar",
165+
"atari-jaguar-cd": "Atari Jaguar CD",
166+
"lynx": "Atari Lynx",
167+
"atari-st": "Atari ST",
168+
"atari-xegs": "Atari XEGS",
169+
"bbcmicro": "BBC Microcomputer System",
170+
"astrocade": "Bally Astrocade",
171+
"super-vision-8000": "Bandai Super Vision 8000",
172+
"camputers-lynx": "Camputers Lynx",
173+
"casio-loopy": "Casio Loopy",
174+
"casio-pv-1000": "Casio PV-1000",
175+
"colecoadam": "Coleco ADAM",
176+
"colecovision": "ColecoVision",
177+
"c128": "Commodore 128",
178+
"c64": "Commodore 64",
179+
"amiga": "Commodore Amiga",
180+
"amiga-cd32": "Commodore Amiga CD32",
181+
"commodore-cdtv": "Commodore CDTV",
182+
"cpet": "Commodore PET",
183+
"c-plus-4": "Commodore Plus 4",
184+
"vic-20": "Commodore VIC-20",
185+
"dragon-32-slash-64": "Dragon 32/64",
186+
"colour-genie": "EACA EG2000 Colour Genie",
187+
"bk": "Elektronika BK",
188+
"arcadia-2001": "Emerson Arcadia 2001",
189+
"enterprise": "Enterprise",
190+
"adventure-vision": "Entex Adventure Vision",
191+
"epoch-game-pocket-computer": "Epoch Game Pocket Computer",
192+
"epoch-super-cassette-vision": "Epoch Super Cassette Vision",
193+
"exelvision": "Exelvision EXL 100",
194+
"exidy-sorcerer": "Exidy Sorcerer",
195+
"fairchild-channel-f": "Fairchild Channel F",
196+
"fm-towns": "Fujitsu FM Towns Marty",
197+
"fm-7": "Fujitsu FM-7",
198+
"super-acan": "Funtech Super Acan",
199+
"": "GCE Vectrex",
200+
"": "Game Wave Family Entertainment System",
201+
"": "GamePark GP32",
202+
"": "GameWave",
203+
"": "Hartung Game Master",
204+
"": "Hector HRX",
205+
"vc-4000": "Interton VC 4000",
206+
"": "Jupiter Ace",
207+
"": "Linux",
208+
"": "MS-DOS",
209+
"": "MUGEN",
210+
"": "Magnavox Odyssey",
211+
"": "Magnavox Odyssey 2",
212+
"": "Matra and Hachette Alice",
213+
"": "Mattel Aquarius",
214+
"": "Mattel HyperScan",
215+
"": "Mattel Intellivision",
216+
"": "Mega Duck",
217+
"": "Memotech MTX512",
218+
"": "Microsoft MSX",
219+
"": "Microsoft MSX2",
220+
"": "Microsoft MSX2+",
221+
"": "Microsoft Xbox",
222+
"": "Microsoft Xbox 360",
223+
"": "Microsoft Xbox One",
224+
"": "Microsoft Xbox Series X/S",
225+
"": "NEC PC-8801",
226+
"": "NEC PC-9801",
227+
"": "NEC PC-FX",
228+
"": "NEC TurboGrafx-16",
229+
"": "NEC TurboGrafx-CD",
230+
"": "Namco System 22",
231+
"": "Nintendo 3DS",
232+
"": "Nintendo 64",
233+
"": "Nintendo 64DD",
234+
"": "Nintendo DS",
235+
"": "Nintendo Entertainment System",
236+
"": "Nintendo Famicom Disk System",
237+
"": "Nintendo Game & Watch",
238+
"": "Nintendo Game Boy",
239+
"": "Nintendo Game Boy Advance",
240+
"": "Nintendo Game Boy Color",
241+
"": "Nintendo GameCube",
242+
"": "Nintendo Pokemon Mini",
243+
"": "Nintendo Satellaview",
244+
"": "Nintendo Switch",
245+
"": "Nintendo Switch 2",
246+
"": "Nintendo Virtual Boy",
247+
"": "Nintendo Wii",
248+
"": "Nintendo Wii U",
249+
"": "Nokia N-Gage",
250+
"": "Nuon",
251+
"": "OpenBOR",
252+
"": "Oric Atmos",
253+
"": "Othello Multivision",
254+
"": "Ouya",
255+
"": "PC Engine SuperGrafx",
256+
"": "PICO-8",
257+
"": "Philips CD-i",
258+
"": "Philips VG 5000",
259+
"": "Philips Videopac+",
260+
"": "Pinball",
261+
"": "RCA Studio II",
262+
"": "SAM Coupé",
263+
"": "SNK Neo Geo AES",
264+
"": "SNK Neo Geo CD",
265+
"": "SNK Neo Geo MVS",
266+
"": "SNK Neo Geo Pocket",
267+
"": "SNK Neo Geo Pocket Color",
268+
"": "Sammy Atomiswave",
269+
"": "ScummVM",
270+
"": "Sega 32X",
271+
"": "Sega CD",
272+
"": "Sega CD 32X",
273+
"": "Sega Dreamcast",
274+
"": "Sega Dreamcast VMU",
275+
"": "Sega Game Gear",
276+
"": "Sega Genesis",
277+
"": "Sega Hikaru",
278+
"": "Sega Master System",
279+
"": "Sega Model 1",
280+
"": "Sega Model 2",
281+
"": "Sega Model 3",
282+
"": "Sega Naomi",
283+
"": "Sega Naomi 2",
284+
"": "Sega Pico",
285+
"": "Sega SC-3000",
286+
"": "Sega SG-1000",
287+
"": "Sega ST-V",
288+
"": "Sega Saturn",
289+
"": "Sega System 16",
290+
"": "Sega System 32",
291+
"": "Sega Triforce",
292+
"": "Sharp MZ-2500",
293+
"": "Sharp X1",
294+
"": "Sharp X68000",
295+
"": "Sinclair ZX Spectrum",
296+
"": "Sinclair ZX-81",
297+
"": "Sony PSP",
298+
"": "Sony PSP Minis",
299+
"": "Sony Playstation",
300+
"": "Sony Playstation 2",
301+
"": "Sony Playstation 3",
302+
"": "Sony Playstation 4",
303+
"": "Sony Playstation 5",
304+
"": "Sony Playstation Vita",
305+
"": "Sony PocketStation",
306+
"": "Sord M5",
307+
"": "Spectravideo",
308+
"": "Super Nintendo Entertainment System",
309+
"": "TRS-80 Color Computer",
310+
"": "Taito Type X",
311+
"": "Tandy TRS-80",
312+
"": "Tapwave Zodiac",
313+
"": "Texas Instruments TI 99/4A",
314+
"": "Tiger Game.com",
315+
"": "Tomy Tutor",
316+
"": "VTech CreatiVision",
317+
"": "VTech Socrates",
318+
"": "VTech V.Smile",
319+
"": "Vector-06C",
320+
"": "Watara Supervision",
321+
"": "Web Browser",
322+
"": "Windows",
323+
"": "Windows 3.X",
324+
"": "WoW Action Max",
325+
"": "WonderSwan",
326+
"": "WonderSwan Color",
327+
"": "XaviXPORT",
328+
"": "ZiNc",
329+
}
330+
331+
# Reverse lookup
332+
LAUNCHBOX_PLATFORM_NAME_TO_SLUG = {
333+
v["id"]: k for k, v in SLUG_TO_LAUNCHBOX_PLATFORM_NAME.items()
334+
}

0 commit comments

Comments
 (0)