From c4d1a77db8fb90053f6a45a010c470d5d588918d Mon Sep 17 00:00:00 2001 From: qist <87984115@qq.com> Date: Sat, 29 Aug 2026 13:14:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20copy=5Fxbpq=20=E6=BC=8F?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=20ext=20=E5=AD=97=E5=85=B8=E5=86=85=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E6=96=87=E4=BB=B6=EF=BC=9B=E6=9B=B4=E6=96=B0=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E8=B7=AF=E5=BE=84=E4=B8=8E=E6=8F=92=E5=85=A5?= =?UTF-8?q?=E6=A0=87=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/run.yml | 8 ++-- tools/copy_xbpq.py | 88 +++++++++++++++++---------------------- tools/dianshi.json | 2 - tools/jsm.json | 1 - tools/xiao.py | 6 +-- 5 files changed, 46 insertions(+), 59 deletions(-) diff --git a/.github/workflows/run.yml b/.github/workflows/run.yml index d27d48c5..59e8bf11 100644 --- a/.github/workflows/run.yml +++ b/.github/workflows/run.yml @@ -34,12 +34,12 @@ jobs: python tvbox.py https://9877.kstore.space/one.json xs.json dec python build_local.py xs.json output \cp -pdr output/* ../xiaosa/ - python xiao.py ../xiaosa/api.json dianshi.json - python xiao.py ../xiaosa/api.json jsm.json - python copy_xbpq.py dianshi_with_app_sites.json + python xiao.py ./output/api.json dianshi.json + python xiao.py ./output/api.json jsm.json + python copy_xbpq.py ./output/api.json \cp -pdr dianshi_with_app_sites.json ../dianshi.json \cp -pdr jsm_with_app_sites.json ../jsm.json - \cp -pdr ../xiaosa/spider.jar ../jar/spider.jar + \cp -pdr ./output/spider.jar ../jar/spider.jar rm -rf dianshi_with_app_sites.json jsm_with_app_sites.json output xs.json cd ../ shell: bash diff --git a/tools/copy_xbpq.py b/tools/copy_xbpq.py index 11a0fa3b..628ff345 100644 --- a/tools/copy_xbpq.py +++ b/tools/copy_xbpq.py @@ -9,55 +9,42 @@ def load_json(path): return json.load(f) -def find_index_by_key(sites, key): - for i, item in enumerate(sites): - if isinstance(item, dict) and item.get("key") == key: - return i - return -1 - - def extract_local_paths(value): - """提取字符串中的本地路径(支持 ./ 和 ../),忽略 URL""" + """提取字符串中的本地路径(支持 ./ 和 ../),忽略 URL。按 ? 与常见分隔符切分。""" if not isinstance(value, str): return [] paths = [] - # 先按 ? 切分,保留路径及查询参数里的路径 - parts = value.split("?") - for part in parts: - # 再按常见分隔符切 + for part in value.split("?"): for token in part.replace("&", " ").replace("$", " ").split(): token = token.strip() if token.startswith("./") or token.startswith("../"): - # 去掉可能的尾部参数 - token = token.split("&", 1)[0] - token = token.split("$", 1)[0] + token = token.split("&", 1)[0].split("$", 1)[0] paths.append(token) return paths -def iter_paths_between(sites, start_key, end_key): - start_idx = find_index_by_key(sites, start_key) - end_idx = find_index_by_key(sites, end_key) - if start_idx == -1 or end_idx == -1 or start_idx >= end_idx: - return [] - subset = sites[start_idx + 1 : end_idx] +def collect_local_paths(obj): + """递归收集任意结构(dict/list/str)中的本地路径字符串""" + found = [] + if isinstance(obj, str): + found.extend(extract_local_paths(obj)) + elif isinstance(obj, dict): + for v in obj.values(): + found.extend(collect_local_paths(v)) + elif isinstance(obj, list): + for v in obj: + found.extend(collect_local_paths(v)) + return found + + +def iter_all_local_paths(sites): + """提取整份 sites 里所有本地路径(不限制区间),递归含 ext 字典""" all_paths = [] - for item in subset: - if not isinstance(item, dict): - continue - # api 仅拷贝 .py - api_val = item.get("api") - if isinstance(api_val, str): - for p in extract_local_paths(api_val): - if p.endswith(".py"): - all_paths.append(p) - # ext 拷贝所有本地路径 - ext_val = item.get("ext") - if isinstance(ext_val, str): - all_paths.extend(extract_local_paths(ext_val)) + for item in sites: + if isinstance(item, dict): + all_paths.extend(collect_local_paths(item)) # 去重,保留顺序 - seen = set() - deduped = [] + seen, deduped = set(), [] for p in all_paths: if p not in seen: seen.add(p) @@ -66,38 +53,41 @@ def iter_paths_between(sites, start_key, end_key): def main(): - if len(sys.argv) < 2: - print("用法: python copy_xbpq.py ") - print("示例: python copy_xbpq.py jsm_with_app_sites.json") + base_dir = os.path.dirname(os.path.abspath(__file__)) + if len(sys.argv) >= 2: + json_path = sys.argv[1] + else: + json_path = os.path.join(base_dir, "output", "api.json") + if not os.path.exists(json_path): + print(f"⚠️ 找不到文件: {json_path}") sys.exit(1) - json_path = sys.argv[1] data = load_json(json_path) sites = data.get("sites", []) - paths = iter_paths_between(sites, "qiletv", "电影天堂") + # 直接提取整份 json 的全部本地路径,不做区间判断 + paths = iter_all_local_paths(sites) if not paths: - print("⚠️ 未找到 qiletv 到 电影天堂 之间的 ./XBPQ/ 电影天堂") + print("⚠️ 未在 sites 中找到任何本地文件") return - base_dir = os.path.dirname(os.path.abspath(__file__)) - src_base = os.path.normpath(os.path.join(base_dir, "..", "xiaosa")) + # 源来自 output 目录,按 json 相对路径复制到项目根(保留子目录,避免 404 且 py/js/json 不混淆) + src_base = os.path.normpath(os.path.join(base_dir, "output")) dst_base = os.path.normpath(os.path.join(base_dir, "..")) copied = 0 missing = 0 for path in paths: - rel_path = path.replace("./", "", 1) - rel_path = rel_path.replace("../", "", 1) - src = os.path.join(src_base, rel_path) - dst = os.path.join(dst_base, rel_path) + rel = path.replace("./", "", 1).replace("../", "", 1) + src = os.path.join(src_base, rel) + dst = os.path.join(dst_base, rel) if not os.path.exists(src): print(f"⚠️ 源文件不存在: {src}") missing += 1 continue os.makedirs(os.path.dirname(dst), exist_ok=True) shutil.copy2(src, dst) - print(f"✅ 已覆盖: {dst}") + print(f"✅ 已复制: {dst}") copied += 1 print(f"完成: 复制 {copied} 个,缺失 {missing} 个") diff --git a/tools/dianshi.json b/tools/dianshi.json index b9832c88..0b660183 100644 --- a/tools/dianshi.json +++ b/tools/dianshi.json @@ -16,8 +16,6 @@ {"key":"cupfox_in","name":"茶杯狐 • cupfox.in","type":3,"api":"./lib/drpy2.min.js","ext":"./js/茶杯狐.js","searchable":1,"quickSearch":1,"filterable":0,"changeable":1,"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}}, {"key":"italkbbtv","name":"ITalkBB • 外","type":3,"api":"./py/ITalkBBTV.py","searchable":1,"quickSearch":1,"filterable":0,"changeable":1}, {"key": "TVB云播","name": "TVB云播","type": 3,"api": "./py/TVB云播.py","searchable": 1,"quickSearch": 1,"filterable": 0,"changeable": 1}, -{"key":"qiletv","name":"奇乐影视","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":"./json/奇乐.json","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}}, - {"key": "电影天堂","name": "影视 • 电影天堂","type": 1,"api": "http://caiji.dyttzyapi.com/api.php/provide/vod","searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"playerType":2}, {"key": "天涯采集","name": "影视 • 天涯","type": 1,"api": "http://tyyszy.com/api.php/provide/vod","style": {"type": "rect","ratio": 1.33},"searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"playerType":2}, diff --git a/tools/jsm.json b/tools/jsm.json index c41ab9a2..04feafa0 100644 --- a/tools/jsm.json +++ b/tools/jsm.json @@ -17,7 +17,6 @@ {"key":"cupfox_in","name":"茶杯狐 • cupfox.in","type":3,"api":"./lib/drpy2.min.js","ext":"./js/茶杯狐.js","searchable":1,"quickSearch":1,"filterable":0,"changeable":1,"header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}}, {"key":"italkbbtv","name":"ITalkBB • 外","type":3,"api":"./py/ITalkBBTV.py","searchable":1,"quickSearch":1,"filterable":0,"changeable":1}, {"key": "TVB云播","name": "TVB云播","type": 3,"api": "./py/TVB云播.py","searchable": 1,"quickSearch": 1,"filterable": 0,"changeable": 1}, -{"key":"qiletv","name":"奇乐影视","type":3,"api":"csp_XBPQ","searchable":1,"quickSearch":1,"filterable":1,"changeable":1,"ext":"./json/奇乐.json","header":{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0"}}, {"key": "电影天堂","name": "影视 • 电影天堂","type": 1,"api": "http://caiji.dyttzyapi.com/api.php/provide/vod","searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"playerType":2}, {"key": "天涯采集","name": "影视 • 天涯","type": 1,"api": "http://tyyszy.com/api.php/provide/vod","style": {"type": "rect","ratio": 1.33},"searchable": 1,"quickSearch": 1,"filterable": 1,"changeable": 1,"playerType":2}, diff --git a/tools/xiao.py b/tools/xiao.py index 202cbcb7..dbe4c3cf 100644 --- a/tools/xiao.py +++ b/tools/xiao.py @@ -62,7 +62,7 @@ def replace_drpy_path(site): def insert_sites_at_key(base_sites, insert_sites, key_marker): for i, item in enumerate(base_sites): if item.get("key") == key_marker: - return base_sites[:i + 1] + insert_sites + base_sites[i + 1:] + return base_sites[:i] + insert_sites + base_sites[i:] print(f"⚠️ 未找到 key 为 {key_marker} 的插入点,追加到末尾") return base_sites + insert_sites @@ -110,13 +110,13 @@ if __name__ == "__main__": with open(local_file, "r", encoding="utf-8") as f: dianshi = json.load(f) - # 5. 插入到 key="cbh" 之后(按 name 去重,保留本地) + # 5. 插入到 key="电影天堂" 之后(按 name 去重,保留本地) dianshi_sites = dianshi.get("sites", []) # 先按 key 删除来源站点 if remove_keys: filtered_sites = [s for s in filtered_sites if s.get("key") not in remove_keys] filtered_sites = dedupe_by_name(dianshi_sites, filtered_sites) - dianshi["sites"] = insert_sites_at_key(dianshi_sites, filtered_sites, "qiletv") + dianshi["sites"] = insert_sites_at_key(dianshi_sites, filtered_sites, "电影天堂") # 6. 删除指定的站点 # before_count = len(dianshi["sites"])