From a6b8c1823cc326bf9209b097e63d8627c1ffff19 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Wed, 28 May 2025 04:02:49 +0200 Subject: [PATCH] auto-git: [add] node/server/spirits/Neuer Ordner mit Objekten/generate_json.py --- .../generate_json.py | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 node/server/spirits/Neuer Ordner mit Objekten/generate_json.py diff --git a/node/server/spirits/Neuer Ordner mit Objekten/generate_json.py b/node/server/spirits/Neuer Ordner mit Objekten/generate_json.py new file mode 100644 index 0000000..95f8624 --- /dev/null +++ b/node/server/spirits/Neuer Ordner mit Objekten/generate_json.py @@ -0,0 +1,106 @@ +import json +import os +import re +from difflib import get_close_matches + +# --- Paste hier die Model-Dateinamen rein (oder lies sie aus dem Ordner) --- +MODEL_FILES = [ + "Ebisu.glb", "Enenra.glb", "Enenra2.glb", "Oboroguruma.glb", "Oiwa.glb", "Okiku.glb", "Okomeki.001.glb", + "Okuninushi.glb", "Oni.glb", "Onryo.glb", "Oyamatsumi.001.glb", "Raijin.glb", "Rokurokubi.glb", "Ryujin.glb", + "Sarutahiko_Okami.glb", "Shinigami.001.glb", "Shuten_Doji.glb", "Sojobo.glb", "Sojobo2.glb", "Susanoo.glb", + "Takeminakata.glb", "Takeminakata2.001.glb", "Tanuki.glb", "Tengu.glb", "Tenjin.glb", "Tsukumogami.glb", + "Tsukuyomi_No_Mikoto.glb", "Tsurube_Otoshi.glb", "Tsurube_Otoshi2.glb", "Tsurube_Otoshi3.glb", "Tsurube_Otoshi4.glb", + "Ubume.glb", "Yama_Uba.glb", "Yama_Uba2.glb", "Yamata_No_Orichi.glb", "Yamawaro.glb", "Yatagarasu2.glb", + "Yuki_Onna.glb", "Yurei.glb", "Abe_No_Seimei.glb", "Abura_Akago.glb", "Abura_Sumashi.glb", "Abura_Sumashi2.glb", + "Aka_Manto.glb", "Akaname.glb", "Akateko2.glb", "Akkorokamui.glb", "Akuchu.glb", "Amabie2.glb", "Amanojaku.glb", + "Amaterasu.glb", "Ame_No_Uzume.001.glb", "Amenominakanushi.glb", "Aoandon.001.glb", "Aoandon2.001.glb", + "Ashiari_Yashiki.glb", "Ashinaga_Tenaga2.glb", "Azukiarai.glb", "Azukibabaa.glb", "Azukihakari.glb", + "Bake_Kujira.glb", "Bake_Kujira2.glb", "Bake_Kujira3.glb", "Bakezori.glb", "Baku.glb", "Basan.glb", + "Benzaiten.glb", "Betobeto_San.glb", "Bishamonten.glb", "Biwa_Bokuboku.glb", "Chochin_Obake.glb", "Daidarabotchi.glb", + "Daikokuten+Text.glb", "Daikokuten.glb", "Fujin.glb", "Funayurei.glb", "Furaribi.glb", "Futakuchi_Onna.glb", + "Gaki.glb", "Gashadokuro.glb", "Hachiman.glb", "Hiderigami.001.glb", "Hitotsume_Kozo.glb", "Hoko.glb", + "Inari_Okami.glb", "Ittan_Momen2.glb", "Izanagi_No_Mikoto.glb", "Izanami_No_Mikoto.glb", "Jikininki.glb", + "Jorogumo3.glb", "Kamaitachi.glb", "Kamikiri.glb", "Kappa.glb", "Karakasa_Obake.glb", "Karakasa_Obake2.glb", + "Kitsune.glb", "Kodama.glb", "Kudan.glb", "Mizushi.glb", "Mokumokuren.glb", "Mujina.glb", "Nekomata.glb", + "Noppera_Bo.glb", "Nue.glb", "Nuppeppo2.glb", "Nurarihyon.glb", "Nure_Onna.glb", "Nurikabe.glb", "Nurikabe2.glb" +] + +def normalize(s): + s = s.lower() + s = re.sub(r"[^a-z0-9]", "", s) + s = s.replace("ou", "o") # YamatanoOrOchi vs Yamata_No_Orichi etc. + return s + +def generate_candidates(spirit_name): + # z.B. "Tsukuyomi (月読命)" => ["Tsukuyomi", "TsukuyomiNoMikoto", ...] + # Extrahiere lateinische Namen und alle Wörter, splitte auf Sonderzeichen + base = spirit_name.split()[0] + latin = re.split(r"\s|\(|(", spirit_name)[0] + # Alternativen, z.B. alles ohne Klammern, nur erstes Wort etc. + candidates = [latin] + candidates += [latin.replace("-", "_"), latin.replace("-", ""), latin.replace("_", ""), latin.title(), latin.upper()] + # Häufig bei Kami: _No_Mikoto-Suffix + if not latin.endswith("NoMikoto"): + candidates.append(latin + "NoMikoto") + candidates.append(latin + "_No_Mikoto") + # Auch mal alles Klein, Snake, Camel + return list(set([normalize(c) for c in candidates])) + +def find_best_model(spirit_name): + candidates = generate_candidates(spirit_name) + model_names = [f[:-4] for f in MODEL_FILES] # .glb weg + normalized_models = [normalize(n) for n in model_names] + # Alle Matches (ab Distanz <= 2 oder exaktes Substring-Match) + results = [] + for c in candidates: + for i, n in enumerate(normalized_models): + dist = levenshtein(c, n) + if dist <= 2 or c in n or n in c: + results.append(MODEL_FILES[i]) + # Unique! + results = sorted(list(set(results))) + if not results: + # Fuzzy best 3 + matches = get_close_matches(candidates[0], normalized_models, n=3, cutoff=0.6) + models = [MODEL_FILES[normalized_models.index(m)] for m in matches] + return models + return results + +# Levenshtein-Distanz +def levenshtein(a, b): + if a == b: return 0 + if not a: return len(b) + if not b: return len(a) + v0 = list(range(len(b) + 1)) + v1 = [0] * (len(b) + 1) + for i in range(len(a)): + v1[0] = i + 1 + for j in range(len(b)): + cost = 0 if a[i] == b[j] else 1 + v1[j + 1] = min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost) + v0, v1 = v1, v0 + return v0[len(b)] + +# ---- Hauptlogik ---- +def main(): + with open("spirit_list.json", encoding="utf-8") as f: + spirits = json.load(f) + for spirit in spirits: + if "Model URL" in spirit and spirit["Model URL"]: + continue # already done + name = spirit.get("Name", "") + matches = find_best_model(name) + if len(matches) == 1: + spirit["Model URL"] = "/assets/models/spirits/" + matches[0] + elif len(matches) > 1: + print(f"\n[?] Mehrere mögliche Modelle für '{name}': {matches}") + spirit["Model URL"] = "/assets/models/spirits/" + matches[0] # Default das erste, Handcheck empfohlen! + else: + print(f"[!] Kein Modell gefunden für '{name}'!") + spirit["Model URL"] = "" + with open("spirit_list_out.json", "w", encoding="utf-8") as f: + json.dump(spirits, f, ensure_ascii=False, indent=2) + print("\nFERTIG. Ergebnis: spirit_list_out.json") + +if __name__ == "__main__": + main() \ No newline at end of file