# -*- coding: utf-8 -*- """ 为 BetterGI AutoPathing 分类目录批量生成 ScriptGroup 配置组。 支持两类结构: - 两层(分类/食材):如"食材与炼金/<食材>/xxx.json" folderName = "<分类>\\<食材>" - 三层(分类/地区/特产):如"地方特产/<地区>/<特产>/xxx.json" folderName = "<分类>\\<地区>\\<特产>" 规则: - 食材/特产目录下若无子目录(json 直接平铺)→ 单一来源路线 → 生成配置组 - 食材/特产目录下若有子目录(多作者/多版本路线)→ 跳过,记录到未加入清单 用法: python generate_scriptgroups.py <分类名> 例: python generate_scriptgroups.py 食材与炼金 python generate_scriptgroups.py 地方特产 输出: - 在 ScriptGroup 目录下为每个可创建的项目生成 <名称>.json - 在项目目录下生成 生成报告_<分类名>.txt """ import os import json import sys BASE = r"C:\Program Files\BetterGI\BetterGI\User" AUTOPATHING = os.path.join(BASE, "AutoPathing") SCRIPTGROUP = os.path.join(BASE, "ScriptGroup") # 非路线文件,扫描时排除 IGNORE_NAMES = {"desktop.ini", "icon.ico", "Thumbs.db"} # 地区显示顺序(仅用于排序,地方特产用) REGION_ORDER = ["蒙德", "璃月", "稻妻", "须弥", "枫丹", "纳塔", "挪德卡莱"] def region_sort_key(name): try: return (REGION_ORDER.index(name), name) except ValueError: return (len(REGION_ORDER), name) def load_config_template(): """读取 子探测单元.json 作为 config 模板。""" template_path = os.path.join(SCRIPTGROUP, "子探测单元.json") if not os.path.exists(template_path): print(f"[错误] 找不到模板文件: {template_path}") sys.exit(1) with open(template_path, "r", encoding="utf-8") as f: return json.load(f)["config"] def get_next_group_index(): """扫描 ScriptGroup 下所有 json,返回下一个可用 index。""" max_index = 0 if not os.path.isdir(SCRIPTGROUP): return 1 for fn in os.listdir(SCRIPTGROUP): if not fn.lower().endswith(".json"): continue fp = os.path.join(SCRIPTGROUP, fn) try: with open(fp, "r", encoding="utf-8") as f: data = json.load(f) idx = data.get("index", 0) if isinstance(idx, int) and idx > max_index: max_index = idx except Exception: continue return max_index + 1 def detect_structure(category_path): """检测目录结构:返回 'two' (分类/食材) 或 'three' (分类/地区/特产)。 判定规则:扫描所有第二层目录,只要有一个直接包含 json 文件, 就判为两层结构(单作者食材直接放 json)。只有当所有第二层目录 都只含子目录(无直接 json)时,才判为三层(地区→特产)。 """ has_any_direct_json = False for entry in os.listdir(category_path): entry_path = os.path.join(category_path, entry) if not os.path.isdir(entry_path): continue sub_entries = [e for e in os.listdir(entry_path) if e not in IGNORE_NAMES] has_json = any( fn.lower().endswith(".json") and os.path.isfile(os.path.join(entry_path, fn)) for fn in sub_entries ) if has_json: has_any_direct_json = True break return "two" if has_any_direct_json else "three" def list_items(category_path, structure): """返回 [(group_key, item_name, item_path)] 列表。 - two: group_key=item_name, item_path=分类/食材 - three: group_key=(region, item_name), item_path=分类/地区/特产 """ result = [] if structure == "two": for item in os.listdir(category_path): ip = os.path.join(category_path, item) if not os.path.isdir(ip): continue result.append((item, item, ip)) result.sort(key=lambda x: x[0]) else: for region in os.listdir(category_path): rp = os.path.join(category_path, region) if not os.path.isdir(rp): continue for item in os.listdir(rp): ip = os.path.join(rp, item) if not os.path.isdir(ip): continue result.append(((region, item), item, ip)) result.sort(key=lambda x: (region_sort_key(x[0][0]), x[0][1])) return result def analyze_item(item_path): """分析项目目录,返回 (subdirs, json_files)。""" entries = os.listdir(item_path) subdirs = [e for e in entries if os.path.isdir(os.path.join(item_path, e)) and e not in IGNORE_NAMES] json_files = [e for e in entries if e.lower().endswith(".json") and os.path.isfile(os.path.join(item_path, e)) and e not in IGNORE_NAMES] return subdirs, sorted(json_files) def classify_subdirs(subdirs): """对有子目录的项目分类,返回 (类型, 说明)。""" authors = [] for sd in subdirs: if "@" in sd: author = sd.split("@", 1)[1] authors.append(author) else: authors.append(None) unique_authors = {a for a in authors if a is not None} has_unmarked = any(a is None for a in authors) if len(unique_authors) == 0: return "版本说明类", "所有子目录均无作者标识(版本/路线说明)" if has_unmarked: return "混合多路线", f"含无作者标识的版本目录;作者: {sorted(unique_authors)}" if len(unique_authors) == 1: return "同一作者多版本", f"作者: {next(iter(unique_authors))}" return "多作者", f"作者: {sorted(unique_authors)}" def build_group(group_index, name, folder_name, json_files, config_template): """构造一个配置组 dict。""" projects = [] for i, jf in enumerate(json_files, 1): projects.append({ "name": jf, "folderName": folder_name, "jsScriptSettingsObject": None, "index": i, "type": "Pathing", "status": "Enabled", "schedule": "Daily", "runNum": 1, "allowJsNotification": True, "allowJsHTTPHash": "" }) return { "index": group_index, "name": name, "config": config_template, "projects": projects } def main(): if len(sys.argv) < 2: print("用法: python generate_scriptgroups.py <分类名>") print("例: python generate_scriptgroups.py 食材与炼金") sys.exit(1) category = sys.argv[1] category_path = os.path.join(AUTOPATHING, category) if not os.path.isdir(category_path): print(f"[错误] 分类目录不存在: {category_path}") sys.exit(1) config_template = load_config_template() report_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), f"生成报告_{category}.txt" ) structure = detect_structure(category_path) print(f"[信息] 分类 '{category}' 结构: {'两层(分类/食材)' if structure == 'two' else '三层(分类/地区/特产)'}") items = list_items(category_path, structure) print(f"[信息] 共扫描到 {len(items)} 个项目") created = [] # (group_key, name, route_count, file_path) skipped = [] # (group_key, name, subdirs, 类型, 说明) write_errors = [] # (name, error) group_index = get_next_group_index() print(f"[信息] 起始 group index: {group_index}") for group_key, name, item_path in items: subdirs, json_files = analyze_item(item_path) if not subdirs: # 无子目录 → 创建配置组 if not json_files: skipped.append((group_key, name, [], "空目录", "目录下无路线文件")) continue # 构造 folderName if structure == "two": folder_name = f"{category}\\{name}" else: region, _ = group_key folder_name = f"{category}\\{region}\\{name}" group = build_group(group_index, name, folder_name, json_files, config_template) out_path = os.path.join(SCRIPTGROUP, f"{name}.json") try: with open(out_path, "w", encoding="utf-8") as f: json.dump(group, f, ensure_ascii=False, indent=2) created.append((group_key, name, len(json_files), out_path)) group_index += 1 except PermissionError as e: write_errors.append((name, f"权限不足: {e}")) except Exception as e: write_errors.append((name, str(e))) else: # 有子目录 → 跳过 kind, desc = classify_subdirs(subdirs) skipped.append((group_key, name, subdirs, kind, desc)) # 生成报告 lines = [] lines.append("=" * 70) lines.append(f"BetterGI {category} 配置组生成报告") lines.append("=" * 70) lines.append("") lines.append(f"【已创建配置组】共 {len(created)} 个") lines.append("-" * 70) def group_label(gk): if structure == "two": return gk else: return f"{gk[0]} / {gk[1]}" # 按分组打印已创建 if structure == "two": for gk, name, cnt, fp in created: lines.append(f" {name} ({cnt} 条路线) -> {os.path.basename(fp)}") else: cur_region = None for gk, name, cnt, fp in created: region = gk[0] if region != cur_region: cur_region = region lines.append(f"\n[{region}]") lines.append(f" {name} ({cnt} 条路线) -> {os.path.basename(fp)}") lines.append("") lines.append(f"【未加入配置组(需手动添加)】共 {len(skipped)} 个") lines.append("-" * 70) if structure == "two": for gk, name, subdirs, kind, desc in skipped: lines.append(f" {name}") lines.append(f" 类型: {kind}") lines.append(f" 说明: {desc}") if subdirs: lines.append(f" 子目录: {subdirs}") lines.append("") else: cur_region = None for gk, name, subdirs, kind, desc in skipped: region = gk[0] if region != cur_region: cur_region = region lines.append(f"\n[{region}]") lines.append(f" {name}") lines.append(f" 类型: {kind}") lines.append(f" 说明: {desc}") if subdirs: lines.append(f" 子目录: {subdirs}") lines.append("") if write_errors: lines.append("【写入错误】") lines.append("-" * 70) for name, err in write_errors: lines.append(f" {name}: {err}") lines.append("") lines.append("=" * 70) report = "\n".join(lines) with open(report_path, "w", encoding="utf-8") as f: f.write(report) print(report) print(f"\n报告已保存: {report_path}") if __name__ == "__main__": main()