mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
feat: draw site images from data, host them
scripts/sitediagrams.py writes the explainer, install flow, verification modes, MinUI card, pipeline and data layer diagrams as inline SVG from the registry, the platform files, the install manifests and the pipeline step list. A page asks for one with an image comment, which the decoration pass resolves. The two mermaid diagrams of the architecture page are replaced, so no page loads mermaid from unpkg. Platform logos and system icons are copied once by fetch_site_images.py with their source and owner recorded, and the build rewrites the remote links to the copies. validate_site refuses an image from another host. Three registry logos named a personal avatar instead of the project's (RomM, ROCKNIX, MiSTer FPGA) and MyMinUI's was a GitHub identicon, now dropped. One 1200x630 sharing card per section, a 404 page with search and the two entry pages, and a list of screenshots that need a real device.
This commit is contained in:
1 parent
f1c58820d1
commit
1b9ea86e1b
120 files changed
+1963
-176
No files matched your search
+17
-75
@@ -2,25 +2,18 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from collections.abc import Callable
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from pathlib import Path
|
||||
|
||||
from artifacts import write_text_atomic
|
||||
|
||||
WIKI_SRC_DIR = "wiki" # manually maintained wiki sources
|
||||
SYSTEM_ICON_BASE = "https://raw.githubusercontent.com/libretro/retroarch-assets/master/xmb/systematic/png"
|
||||
ICON_CACHE_PATH = Path(".cache") / "system_icons.json"
|
||||
# Local copies of the upstream icons, named as upstream names them.
|
||||
ICON_DIR = Path(__file__).resolve().parent.parent / "docs_assets" / "system-icons"
|
||||
|
||||
# Icon names confirmed to exist upstream. A name absent from this map has not
|
||||
# been checked yet. A name mapped to False has no icon and gets none rendered,
|
||||
# because a heading with a broken image reads worse than a heading without one.
|
||||
# Icon names the site holds a copy of. A name mapped to False has none.
|
||||
_icon_available: dict[str, bool] = {}
|
||||
|
||||
_CODE_SPAN = re.compile(r"(`+).+?\1", re.S)
|
||||
@@ -73,77 +66,26 @@ def _icon_name(manufacturer: str, console_name: str) -> str:
|
||||
def _icon_url(icon_name: str) -> str:
|
||||
return f"{SYSTEM_ICON_BASE}/{urllib.parse.quote(icon_name)}.png"
|
||||
|
||||
def _read_icon_cache() -> dict[str, bool]:
|
||||
"""The recorded icon checks, empty when none were recorded.
|
||||
|
||||
An unreadable file is reported, not taken for an empty cache in silence.
|
||||
"""
|
||||
try:
|
||||
text = ICON_CACHE_PATH.read_text(encoding="utf-8")
|
||||
except FileNotFoundError:
|
||||
return {}
|
||||
except OSError as exc:
|
||||
print(f" WARNING: cannot read {ICON_CACHE_PATH}: {exc}", file=sys.stderr)
|
||||
return {}
|
||||
try:
|
||||
data = json.loads(text)
|
||||
except json.JSONDecodeError as exc:
|
||||
print(f" WARNING: {ICON_CACHE_PATH} is not valid JSON ({exc}), "
|
||||
"icons are checked again", file=sys.stderr)
|
||||
return {}
|
||||
return {str(k): bool(v) for k, v in data.items()} if isinstance(data, dict) else {}
|
||||
|
||||
|
||||
def prime_system_icons(names: set[str]) -> None:
|
||||
"""Record which system icons upstream actually serves.
|
||||
"""Record which system icons the site holds a copy of.
|
||||
|
||||
Results persist in ``.cache`` so later runs skip the network. Names that
|
||||
cannot be checked stay unavailable: the site never links an image it has
|
||||
not seen answer.
|
||||
The icons are copied once into ``docs_assets/system-icons/`` by
|
||||
``scripts/fetch_site_images.py``: the build reads that folder and never
|
||||
the network. A system without a copy gets no icon, because a heading
|
||||
with a broken image reads worse than a heading without one.
|
||||
"""
|
||||
cached = _read_icon_cache()
|
||||
unknown = sorted(n for n in names if n not in cached)
|
||||
if unknown:
|
||||
def check(name: str) -> tuple[str, bool | None]:
|
||||
"""True when served, False when upstream says it is gone.
|
||||
|
||||
None on a transient failure, so a flaky run never records an
|
||||
icon as absent for every later build.
|
||||
"""
|
||||
req = urllib.request.Request(_icon_url(name), method="HEAD")
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
return name, resp.status == 200
|
||||
except urllib.error.HTTPError as exc:
|
||||
return name, False if exc.code == 404 else None
|
||||
except (urllib.error.URLError, OSError):
|
||||
return name, None
|
||||
|
||||
print(f"Checking {len(unknown)} system icons...")
|
||||
learned: dict[str, bool] = {}
|
||||
with ThreadPoolExecutor(max_workers=8) as pool:
|
||||
for name, ok in pool.map(check, unknown):
|
||||
if ok is not None:
|
||||
learned[name] = ok
|
||||
cached.update(learned)
|
||||
if learned:
|
||||
# Another run may have recorded names meanwhile: merge with what
|
||||
# the file holds now, and replace it whole so a reader never sees
|
||||
# a half-written cache.
|
||||
merged = {**_read_icon_cache(), **learned}
|
||||
ICON_CACHE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
write_text_atomic(
|
||||
str(ICON_CACHE_PATH),
|
||||
json.dumps(merged, indent=2, sort_keys=True) + "\n",
|
||||
)
|
||||
|
||||
_icon_available.update(cached)
|
||||
missing = sum(1 for n in names if not cached.get(n))
|
||||
held = {path.stem for path in ICON_DIR.glob("*.png")}
|
||||
_icon_available.update({name: name in held for name in names})
|
||||
missing = sum(1 for name in names if name not in held)
|
||||
if missing:
|
||||
print(f" {missing} systems have no upstream icon")
|
||||
print(f" {missing} systems have no icon in {ICON_DIR.relative_to(ICON_DIR.parents[1])}")
|
||||
|
||||
def system_icon_markdown(manufacturer: str, console_name: str) -> str:
|
||||
"""Icon image for a system heading, empty when upstream serves none."""
|
||||
"""Icon image for a system heading, empty when the site holds none.
|
||||
|
||||
The link names the upstream file: ``sitediagrams.localize_images``
|
||||
points it at the local copy, whose source is recorded beside it.
|
||||
"""
|
||||
name = _icon_name(manufacturer, console_name)
|
||||
if not _icon_available.get(name):
|
||||
return ""
|
||||
|
||||
Reference in new issue
Block a user