xiao.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import json
  2. import requests
  3. import re
  4. import sys
  5. import hashlib
  6. import os
  7. # 默认 jar 路径和下载 URL(如需下载)
  8. default_jar = "./xiaosa/spider.jar"
  9. # 如果需要自动下载 jar,可替换为真实链接;否则留空
  10. default_jar_url = "../xiaosa/spider.jar"
  11. # 保存 JSON 文件(折叠字典数组为单行,空数组和基础数组一行)
  12. class CompactJSONEncoder(json.JSONEncoder):
  13. def iterencode(self, o, _one_shot=False):
  14. def _compact_list(lst, indent_level):
  15. pad = ' ' * indent_level
  16. if not lst or all(isinstance(i, (str, int, float, bool, type(None))) for i in lst):
  17. return json.dumps(lst, ensure_ascii=False)
  18. if all(isinstance(i, dict) for i in lst):
  19. return '[\n' + ',\n'.join([pad + ' ' + json.dumps(i, ensure_ascii=False, separators=(',', ': ')) for i in lst]) + '\n' + pad + ']'
  20. return json.dumps(lst, ensure_ascii=False, indent=2)
  21. def _encode(obj, indent_level=0):
  22. pad = ' ' * indent_level
  23. if isinstance(obj, dict):
  24. lines = [f'"{k}": {_encode(v, indent_level+1)}' for k, v in obj.items()]
  25. return '{\n' + pad + ' ' + (',\n' + pad + ' ').join(lines) + '\n' + pad + '}'
  26. elif isinstance(obj, list):
  27. return _compact_list(obj, indent_level)
  28. return json.dumps(obj, ensure_ascii=False)
  29. return iter([_encode(o)])
  30. def fetch_json(path_or_url):
  31. if os.path.exists(path_or_url):
  32. # 本地文件
  33. with open(path_or_url, "r", encoding="utf-8") as f:
  34. return json.load(f)
  35. elif path_or_url.startswith("http://") or path_or_url.startswith("https://"):
  36. # 网络 URL
  37. resp = requests.get(path_or_url)
  38. resp.raise_for_status()
  39. return resp.json()
  40. else:
  41. raise ValueError(f"无效路径或 URL:{path_or_url}")
  42. def get_md5(filepath):
  43. md5 = hashlib.md5()
  44. with open(filepath, "rb") as f:
  45. while chunk := f.read(8192):
  46. md5.update(chunk)
  47. return md5.hexdigest()
  48. def ensure_jar_with_md5(site):
  49. if not isinstance(site, dict):
  50. return
  51. jar_val = site.get("jar")
  52. if jar_val and ";md5;" in jar_val:
  53. return # 已包含 md5
  54. if not os.path.exists(default_jar_url):
  55. print(f"⚠️ 找不到本地 jar 文件:{default_jar_url}")
  56. return
  57. md5_val = get_md5(default_jar_url)
  58. site["jar"] = f"{default_jar};md5;{md5_val}"
  59. def insert_sites_at_key(base_sites, insert_sites, key_marker):
  60. for i, item in enumerate(base_sites):
  61. if item.get("key") == key_marker:
  62. return base_sites[:i] + insert_sites + base_sites[i:]
  63. print(f"⚠️ 未找到 key 为 {key_marker} 的插入点,追加到末尾")
  64. return base_sites + insert_sites
  65. if __name__ == "__main__":
  66. if len(sys.argv) < 3:
  67. print("用法: python script.py <远程json_url> <本地dianshi.json路径>")
  68. print("示例: python script.py https://raw.githubusercontent.com/qist/tvbox/master/xiaosa/api.json dianshi.json")
  69. sys.exit(1)
  70. remote_url = sys.argv[1]
  71. local_file = sys.argv[2]
  72. # 1. 下载远程 JSON
  73. data = fetch_json(remote_url)
  74. # 2. 筛选 sites,只保留 name 含 APP
  75. sites = data.get("sites", [])
  76. filtered_sites = [s for s in sites if isinstance(s, dict) and "name" in s and "APP" in s["name"]]
  77. # 3. 为每个筛选 site 添加 jar 字段和 md5
  78. for site in filtered_sites:
  79. ensure_jar_with_md5(site)
  80. print(f"✅ 筛选并更新 {len(filtered_sites)} 个含 APP 的 sites(包含 md5 jar 字段)")
  81. # 4. 读取本地文件
  82. with open(local_file, "r", encoding="utf-8") as f:
  83. dianshi = json.load(f)
  84. # 5. 插入到 key="玩偶" 处
  85. dianshi_sites = dianshi.get("sites", [])
  86. dianshi["sites"] = insert_sites_at_key(dianshi_sites, filtered_sites, "玩偶")
  87. # 6. 保存合并结果
  88. output_file = f"{local_file.rsplit('.',1)[0]}_with_app_sites.json"
  89. with open(output_file, "w", encoding="utf-8") as f:
  90. json.dump(dianshi, f, ensure_ascii=False, indent=2, cls=CompactJSONEncoder)
  91. print(f"✅ 合并完成,已保存为 {output_file}")