Complete curated-list import automation and vault sync.

Adds list-import tooling, expands romhack-import for PC variants and patch batches, and syncs catalog with 2026-06-23 valhalla placements (Vanguard, Xenoverse, GBA patches, Infinity 3.3.1).
This commit is contained in:
ginnoir
2026-06-23 03:57:50 -05:00
parent 0537ceba5d
commit 52ee1b98a1
8 changed files with 1897 additions and 73 deletions
+221
View File
@@ -0,0 +1,221 @@
#!/usr/bin/env python3
"""Parse curated URL lists, diff against wiki/catalog.json, report gaps."""
import json, re, unicodedata
from pathlib import Path
CATALOG = Path(__file__).resolve().parents[1] / "wiki" / "catalog.json"
# Games extracted from the 7 source lists (deduped, normalized keys).
LIST_ENTRIES = [
# UfzGCYzA
("Abstract", "https://www.pokecommunity.com/threads/pokémon-abstract-version.527324/"),
("Alchemist", "https://www.pokecommunity.com/threads/pokémon-alchemist.378280/"),
("AfricanVs", "https://mega.nz/file/UgZj3BCQ#Xc6243i1iMfBTTJR7NfEsA7mFgfCFj37e7_u_B1EvMM"),
("Awakening", "https://www.pokecommunity.com/threads/pokémon-awakening.535910/"),
("Bizarre", "https://drive.google.com/file/d/1NXXr96vzeMD8sGplV1sWZFSph2vZijaK/view"),
("Bushido", "https://eeveeexpo.com/bushido/"),
("Empyrean", "https://www.pokecommunity.com/threads/pokémon-empyrean-v1-1-final-arc-released.411322/"),
("Eon Guardians", "https://www.pokecommunity.com/threads/pokémon-eon-guardians.527913/"),
("Floral Tempus", "https://www.pokecommunity.com/threads/pokémon-floral-tempus-ex-1-9-4-released-12-gym-badges-elite-4-champion.409175/"),
("HGSS Sevii", "https://www.pokecommunity.com/threads/pokémon-hgss-sevii-islands-hoenn-postgame.434636/"),
("Infinity", "https://eeveeexpo.com/infinity/"),
("Insurgence", "https://p-insurgence.com"),
("Itinerant", "https://www.pokecommunity.com/threads/pokemon-itinerant.528218/"),
("Infinite Fusion", "https://www.pokecommunity.com/threads/v6-4-pokémon-infinite-fusion-new-pokémon-character-customization-and-more.347883/"),
("Legends of the Arena", "https://www.pokecommunity.com/threads/pokémon-legends-of-the-arena.298738/"),
("Mega Adventures", "https://www.pokecommunity.com/threads/pokemon-mega-adventure.362058/"),
("Nightmare", "https://www.pokecommunity.com/threads/pokémon-nightmare.504856/"),
("Pokerogue", "https://pokerogue.net"),
("Poketale", "https://www.mediafire.com/file/n6yzxnclf2uys7c/Poketale.zip/file"),
("Realidea System", "https://www.pokecommunity.com/threads/pokémon-realidea-system-now-playable-in-english.481090/"),
("Reborn", "https://www.rebornevo.com/pr/index.html/"),
("Reloaded", "https://pokemon-reloaded.blogspot.com"),
("Reminiscencia", "https://www.pokecommunity.com/threads/pokémon-reminiscencia-full-release-spa-en-translation-complete.529202/"),
("Shattered Light", "https://eeveeexpo.com/shattered-light/"),
("Solar Light Lunar Dark", "https://pokemonsolarlightlunardark.fandom.com/wiki/Pokémon_Solar_Light_%26_Lunar_Dark_Wiki"),
("Tectonic", "https://www.tectonic-game.com"),
("Uranium", "https://pokemon-uranium.fandom.com/wiki/Main_Page"),
("Xenoverse", "https://mega.nz/file/FbBgiTTK#qJpzZeUb5XqO5rLt6meW5_tOXeQ3K6y3_mslGNUf41M"),
("Zeta", "https://zo.p-insurgence.com"),
("Atlas", None), ("Unbreakable Ties", None), ("Bioterror", None), ("Fuso Meteor", None),
("Comet", None), ("Ruckus", None), ("Island", None), ("Impetu", None),
("Slowpoke Shack", None), ("Empire", None), ("Flux", None),
# S1CSW465
("Consonancia", "https://www.pokecommunity.com/threads/pokémon-consonancia-full-game.537329/"),
("Concealed", "https://www.pokecommunity.com/threads/concealed.527325/"),
("Chaos in Vesita", "https://www.pokecommunity.com/threads/pokémon-chaos-in-vesita-new-version-out-now-eng-ger.491767/"),
("Dark Horizon", "https://eeveeexpo.com/threads/9332/"),
("Decay", "https://eeveeexpo.com/decay/"),
("Fire Ash", "https://www.pokecommunity.com/threads/pokémon-fire-ash.399096/"),
("Gaidr Deluxe", "https://www.pokecommunity.com/threads/pokémon-gadir-deluxe-complete-v-1-3.539451/"),
("Hero Legacy", "https://www.pokecommunity.com/threads/pokémon-hero-legacy-eng-de-now-with-new-game-mode.538518/"),
("Hollow Woods", "https://www.pokecommunity.com/threads/pokémon-hollow-woods.525865/"),
("Hydro Bliss", "https://eeveeexpo.com/threads/9100/"),
("Jam Festival", "https://www.pokecommunity.com/threads/pkmn-jam-festival.536702/"),
("Keishou", "https://www.pokecommunity.com/threads/pokémon-keishou-v2-06-1.504806/"),
("Prisme", "https://pokemonprisme.com"),
("Re:Union", "https://projetreunion.com"),
("Relict", "https://phiongames.blogspot.com/2025/12/descarga-pokemon-relict.html"),
# 6wPjUcrE
("Spades and Clubs", "https://senta-storage-system.neocities.org/main"),
("Giratina's Legend", "https://www.pokecommunity.com/threads/pokemon-giratinas-legend-07-01-24.289561"),
("Crown", "https://www.pokecommunity.com/threads/pokémon-crown.463821"),
("Odyssey II", None), # Discord only
("Coral", "https://www.pokecommunity.com/threads/pokémon-coral-version-2022-demo-out-now.402361"),
("DarkFire", "https://www.pokecommunity.com/threads/new-2024-beta-2-1-out-now-pokémon-darkfire.421395/"),
("Sovereign of the Skies", "https://www.pokecommunity.com/threads/sovereign-of-the-skies-new-beta-2-0-0-version-released-06-10-2021.292651/"),
("Legacy Edition", None), # Discord only
("Iridium", None), # Discord only
("Light Platinum DS", None), # Twitter only
# ZwA8ng8E
("Deserted", "https://eeveeexpo.com/threads/5344/"),
# 1S4acLQA
("Saiph", "https://www.pokecommunity.com/threads/pokémon-saiph-the-vytroverse-part-1-full-game-released.420990"),
("Sors", "https://www.pokecommunity.com/threads/pokémon-sors-the-vytroverse-part-2-full-game-v1-3-available-now.433238"),
("Saiph 2", "https://www.pokecommunity.com/threads/pokémon-saiph-2-the-vytroverse-part-3-full-game-v1-4-0-update-available.458509"),
("Sors 2", "https://www.pokecommunity.com/threads/pokémon-sors-2-the-vytroverse-part-4-tech-demo-available.526108"),
("Victory Fire", "https://www.pokecommunity.com/threads/pokemon-victory-fire-version-2-75-released.285031/"),
("Resolute", "https://www.pokecommunity.com/threads/pokemon-resolute-version-version-2-97-released.294565/"),
("Mega Power", "https://www.pokecommunity.com/threads/pokemon-mega-power-completed-beta-5-73-released.325422/"),
("Nameless", "https://www.pokecommunity.com/threads/pokemon-nameless-version-complete-beta-5-35-released-with-darker-future-episode.419717/"),
("Ruby Destiny", None),
("ROWE", "https://www.pokecommunity.com/threads/pokemon-r-o-w-e-2-0-an-open-world-version-of-pokémon-emerald-gen-9-16-badges-sevii-islands-following-pokémon-costumes-and-much-more.442592/"),
("Gaia", "https://www.pokecommunity.com/threads/pokémon-gaia-version.326118/"),
("Vanguard", None),
# tARt0w7x
("Adventure Red", "https://www.pokecommunity.com/threads/pokémon-adventure-red-chapter-new-beta-expansion.298920/"),
("AshGray", "https://www.pokecommunity.com/threads/pokémon-ashgray-version-beta-4-5-released.180722/"),
("Azure Horizons", "https://hacksrepairman.blogspot.com/2020/03/pokemon-azure-horizons-fixed-beta2.html"),
("Crystal Clear", "https://shockslayer.com/crystal-clear/"),
("Dark Violet", "https://www.pokecommunity.com/threads/pokémon-darkviolet-full-version-released.291789/"),
("Dreams", "https://www.pokecommunity.com/threads/dreams-completed-version-1-5-1-now-available.443298/"),
("Elite Redux", "https://www.pokecommunity.com/threads/pokémon-elite-redux-v2-1-complete-—-unique-multi-ability-difficulty-hack.499227/"),
("Emerald Rogue", "https://www.pokecommunity.com/threads/pokemon-emerald-rogue.479406/"),
("Emerald Seaglass", "https://ko-fi.com/s/4a1535f351"),
("Elysium", "https://www.pokecommunity.com/threads/pokémon-elysium-gba.502953/"),
("Fire Red Extended", "https://www.pokecommunity.com/threads/pokémon-fire-red-extended-version.466535/"),
("Flora Sky", "https://www.pokemoncoders.com/pokemon-flora-sky/"),
("Glazed", "https://www.pokemoncoders.com/pokemon-glazed/"),
("Light Platinum", "https://www.pokemoncoders.com/pokemon-light-platinum-rom-hack/"),
("Liquid Crystal", "https://www.pokecommunity.com/threads/pokémon-liquid-crystal-3-3-xxxxx-live-beta.242023/"),
("Mariomon", "https://www.pokecommunity.com/threads/super-mariomon.535764/"),
("Odyssey", "https://www.pokecommunity.com/threads/pokémon-odyssey-complete-v4-0-1.488536/"),
("Pisces", "https://pebblerplatoon.miraheze.org/wiki/Pisces_Download"),
("Prism", "https://rainbowdevs.com/"),
("Polished Crystal", "https://www.pokecommunity.com/threads/pokémon-polished-crystal-update-3-1-1.373172/"),
("Radical Red", "https://www.pokecommunity.com/threads/pokémon-radical-red-version-4-1-released-gen-9-dlc-pokemon-character-customization-now-available.437688/"),
("Recharged Series", "https://jaizu.moe"),
("FireRed Rocket Edition", "https://www.pokecommunity.com/threads/pokémon-firered-rocket-edition-completed.360725/"),
("Scorched Silver", "https://www.pokecommunity.com/threads/pokémon-scorched-silver-v1-3-complete.529230/"),
("Sienna", "https://www.pokecommunity.com/threads/hack-of-the-year-2010-pokémon-sienna-complete-version-released.202372/"),
("Snakewood", "https://www.pokecommunity.com/threads/pokémon-snakewood-version.235371/"),
("Team Rocket Edition", "https://www.pokecommunity.com/threads/pokémon-team-rocket-edition-dragonsden-version-kanto-sevii-johto-next-release-dlc-season-4.527368/"),
("The Pit", "https://www.pokecommunity.com/threads/the-pit-v2-roguelite-style-hack.528423/"),
("Unbound", "https://www.pokecommunity.com/threads/pokémon-unbound-completed.382178/"),
("Vega", "https://www.pokecommunity.com/threads/pokémon-vega-and-altair-sirius-english-version.365959/"),
("Vega Fairy EX", "https://www.pokecommunity.com/threads/pokemon-vega-fairy-edition-ex-minus-v1-4-balance-updates-and-skarmory-evo.475538/"),
# Google Doc
("Lazarus", "https://ko-fi.com/nemo622"),
("Renegade Platinum", "https://projectpokemon.org/home/forums/topic/52294-pokémon-renegade-platinum/"),
("Heart n Soul", "https://www.hackdex.app/hack/pokemon-heart-and-soul"),
("Dreamstone Mysteries", "https://github.com/dsmyst/dreamstone-mysteries"),
("Rejuvenation", None),
]
ALIASES = {
"adventure red": "adventure red chapter",
"ashgray": "ash gray",
"ash gray": "ashgray",
"firered rocket edition": "firered rocket edition",
"fire red extended": "fire red extended",
"darkfire": "dark fire",
"gaidr deluxe": "gadir deluxe",
"reunion": "re union",
"re:union": "re union",
"re union dx": "re union",
"rowe": "r o w e",
"r.o.w.e": "r o w e",
"itinerant": "iternant",
"keishou": "kieshou",
"giratina's legend": "giratina",
"vega fairy ex": "vega fairy",
"team rocket edition": "team rocket edition",
"mega adventures": "mega adventure",
"reminiscencia": "reminiscensia",
"spades and clubs": "spades",
"odyssey ii": "odyssey 2",
"heroes of lemuria": "odyssey 2",
"light platinum ds": "light platinum ds",
"heart n soul": "heart and soul",
"recharged series": "recharged",
}
def norm(s: str) -> str:
s = unicodedata.normalize("NFKD", s).encode("ascii", "ignore").decode().lower()
s = re.sub(r"^pok[eé]mon\s+", "", s)
s = re.sub(r"[^a-z0-9]+", " ", s).strip()
return ALIASES.get(s, s)
def load_catalog():
data = json.loads(CATALOG.read_text(encoding="utf-8"))
by_norm = {}
for h in data["hacks"]:
title = h.get("title") or h.get("stem", "")
key = norm(title)
by_norm.setdefault(key, []).append(h)
if h.get("stem"):
by_norm.setdefault(norm(h["stem"]), []).append(h)
return by_norm
def match_catalog(name, catalog):
key = norm(name)
hits = catalog.get(key, [])
if hits:
return hits[0]
# fuzzy: substring
for ck, hits in catalog.items():
if key in ck or ck in key:
return hits[0]
return None
def main():
catalog = load_catalog()
seen = set()
rows = []
for name, url in LIST_ENTRIES:
nk = norm(name)
if nk in seen:
continue
seen.add(nk)
hit = match_catalog(name, catalog)
rows.append({
"name": name,
"url": url,
"in_catalog": hit is not None,
"catalog_title": hit["title"] if hit else None,
"platform": hit.get("platform") if hit else None,
"library_path": hit.get("library_path") if hit else None,
})
missing_cat = [r for r in rows if not r["in_catalog"]]
in_cat = [r for r in rows if r["in_catalog"]]
print(f"Total unique list entries: {len(rows)}")
print(f"In catalog: {len(in_cat)}")
print(f"NOT in catalog: {len(missing_cat)}")
print("\n=== NOT IN CATALOG ===")
for r in sorted(missing_cat, key=lambda x: x["name"].lower()):
print(f" {r['name']:30} {r['url'] or '(no URL)'}")
print("\n=== IN CATALOG (for library check) ===")
for r in sorted(in_cat, key=lambda x: x["name"].lower()):
lp = r["library_path"] or ""
print(f" {r['name']:30} | {r['platform'] or '?':5} | {lp}")
if __name__ == "__main__":
main()