Added Firmware archive
This commit is contained in:
82
archive.json
Normal file
82
archive.json
Normal file
@@ -0,0 +1,82 @@
|
||||
[
|
||||
{
|
||||
"tag": "22.1.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/22.1.0/Firmware.22.1.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "22.0.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/22.0.0/Firmware.22.0.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "21.2.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/21.2.0/Firmware.21.2.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "21.1.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/21.1.0/Firmware.21.1.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "21.0.1",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/21.0.1/Firmware.21.0.1.zip"
|
||||
},
|
||||
{
|
||||
"tag": "21.0.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/21.0.0/Firmware.21.0.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "20.5.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/20.5.0/Firmware.20.5.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "20.4.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/20.4.0/Firmware.20.4.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "20.3.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/20.3.0/Firmware.20.3.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "20.2.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/20.2.0/Firmware.20.2.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "20.1.5",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/20.1.5/Firmware.20.1.5.zip"
|
||||
},
|
||||
{
|
||||
"tag": "20.1.1",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/20.1.1/Firmware.20.1.1.zip"
|
||||
},
|
||||
{
|
||||
"tag": "20.1.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/20.1.0/Firmware.20.1.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "20.0.1",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/20.0.1/Firmware.20.0.1.zip"
|
||||
},
|
||||
{
|
||||
"tag": "20.0.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/20.0.0/Firmware.20.0.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "19.0.1",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/19.0.1/Firmware.19.0.1.zip"
|
||||
},
|
||||
{
|
||||
"tag": "19.0.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/19.0.0/Firmware.19.0.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "18.1.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/18.1.0/Firmware.18.1.0.zip"
|
||||
},
|
||||
{
|
||||
"tag": "18.0.1",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/18.0.1/Firmware.18.0.1.zip"
|
||||
},
|
||||
{
|
||||
"tag": "18.0.0",
|
||||
"download": "https://github.com/THZoria/NX_Firmware/releases/download/18.0.0/Firmware.18.0.0.zip"
|
||||
}
|
||||
]
|
||||
137
generate_fw_archive.py
Executable file
137
generate_fw_archive.py
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Fetch Nintendo Switch firmware release metadata from THZoria/NX_Firmware via the GitHub API
|
||||
and write a JSON list of {tag, download} for each release that has a .zip asset.
|
||||
|
||||
Example:
|
||||
python3 generate_nx_firmware_json.py
|
||||
python3 generate_nx_firmware_json.py -o archive.json --min 18.0.0
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from typing import Any
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
DEFAULT_REPO = "THZoria/NX_Firmware"
|
||||
DEFAULT_MIN_FW = "18.0.0"
|
||||
DEFAULT_OUTPUT = "archive.json"
|
||||
API_BASE = "https://api.github.com"
|
||||
|
||||
FW_TAG_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
|
||||
|
||||
|
||||
def parse_fw_tag(tag: str) -> tuple[int, int, int] | None:
|
||||
m = FW_TAG_RE.match(tag.strip())
|
||||
if not m:
|
||||
return None
|
||||
return int(m.group(1)), int(m.group(2)), int(m.group(3))
|
||||
|
||||
|
||||
def parse_min_version(s: str) -> tuple[int, int, int]:
|
||||
v = parse_fw_tag(s)
|
||||
if v is None:
|
||||
raise argparse.ArgumentTypeError("must look like MAJOR.MINOR.PATCH, e.g. 18.0.0")
|
||||
return v
|
||||
|
||||
|
||||
def fetch_releases(repo: str) -> list[dict[str, Any]]:
|
||||
"""Paginate GET /repos/{owner}/{repo}/releases."""
|
||||
releases: list[dict[str, Any]] = []
|
||||
page = 1
|
||||
headers = {
|
||||
"Accept": "application/vnd.github+json",
|
||||
"User-Agent": "generate-nx-firmware-json",
|
||||
}
|
||||
while True:
|
||||
url = f"{API_BASE}/repos/{repo}/releases?per_page=100&page={page}"
|
||||
req = Request(url, headers=headers)
|
||||
try:
|
||||
with urlopen(req, timeout=60) as resp:
|
||||
body = resp.read().decode("utf-8")
|
||||
except HTTPError as e:
|
||||
raise SystemExit(f"GitHub API error: {e.code} {e.reason}\n{e.read().decode('utf-8', errors='replace')}") from e
|
||||
except URLError as e:
|
||||
raise SystemExit(f"Request failed: {e}") from e
|
||||
|
||||
chunk = json.loads(body)
|
||||
if not isinstance(chunk, list):
|
||||
raise SystemExit("Unexpected API response (expected a JSON array)")
|
||||
releases.extend(chunk)
|
||||
if len(chunk) < 100:
|
||||
break
|
||||
page += 1
|
||||
return releases
|
||||
|
||||
|
||||
def pick_zip_url(assets: list[dict[str, Any]]) -> str | None:
|
||||
zips = [a for a in assets if str(a.get("name", "")).lower().endswith(".zip")]
|
||||
if not zips:
|
||||
return None
|
||||
zips.sort(key=lambda a: (0 if str(a.get("name", "")).lower().startswith("firmware") else 1, a.get("name", "")))
|
||||
return str(zips[0].get("browser_download_url") or "")
|
||||
|
||||
|
||||
def build_entries(
|
||||
releases: list[dict[str, Any]],
|
||||
min_version: tuple[int, int, int],
|
||||
) -> list[dict[str, str]]:
|
||||
out: list[dict[str, str]] = []
|
||||
for rel in releases:
|
||||
if rel.get("draft"):
|
||||
continue
|
||||
tag = str(rel.get("tag_name", "")).strip()
|
||||
ver = parse_fw_tag(tag)
|
||||
if ver is None or ver < min_version:
|
||||
continue
|
||||
url = pick_zip_url(rel.get("assets") or [])
|
||||
if not url:
|
||||
continue
|
||||
out.append({"tag": tag, "download": url})
|
||||
|
||||
out.sort(key=lambda e: parse_fw_tag(e["tag"]) or (0, 0, 0), reverse=True)
|
||||
return out
|
||||
|
||||
|
||||
def main() -> None:
|
||||
p = argparse.ArgumentParser(description="Build NX firmware JSON index from GitHub releases.")
|
||||
p.add_argument(
|
||||
"--repo",
|
||||
default=DEFAULT_REPO,
|
||||
help=f"owner/repo (default: {DEFAULT_REPO})",
|
||||
)
|
||||
p.add_argument(
|
||||
"--min",
|
||||
dest="min_fw",
|
||||
type=parse_min_version,
|
||||
default=parse_min_version(DEFAULT_MIN_FW),
|
||||
help=f"minimum firmware tag to include (default: {DEFAULT_MIN_FW})",
|
||||
)
|
||||
p.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
default=DEFAULT_OUTPUT,
|
||||
help=f"write JSON to this file (default: {DEFAULT_OUTPUT}); use - for stdout",
|
||||
)
|
||||
args = p.parse_args()
|
||||
|
||||
releases = fetch_releases(args.repo)
|
||||
entries = build_entries(releases, args.min_fw)
|
||||
text = json.dumps(entries, indent=2) + "\n"
|
||||
|
||||
if args.output == "-":
|
||||
sys.stdout.write(text)
|
||||
else:
|
||||
with open(args.output, "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
print(f"Wrote {len(entries)} entries to {args.output}", file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user