mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-06 10:03:49 -06:00
refactor: one emudeck script-to-profile rule
The target scraper kept its own copy of the EmuScripts table and took .ps1 files the platform scraper never read, so emudeck-we's template skeleton was published as a core and removed afterwards by an override. Both scrapers now read emulator_of_script, and the platform scraper lists the installers at the pinned revision, as launchers: and the SteamOS target already did. Targets rescraped, override dropped.
This commit is contained in:
1 parent
19898cb6ea
commit
5c93c6dc6b
9 files changed
+86
-61
No files matched your search
@@ -83,6 +83,7 @@
|
||||
"galaksija",
|
||||
"gam4980",
|
||||
"gambatte",
|
||||
"gametank",
|
||||
"gearboy",
|
||||
"gearcoleco",
|
||||
"geargrafx",
|
||||
@@ -346,6 +347,7 @@
|
||||
"galaksija",
|
||||
"gam4980",
|
||||
"gambatte",
|
||||
"gametank",
|
||||
"gearboy",
|
||||
"gearcoleco",
|
||||
"geargrafx",
|
||||
|
||||
@@ -121,13 +121,6 @@ retrodeck:
|
||||
# includes the frontend name alongside the actual cores.
|
||||
remove_cores: [retroarch]
|
||||
|
||||
emudeck:
|
||||
targets:
|
||||
_default:
|
||||
# EmuDeck's EmuScripts tree carries a template.sh placeholder used
|
||||
# to scaffold new installers. It is not an emulator.
|
||||
remove_cores: [template]
|
||||
|
||||
lakka:
|
||||
targets:
|
||||
generic:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
platform: emudeck
|
||||
source: https://github.com/dragoonDorise/EmuDeck
|
||||
scraped_at: '2026-10-17T18:43:57Z'
|
||||
source: https://github.com/dragoonDorise/EmuDeck/tree/dddb30229adbbd54d5ff7283cfd70e11c4322c26
|
||||
scraped_at: '2026-11-06T07:10:22Z'
|
||||
targets:
|
||||
steamos:
|
||||
architecture: x86_64
|
||||
@@ -88,6 +88,7 @@ targets:
|
||||
- galaksija
|
||||
- gam4980
|
||||
- gambatte
|
||||
- gametank
|
||||
- gearboy
|
||||
- gearcoleco
|
||||
- geargrafx
|
||||
@@ -352,6 +353,7 @@ targets:
|
||||
- galaksija
|
||||
- gam4980
|
||||
- gambatte
|
||||
- gametank
|
||||
- gearboy
|
||||
- gearcoleco
|
||||
- geargrafx
|
||||
@@ -487,7 +489,6 @@ targets:
|
||||
- supermodel
|
||||
- swanstation
|
||||
- tamalibretro
|
||||
- template
|
||||
- tgbdual
|
||||
- theodore
|
||||
- thepowdertoy
|
||||
|
||||
@@ -46,14 +46,18 @@ STORAGE = "${storagePath}"
|
||||
FLATPAK_DATA = "${HOME}/.var/app/"
|
||||
|
||||
# EmuScripts file names that differ from the emulator profile they set up.
|
||||
# The platform and target scrapers both read them here.
|
||||
SCRIPT_EMULATORS = {
|
||||
"pcsx2qt": "pcsx2",
|
||||
"rpcs3legacy": "rpcs3",
|
||||
"cemuproton": "cemu",
|
||||
"rmg": "mupen64plus_next",
|
||||
}
|
||||
# The main RetroArch config script installs no emulator.
|
||||
NOT_EMULATORS = {"retroarch_maincfg"}
|
||||
# The main RetroArch config script installs no emulator, and emudeck-we's
|
||||
# emuDeckTemplate.ps1 is the skeleton new installers are copied from.
|
||||
NOT_EMULATORS = {"retroarch_maincfg", "template"}
|
||||
# EmuDeck's SteamOS installers are shell scripts, emudeck-we's PowerShell.
|
||||
SCRIPT_SUFFIXES = (".sh", ".ps1")
|
||||
|
||||
# What the emulator does with a directory written into each key.
|
||||
DIRECTORY_KINDS = {
|
||||
@@ -66,9 +70,10 @@ DIRECTORY_KINDS = {
|
||||
|
||||
def emulator_of_script(file_name: str) -> str | None:
|
||||
"""The emulator an EmuScripts file sets up, None for a helper script."""
|
||||
if not file_name.endswith(".sh"):
|
||||
stem, dot, suffix = file_name.rpartition(".")
|
||||
if not dot or f".{suffix}" not in SCRIPT_SUFFIXES:
|
||||
return None
|
||||
key = re.sub(r"^emuDeck", "", file_name[: -len(".sh")], flags=re.IGNORECASE).lower()
|
||||
key = re.sub(r"^emuDeck", "", stem, flags=re.IGNORECASE).lower()
|
||||
if not key or key in NOT_EMULATORS:
|
||||
return None
|
||||
return SCRIPT_EMULATORS.get(key, key)
|
||||
|
||||
@@ -469,9 +469,10 @@ class Scraper(BaseScraper):
|
||||
|
||||
version = fetch_github_latest_version("dragoonDorise/EmuDeck")
|
||||
|
||||
cores = self._fetch_installed_emulators()
|
||||
revision = launcher_revision(self.checkbios_url)
|
||||
cores = self._fetch_installed_emulators(revision)
|
||||
standalone = [core for core in cores if core != "retroarch"]
|
||||
tree = RepoTree.github(EMUDECK_REPO, launcher_revision(self.checkbios_url))
|
||||
tree = RepoTree.github(EMUDECK_REPO, revision)
|
||||
libretro = set(installed_libretro_cores(tree.read(RETROARCH_SCRIPT)))
|
||||
|
||||
return {
|
||||
@@ -501,16 +502,16 @@ class Scraper(BaseScraper):
|
||||
"launchers": extract_launchers(tree),
|
||||
}
|
||||
|
||||
def _fetch_installed_emulators(self) -> list[str]:
|
||||
"""Fetch the list of emulators installed by EmuDeck from EmuScripts.
|
||||
|
||||
Returns core names normalized to match emulator profile keys.
|
||||
def _fetch_installed_emulators(self, revision: str) -> list[str]:
|
||||
"""The emulators EmuDeck's EmuScripts install at the pinned revision,
|
||||
named as their profiles are: the launcher layer and the SteamOS
|
||||
target are read at that revision too.
|
||||
"""
|
||||
import json
|
||||
|
||||
api_url = (
|
||||
"https://api.github.com/repos/dragoonDorise/EmuDeck/"
|
||||
"contents/functions/EmuScripts"
|
||||
f"contents/functions/EmuScripts?ref={revision}"
|
||||
)
|
||||
try:
|
||||
req = urllib.request.Request(api_url, headers=github_headers())
|
||||
|
||||
@@ -12,7 +12,6 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
@@ -22,6 +21,7 @@ import yaml
|
||||
|
||||
from common import github_token, yaml_load
|
||||
|
||||
from ..emudeck_launchers import SCRIPT_SUFFIXES, emulator_of_script
|
||||
from . import BaseTargetScraper
|
||||
|
||||
PLATFORM_NAME = "emudeck"
|
||||
@@ -33,24 +33,6 @@ WINDOWS_API = (
|
||||
"https://api.github.com/repos/EmuDeck/emudeck-we/contents/functions/EmuScripts"
|
||||
)
|
||||
|
||||
# Map EmuDeck script names to emulator profile keys
|
||||
# Script naming: emuDeckDolphin.sh -> dolphin
|
||||
# Some need explicit mapping when names differ
|
||||
_NAME_OVERRIDES: dict[str, str] = {
|
||||
"pcsx2qt": "pcsx2",
|
||||
"rpcs3legacy": "rpcs3",
|
||||
"cemuproton": "cemu",
|
||||
"rmg": "mupen64plus_next",
|
||||
"bigpemu": "bigpemu",
|
||||
"eden": "eden",
|
||||
"suyu": "suyu",
|
||||
"ares": "ares",
|
||||
}
|
||||
|
||||
# Scripts that are not emulators (config helpers, etc.)
|
||||
_SKIP = {"retroarch_maincfg", "retroarch"}
|
||||
|
||||
|
||||
def _fetch(url: str) -> str:
|
||||
headers = {"User-Agent": "retrobios-scraper/1.0"}
|
||||
token = github_token()
|
||||
@@ -69,26 +51,13 @@ def _list_emuscripts(api_url: str) -> list[str]:
|
||||
"""List emulator script filenames from GitHub API."""
|
||||
entries = json.loads(_fetch(api_url))
|
||||
names = [
|
||||
e["name"] for e in entries if e.get("name", "").endswith((".sh", ".ps1"))
|
||||
e["name"] for e in entries if e.get("name", "").endswith(SCRIPT_SUFFIXES)
|
||||
]
|
||||
if not names:
|
||||
raise RuntimeError(f"no emulator scripts listed at {api_url}")
|
||||
return names
|
||||
|
||||
|
||||
def _script_to_core(filename: str) -> str | None:
|
||||
"""Convert EmuScripts filename to core profile key."""
|
||||
# Strip extension and emuDeck prefix
|
||||
name = re.sub(r"\.(sh|ps1)$", "", filename, flags=re.IGNORECASE)
|
||||
name = re.sub(r"^emuDeck", "", name, flags=re.IGNORECASE)
|
||||
if not name:
|
||||
return None
|
||||
key = name.lower()
|
||||
if key in _SKIP:
|
||||
return None
|
||||
return _NAME_OVERRIDES.get(key, key)
|
||||
|
||||
|
||||
class Scraper(BaseTargetScraper):
|
||||
"""Fetches emulator lists for EmuDeck SteamOS and Windows targets."""
|
||||
|
||||
@@ -114,14 +83,13 @@ class Scraper(BaseTargetScraper):
|
||||
seen: set[str] = set()
|
||||
has_retroarch = False
|
||||
for script in scripts:
|
||||
core = _script_to_core(script)
|
||||
if core and core not in seen:
|
||||
core = emulator_of_script(script)
|
||||
# RetroArch stands for its libretro cores, added below.
|
||||
if core == "retroarch":
|
||||
has_retroarch = True
|
||||
elif core and core not in seen:
|
||||
seen.add(core)
|
||||
cores.append(core)
|
||||
# Detect RetroArch presence (provides all libretro cores)
|
||||
name = re.sub(r"\.(sh|ps1)$", "", script, flags=re.IGNORECASE)
|
||||
if name.lower() in ("emudeckretroarch", "retroarch_maincfg"):
|
||||
has_retroarch = True
|
||||
|
||||
standalone_count = len(cores)
|
||||
# EmuDeck ships RetroArch = all its libretro cores are available
|
||||
|
||||
@@ -373,6 +373,8 @@ class Helpers(unittest.TestCase):
|
||||
self.assertEqual(el.emulator_of_script("emuDeckXemu.sh"), "xemu")
|
||||
self.assertIsNone(el.emulator_of_script("RetroArch_maincfg.sh"))
|
||||
self.assertIsNone(el.emulator_of_script("README.md"))
|
||||
self.assertEqual(el.emulator_of_script("emuDeckPCSX2QT.ps1"), "pcsx2")
|
||||
self.assertIsNone(el.emulator_of_script("emuDeckTemplate.ps1"))
|
||||
|
||||
def test_the_revision_comes_from_the_pinned_url(self):
|
||||
url = (
|
||||
|
||||
@@ -724,7 +724,7 @@ class UnreadableReleaseStopsTheScrape(unittest.TestCase):
|
||||
with mock.patch("urllib.request.urlopen", self._refuse), self.assertRaises(
|
||||
RuntimeError
|
||||
):
|
||||
emudeck_scraper.Scraper()._fetch_installed_emulators()
|
||||
emudeck_scraper.Scraper(checkbios_url="pinned")._fetch_installed_emulators("0" * 40)
|
||||
|
||||
def test_libretro_core_metadata_raises(self):
|
||||
"""libretro dropped every system name and manufacturer when the
|
||||
|
||||
@@ -541,6 +541,59 @@ class TargetsReadThePlatformRevision(unittest.TestCase):
|
||||
self.assertTrue(steamos)
|
||||
self.assertTrue(all(u.endswith(f"?ref={pin}") for u in steamos), steamos)
|
||||
|
||||
def test_emudeck_platform_lists_its_installers_at_the_pin(self):
|
||||
"""cores: was read on master while launchers: and the SteamOS target
|
||||
were read at the pin: the YAML described two revisions."""
|
||||
from scripts.scraper import emudeck_scraper
|
||||
|
||||
pin = "d" * 40
|
||||
seen: list[str] = []
|
||||
|
||||
class _Body:
|
||||
def __init__(self, url):
|
||||
seen.append(url)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *_exc):
|
||||
return False
|
||||
|
||||
def read(self):
|
||||
return json.dumps([{"name": "emuDeckRMG.sh"}]).encode()
|
||||
|
||||
with mock.patch("urllib.request.urlopen", lambda req, **_kw: _Body(req.full_url)):
|
||||
cores = emudeck_scraper.Scraper(checkbios_url="pinned")._fetch_installed_emulators(pin)
|
||||
self.assertEqual(cores, ["mupen64plus_next"])
|
||||
self.assertEqual(len(seen), 1)
|
||||
self.assertTrue(seen[0].endswith(f"?ref={pin}"), seen)
|
||||
|
||||
|
||||
class EmuDeckScriptNamesHaveOneRule(unittest.TestCase):
|
||||
"""The target scraper kept its own copy of the EmuScripts-to-profile
|
||||
table: it published emudeck-we's template skeleton as a core, and a
|
||||
rename added to one copy only made --target filter that core out."""
|
||||
|
||||
def test_the_target_scraper_names_scripts_as_the_platform_does(self):
|
||||
from scripts.scraper.emudeck_launchers import emulator_of_script
|
||||
|
||||
listing = [
|
||||
"emuDeckRMG.sh",
|
||||
"emuDeckPCSX2QT.ps1",
|
||||
"emuDeckTemplate.ps1",
|
||||
"emuDeckRetroArch.ps1",
|
||||
"RetroArch_maincfg.sh",
|
||||
"emuDeckXemu.ps1",
|
||||
]
|
||||
scraper = emudeck_targets_scraper.Scraper(revision="0" * 40)
|
||||
with mock.patch.object(
|
||||
emudeck_targets_scraper, "_list_emuscripts", return_value=listing
|
||||
), mock.patch.object(scraper, "_load_retroarch_cores", return_value=["mgba"]):
|
||||
cores = scraper._fetch_cores_for_target("unused", "Windows")
|
||||
standalone = {emulator_of_script(name) for name in listing} - {None, "retroarch"}
|
||||
self.assertEqual(standalone, {"mupen64plus_next", "pcsx2", "xemu"})
|
||||
self.assertEqual(cores, sorted(standalone | {"mgba"}))
|
||||
|
||||
|
||||
class HashScrapersRefuseAnUnknownVersion(unittest.TestCase):
|
||||
"""mame_hash_scraper wrote core_version "unknown" after a failed API call
|
||||
|
||||
Reference in new issue
Block a user