Files
2026-06-29 03:41:14 +00:00

276 lines
9.5 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import json
import time
import requests
import subprocess
import shutil
from pathlib import Path
# ======================
# 配置(全部走环境变量,带默认值)
# ======================
SOURCES_JSON_PATH = os.environ.get("SOURCES_JSON_PATH", "sources.json")
TARGET_JSON_PATH = os.environ.get("TARGET_JSON_PATH", "TV.json")
# Git 配置(新增)
GIT_REPO_DIR = os.environ.get("GIT_REPO_DIR", "./git_repo") # 本地克隆的仓库目录
GIT_REMOTE_URL = os.environ.get("GIT_REMOTE_URL", "") # Git 仓库远程地址,如:https://github.com/leexuben/TVBOX-merge.git 或 [email protected]:leexuben/TVBOX-merge.git
GIT_BRANCH = os.environ.get("GIT_BRANCH", "main") # 推送分支
GIT_COMMIT_MESSAGE = os.environ.get("GIT_COMMIT_MESSAGE", "🔄 自动更新 TV.json (站点 & Live)")
HEADERS = {
"User-Agent": "Mozilla/5.0 (compatible; TVBoxMerge/1.0)"
}
# ======================
# 拉取站点 / lives
# ======================
def get_data_from_url(url):
"""
返回:
{
"sites": [...],
"lives": [...]
}
"""
try:
r = requests.get(url, headers=HEADERS, timeout=15)
if r.status_code == 200:
data = json.loads(r.text)
sites = []
lives = []
if isinstance(data, dict):
if "sites" in data and isinstance(data["sites"], list):
sites = data["sites"]
if "lives" in data and isinstance(data["lives"], list):
lives = data["lives"]
elif isinstance(data, list):
# 兼容旧格式,当成 sites
sites = data
return {
"sites": sites,
"lives": lives
}
except Exception as e:
print(f"[拉取失败] {url} | {e}")
return {"sites": [], "lives": []}
# ======================
# 修复路径
# ======================
def fix_item_paths(item, base):
if not base:
return item
base = base.rstrip("/")
for k, v in item.items():
if isinstance(v, str) and v.startswith("./"):
item[k] = base + "/" + v[2:]
return item
# ======================
# Git 强制推送功能(新增核心部分)
# ======================
def git_force_push(target_file_path, commit_message):
"""
将指定的目标文件通过 Git 强制推送到远程仓库
"""
print("=" * 50)
print("[Git] 开始 Git 强制推送流程")
print(f"[Git] 目标文件: {target_file_path}")
print(f"[Git] 远程仓库: {GIT_REMOTE_URL}")
print(f"[Git] 分支: {GIT_BRANCH}")
print("=" * 50)
# 1. 检查是否设置了远程仓库地址
if not GIT_REMOTE_URL:
print("[Git] 错误: 未设置 GIT_REMOTE_URL 环境变量")
return False
# 2. 克隆或准备本地仓库目录
repo_dir = GIT_REPO_DIR
target_file = target_file_path
try:
# 如果目录已存在,先删除(确保干净)
if os.path.exists(repo_dir):
print(f"[Git] 清理旧仓库目录: {repo_dir}")
shutil.rmtree(repo_dir)
# 克隆仓库(裸克隆不推荐,我们直接克隆普通仓库)
print(f"[Git] 克隆仓库到: {repo_dir}")
clone_cmd = ["git", "clone", GIT_REMOTE_URL, repo_dir]
result = subprocess.run(clone_cmd, capture_output=True, text=True, timeout=30)
if result.returncode != 0:
print(f"[Git] 克隆失败: {result.stderr}")
return False
# 进入仓库目录
os.chdir(repo_dir)
# 检查是否有该文件,如果没有则从外部复制进来
if not os.path.exists(target_file):
# 假设目标文件在脚本同目录,复制到仓库目录
script_dir = os.path.dirname(os.path.abspath(__file__))
source_file = os.path.join(script_dir, target_file)
if os.path.exists(source_file):
print(f"[Git] 复制目标文件到仓库: {source_file} -> {repo_dir}/{target_file}")
shutil.copy2(source_file, repo_dir)
else:
print(f"[Git] 错误: 目标文件 {target_file} 不存在于脚本目录")
return False
# 拉取最新代码(避免冲突,虽然我们要强制推送)
print(f"[Git] 拉取最新代码 (git pull)")
pull_cmd = ["git", "pull", "origin", GIT_BRANCH]
pull_result = subprocess.run(pull_cmd, capture_output=True, text=True, timeout=20)
if pull_result.returncode != 0:
print(f"[Git] 拉取失败(可能首次克隆无更新): {pull_result.stderr}")
# 首次克隆可能没有远程分支,忽略错误
# 添加目标文件
print(f"[Git] 添加文件: {target_file}")
add_cmd = ["git", "add", target_file]
add_result = subprocess.run(add_cmd, capture_output=True, text=True, timeout=10)
if add_result.returncode != 0:
print(f"[Git] 添加文件失败: {add_result.stderr}")
return False
# 检查是否有变更
status_cmd = ["git", "status", "--porcelain"]
status_result = subprocess.run(status_cmd, capture_output=True, text=True, timeout=10)
if not status_result.stdout.strip():
print(f"[Git] 没有变更,无需提交")
return True
# 提交
print(f"[Git] 提交变更: {commit_message}")
commit_cmd = ["git", "commit", "-m", commit_message]
commit_result = subprocess.run(commit_cmd, capture_output=True, text=True, timeout=10)
if commit_result.returncode != 0:
print(f"[Git] 提交失败: {commit_result.stderr}")
return False
# 强制推送到远程
print(f"[Git] 强制推送分支: {GIT_BRANCH}")
push_cmd = ["git", "push", "origin", GIT_BRANCH, "--force"]
push_result = subprocess.run(push_cmd, capture_output=True, text=True, timeout=20)
if push_result.returncode != 0:
print(f"[Git] 强制推送失败: {push_result.stderr}")
return False
print(f"[Git] ✅ 强制推送成功!")
return True
except Exception as e:
print(f"[Git] 异常: {e}")
return False
# ======================
# 主流程
# ======================
def main():
print("=" * 60)
print("TVBox 站点 & Live 合并脚本")
print(f"时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
if not os.path.exists(SOURCES_JSON_PATH):
print(f"[错误] 找不到源配置文件: {SOURCES_JSON_PATH}")
sys.exit(1)
with open(SOURCES_JSON_PATH, "r", encoding="utf-8") as f:
sources = json.load(f)
print(f"[读取] 加载了 {len(sources)} 个源")
# ---------- 读取目标文件 ----------
if os.path.exists(TARGET_JSON_PATH):
with open(TARGET_JSON_PATH, "r", encoding="utf-8") as f:
target_data = json.load(f)
sites = target_data.get("sites", [])
lives = target_data.get("lives", [])
existing_fields = {
k: v for k, v in target_data.items()
if k not in ("sites", "lives")
}
print(f"[读取] 现有 sites: {len(sites)}")
print(f"[读取] 现有 lives: {len(lives)}")
else:
sites = []
lives = []
existing_fields = {}
site_keys = {s.get("key") for s in sites if s.get("key")}
live_names = {l.get("name") for l in lives if l.get("name")}
# ---------- 合并 ----------
added_sites = 0
added_lives = 0
for i, src in enumerate(sources, 1):
url = src.get("url")
base = src.get("base", "")
if not url:
continue
print(f"[{i}/{len(sources)}] 处理: {url}")
data = get_data_from_url(url)
# 合并 sites
for s in data.get("sites", []):
key = s.get("key")
if key and key not in site_keys:
sites.append(fix_item_paths(s, base))
site_keys.add(key)
added_sites += 1
# ✅ 合并 lives(按 name 去重)
for l in data.get("lives", []):
name = l.get("name")
if name and name not in live_names:
lives.append(fix_item_paths(l, base))
live_names.add(name)
added_lives += 1
# ---------- 写出 ----------
result = {
"sites": sites,
"lives": lives,
**existing_fields
}
with open(TARGET_JSON_PATH, "w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
print("=" * 60)
print(f"[完成] sites 总数: {len(sites)},新增: {added_sites}")
print(f"[完成] lives 总数: {len(lives)},新增: {added_lives}")
print(f"[完成] 已写入 {TARGET_JSON_PATH}")
print("=" * 60)
# ========== 新增:执行 Git 强制推送 ==========
print("\n" + "=" * 50)
print("[流程] 开始 Git 强制推送流程")
print("=" * 50)
git_success = git_force_push(TARGET_JSON_PATH, GIT_COMMIT_MESSAGE)
if git_success:
print("[最终] ✅ 脚本执行完成 & Git 强制推送成功")
else:
print("[最终] ⚠️ 脚本执行完成,但 Git 强制推送失败,请检查配置")
# ======================
# 入口
# ======================
if __name__ == "__main__":
main()