import requests import re import demjson3 as demjson import json import hashlib import sys import os from pathlib import Path # 创建全局 session 并设置浏览器 UA session = requests.Session() COMMON_HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", } session.headers.update(COMMON_HEADERS) session.cookies.set("visited", "1") # 提高拟真度 # 下载伪 JSON 文本 def fetch_raw_json(): url = "http://www.xn--sss604efuw.art/jm/jiemi.php?url=http%3A%2F%2Fwww.%E9%A5%AD%E5%A4%AA%E7%A1%AC.art%2Ftv" resp = session.get(url, timeout=30, allow_redirects=True) resp.encoding = 'utf-8' return resp.text # 读取本地 JSON 文件 def read_local_json(file_path): """读取本地JSON文件,支持带注释的JSON和图片中的base64数据""" with open(file_path, 'rb') as f: raw_content = f.read() # 检查是否为图片文件 image_headers = [ b'\xff\xd8\xff\xe0', # JPEG b'\xff\xd8\xff\xe1', # JPEG b'\x89PNG', # PNG b'GIF87a', # GIF b'GIF89a', # GIF b'BM', # BMP ] is_image = False for header in image_headers: if raw_content.startswith(header): is_image = True break # 如果是图片,尝试提取嵌入的base64数据 if is_image: import base64 print(f" 检测到图片文件,尝试提取嵌入的base64数据...") try: text_content = raw_content.decode('latin-1') # 查找长base64字符串(至少50个字符) base64_pattern = r'[A-Za-z0-9+/=]{50,}' match = re.search(base64_pattern, text_content) if match: base64_str = match.group(0) print(f" 找到base64数据,长度: {len(base64_str)}") decoded = base64.b64decode(base64_str) content = decoded.decode('utf-8') # 移除JavaScript风格的注释 content = re.sub(r'^//.*$', '', content, flags=re.MULTILINE).strip() return content except Exception as e: print(f" 提取图片数据失败: {e}") # 普通文本文件处理 content = raw_content.decode('utf-8') # 移除JavaScript风格的注释 content = re.sub(r'^//.*$', '', content, flags=re.MULTILINE).strip() return content # 下载 spider 文件 def extract_and_save_spider(json_text): match = re.search(r'"spider"\s*:\s*"([^"]+)"', json_text) if not match: raise ValueError("未找到 spider 字段") full_spider = match.group(1) spider_url = full_spider.split(";")[0] print(f"📥 下载 spider 文件: {spider_url}") resp = session.get(spider_url, timeout=30, allow_redirects=True) with open("fan.txt", "wb") as f: f.write(resp.content) print("✅ 已保存为 fan.txt") # 计算本地文件 MD5 def get_md5(filepath): md5 = hashlib.md5() with open(filepath, "rb") as f: while chunk := f.read(8192): md5.update(chunk) return md5.hexdigest() # 删除不需要的 sites 项 + 替换链接 def clean_data(raw_text): # 统一把各种 GitHub 代理壳替换掉 raw_text = re.sub( r'https?://[^/]+/https://raw\.githubusercontent\.com/fantaiying7/EXT/refs/heads/main', './FTY', raw_text ) data = demjson.decode(raw_text) # keywords = [ # "豆", "饭太硬", "广告", "PanSso", "YpanSo", "xzso", "米搜", "夸搜", "Aliso", "YiSo" # ] # original_count = len(data.get("sites", [])) # data["sites"] = [ # s for s in data["sites"] # if not any(kw in s.get("key", "") or kw in s.get("name", "") for kw in keywords) # ] # print(f"🧹 清理 {data - len(data['sites'])} 条 sites") return data # 格式美化保存 class CompactJSONEncoder(json.JSONEncoder): def iterencode(self, o, _one_shot=False): def _compact_list(lst, indent_level): pad = ' ' * indent_level if all(isinstance(i, dict) for i in lst): return '[\n' + ',\n'.join([pad + ' ' + json.dumps(i, ensure_ascii=False, separators=(',', ': ')) for i in lst]) + '\n' + pad + ']' return json.dumps(lst, ensure_ascii=False, indent=2) def _encode(obj, indent_level=0): pad = ' ' * indent_level if isinstance(obj, dict): lines = [f'"{k}": {_encode(v, indent_level+1)}' for k, v in obj.items()] return '{\n' + pad + ' ' + (',\n' + pad + ' ').join(lines) + '\n' + pad + '}' elif isinstance(obj, list): return _compact_list(obj, indent_level) return json.dumps(obj, ensure_ascii=False) return iter([_encode(o)]) def save_json(data, filename="tvbox_cleaned.json"): with open(filename, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2, cls=CompactJSONEncoder) print(f"✅ 最终 JSON 保存为 {filename}") # 主流程 if __name__ == "__main__": try: # 判断输入源 if len(sys.argv) > 1: input_path = sys.argv[1] if input_path in ['-h', '--help']: print("用法:") print(" python fty.py [输入文件]") print(" python fty.py # 从URL获取数据") print(" python fty.py input.json # 从本地JSON文件读取") print(" python fty.py fff.json # 从本地图片文件读取(自动提取base64数据)") sys.exit(0) if os.path.isfile(input_path): print(f"📂 读取本地文件: {input_path}") raw_text = read_local_json(input_path) else: print(f"❌ 文件不存在: {input_path}") sys.exit(1) else: print("🌐 从URL获取数据...") raw_text = fetch_raw_json() extract_and_save_spider(raw_text) data = clean_data(raw_text) # 更新 spider 为本地 fan.txt + 最新 MD5 md5_value = get_md5("fan.txt") data["spider"] = f"./jar/fan.txt;md5;{md5_value}" print(f"🔄 spider 已更新为: {data['spider']}") save_json(data) except Exception as e: print(f"❌ 错误: {e}")