314 lines
11 KiB
Python
314 lines
11 KiB
Python
# -*- 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()
|