syn_local.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import os
  2. import subprocess
  3. import requests
  4. import datetime
  5. import re
  6. import time
  7. import shutil
  8. from requests.adapters import HTTPAdapter
  9. from urllib3.util.retry import Retry
  10. # 获取脚本所在目录
  11. script_path = os.path.dirname(os.path.abspath(__file__))
  12. # 获取仓库根目录(脚本所在目录的上一级)
  13. repo_root = os.path.dirname(script_path)
  14. def run_command(command, cwd=None):
  15. process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cwd)
  16. output, error = process.communicate()
  17. if process.returncode != 0:
  18. print(f"错误: {error.decode('utf-8')}")
  19. exit(1)
  20. return output.decode('utf-8').strip()
  21. def print_current_time(message):
  22. current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  23. print(f"{message} {current_time}")
  24. def download_file_with_retry(url, filename, max_retries=3, backoff_factor=1):
  25. """
  26. 下载文件的函数,包含重试机制
  27. Args:
  28. url: 下载地址
  29. filename: 保存的文件名
  30. max_retries: 最大重试次数
  31. backoff_factor: 重试延迟因子
  32. """
  33. session = requests.Session()
  34. retry_strategy = Retry(
  35. total=max_retries,
  36. backoff_factor=backoff_factor,
  37. status_forcelist=[500, 502, 503, 504],
  38. )
  39. adapter = HTTPAdapter(max_retries=retry_strategy)
  40. session.mount("http://", adapter)
  41. session.mount("https://", adapter)
  42. for attempt in range(max_retries + 1):
  43. try:
  44. response = session.get(url, timeout=30)
  45. response.raise_for_status()
  46. return response
  47. except requests.exceptions.RequestException as e:
  48. if attempt == max_retries:
  49. raise e
  50. print(f"下载 {filename} 失败,正在重试 ({attempt + 1}/{max_retries})...")
  51. time.sleep(backoff_factor * (2 ** attempt)) # 指数退避
  52. return None
  53. # 1. 获取脚本所在路径和系统时间日期
  54. print(f"脚本所在路径:{script_path}")
  55. print_current_time("系统时间:")
  56. # 2. 切换到程序所在目录
  57. os.chdir(script_path)
  58. print("切换到程序所在目录...")
  59. # 3. 设置 Git 用户名和邮箱
  60. run_command('git config user.name "vbskycn"', cwd=repo_root)
  61. run_command('git config user.email "zhoujie218@gmail.com"', cwd=repo_root)
  62. # 4. 执行 Git 操作,放弃本地更改并拉取最新代码
  63. print("正在执行 Git 操作...")
  64. run_command('git fetch origin', cwd=repo_root)
  65. run_command('git reset --hard origin/master', cwd=repo_root)
  66. run_command('git clean -fd', cwd=repo_root)
  67. run_command('git pull', cwd=repo_root)
  68. # 5. 下载文件列表
  69. print("正在下载文件...")
  70. files_to_download = [
  71. {
  72. "url": "https://mycode.zbds.top/me/jxdx_hd.txt",
  73. "filename": "jxdx_hd.txt"
  74. },
  75. {
  76. "url": "https://mycode.zbds.top/me/jxdx_hd.m3u",
  77. "filename": "jxdx_hd.m3u"
  78. },
  79. {
  80. "url": "https://mycode.zbds.top/me/jxyd.txt",
  81. "filename": "jxyd.txt"
  82. },
  83. {
  84. "url": "https://mycode.zbds.top/me/jxyd.m3u",
  85. "filename": "jxyd.m3u"
  86. }
  87. ]
  88. for file_info in files_to_download:
  89. url = file_info["url"]
  90. filename = file_info["filename"]
  91. file_path = os.path.join(script_path, filename)
  92. print(f"正在下载 {filename} 文件...")
  93. try:
  94. response = download_file_with_retry(url, filename)
  95. if response and response.status_code == 200:
  96. with open(file_path, 'w', encoding='utf-8') as f:
  97. f.write(response.text)
  98. print(f"成功下载并保存 {filename} 到 {file_path}")
  99. else:
  100. print(f"下载失败 {filename}, 状态码: {response.status_code if response else 'N/A'}")
  101. if os.path.exists(file_path):
  102. print(f"使用本地已存在的 {filename} 文件继续执行")
  103. continue
  104. else:
  105. print(f"本地不存在 {filename} 文件,退出执行")
  106. exit(1)
  107. except Exception as e:
  108. print(f"下载 {filename} 时发生错误: {str(e)}")
  109. if os.path.exists(file_path):
  110. print(f"使用本地已存在的 {filename} 文件继续执行")
  111. continue
  112. else:
  113. print(f"本地不存在 {filename} 文件,退出执行")
  114. exit(1)
  115. # 6. 同步文件
  116. print("正在同步文件...")
  117. files_to_sync = ['iptv4.txt', 'iptv4.m3u', 'iptv6.txt', 'iptv6.m3u']
  118. # 根据操作系统判断源目录路径和命令
  119. if os.name == 'nt': # Windows系统
  120. docker_iptv_paths = [
  121. os.path.join("D:", "docker", "iptv4"),
  122. os.path.join("C:", "docker", "iptv4")
  123. ]
  124. else: # Linux系统
  125. docker_iptv_paths = ["/docker/iptv4"]
  126. # 尝试所有可能的路径
  127. docker_iptv_path = None
  128. for path in docker_iptv_paths:
  129. if os.path.exists(path):
  130. docker_iptv_path = path
  131. print(f"找到源目录: {docker_iptv_path}")
  132. break
  133. if docker_iptv_path is None:
  134. print("警告:未找到源目录,跳过文件同步")
  135. else:
  136. for file in files_to_sync:
  137. source = os.path.join(docker_iptv_path, file)
  138. destination = os.path.join(script_path, file)
  139. try:
  140. if os.path.exists(source):
  141. shutil.copy2(source, destination)
  142. print(f"成功复制文件: {file}")
  143. else:
  144. print(f"源文件不存在: {source}")
  145. except Exception as e:
  146. print(f"复制文件 {file} 时出错: {str(e)}")
  147. if not os.path.exists(destination):
  148. print(f"目标文件 {file} 不存在,继续执行")
  149. continue
  150. # 7. 合并文件
  151. print("正在合并文件...")
  152. output_file = os.path.join(script_path, 'hd.txt')
  153. # 定义文件合并顺序
  154. merge_order = ['jxyd.txt', 'jxdx_hd.txt', 'iptv6.txt', 'iptv4.txt']
  155. replacements = {
  156. 'jxdx_hd.txt': 'jdx,#genre#',
  157. 'jxyd.txt': 'jyd,#genre#',
  158. 'iptv6.txt': 'ip6,#genre#',
  159. 'iptv4.txt': 'ip4,#genre#'
  160. }
  161. with open(output_file, 'w', encoding='utf-8') as outfile:
  162. for filename in merge_order:
  163. filepath = os.path.join(script_path, filename)
  164. if os.path.exists(filepath):
  165. with open(filepath, 'r', encoding='utf-8') as infile:
  166. content = infile.read()
  167. if filename in replacements:
  168. content = content.replace(',#genre#', replacements[filename])
  169. outfile.write(content)
  170. outfile.write('\n')
  171. else:
  172. print(f'警告:文件 {filepath} 不存在,已跳过')
  173. print(f'文件已合并到 {output_file}')
  174. # 8. 更新 README.md 文件
  175. print("正在更新 README.md 文件...")
  176. readme_path = os.path.join(repo_root, 'README.md')
  177. current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  178. try:
  179. with open(readme_path, 'r', encoding='utf-8') as file:
  180. content = file.read()
  181. print(f"README.md 文件内容长度: {len(content)} 字符")
  182. # 更新 IPTV6 时间
  183. iptv6_pattern = r'<!-- UPDATE_TIME_IPTV6 -->本次更新时间:.*?<!-- END_UPDATE_TIME_IPTV6 -->'
  184. new_iptv6 = f'<!-- UPDATE_TIME_IPTV6 -->本次更新时间: {current_time}<!-- END_UPDATE_TIME_IPTV6 -->'
  185. content, iptv6_count = re.subn(iptv6_pattern, new_iptv6, content)
  186. # 更新 IPTV4 时间
  187. iptv4_pattern = r'<!-- UPDATE_TIME_IPTV4 -->本次更新时间:.*?<!-- END_UPDATE_TIME_IPTV4 -->'
  188. new_iptv4 = f'<!-- UPDATE_TIME_IPTV4 -->本次更新时间: {current_time}<!-- END_UPDATE_TIME_IPTV4 -->'
  189. content, iptv4_count = re.subn(iptv4_pattern, new_iptv4, content)
  190. print(f"IPTV6 更新: {'成功' if iptv6_count > 0 else '失败'}")
  191. print(f"IPTV4 更新: {'成功' if iptv4_count > 0 else '失败'}")
  192. if iptv6_count > 0 or iptv4_count > 0:
  193. with open(readme_path, 'w', encoding='utf-8') as file:
  194. file.write(content)
  195. print(f"README.md 文件更新后内容长度: {len(content)} 字符")
  196. print("README.md 文件已更新")
  197. else:
  198. print("没有找到需要更新的时间标记,README.md 文件未修改")
  199. except Exception as e:
  200. print(f"更新 README.md 文件时出错: {str(e)}")
  201. exit(1)
  202. # 8.5 执行 Python 脚本
  203. def execute_python_script(script_name):
  204. print(f"正在执行 {script_name}...")
  205. try:
  206. script_path_full = os.path.join(script_path, script_name)
  207. if os.path.exists(script_path_full):
  208. # 根据操作系统选择 Python 命令
  209. if os.name == 'nt': # Windows
  210. python_cmd = 'python'
  211. else: # Linux
  212. python_cmd = 'python3'
  213. command = f'{python_cmd} "{script_path_full}"'
  214. try:
  215. run_command(command, cwd=script_path) # 添加 cwd 参数
  216. print(f"{script_name} 执行完成")
  217. except Exception as e:
  218. print(f"{script_name} 执行出错: {str(e)}")
  219. if os.name == 'nt': # 只在 Windows 上尝试备选命令
  220. alt_python_cmd = 'python3'
  221. alt_command = f'{alt_python_cmd} "{script_path_full}"'
  222. try:
  223. run_command(alt_command, cwd=script_path)
  224. print(f"{script_name} 使用备选命令执行完成")
  225. except Exception as e2:
  226. print(f"{script_name} 备选命令执行也失败: {str(e2)}")
  227. return False
  228. else:
  229. return False
  230. else:
  231. print(f"警告:{script_name} 文件不存在")
  232. return False
  233. except Exception as e:
  234. print(f"执行 {script_name} 时出错: {str(e)}")
  235. return False
  236. return True
  237. # 依次执行三个Python脚本
  238. scripts_to_execute = ['update_index.py', 'indexnow-live.py', 'indexnow-www.py']
  239. for script in scripts_to_execute:
  240. success = execute_python_script(script)
  241. if not success:
  242. print(f"警告:{script} 执行失败,继续执行下一个脚本")
  243. # 9. 提交更改并推送到 GitHub
  244. print("正在提交更改并推送...")
  245. try:
  246. output = run_command('git status', cwd=repo_root)
  247. print(f"Git 状态:\n{output}")
  248. if "nothing to commit" not in output:
  249. output = run_command('git add .', cwd=repo_root)
  250. print(f"Git add 输出:\n{output}")
  251. commit_message = f"debian100 {current_time} - 同步IPTV4仓库文件和处理新文件"
  252. output = run_command(f'git commit -m "{commit_message}"', cwd=repo_root)
  253. print(f"Git commit 输出:\n{output}")
  254. output = run_command('git push', cwd=repo_root)
  255. print(f"Git push 输出:\n{output}")
  256. print("更改已成功提交并推送到 GitHub")
  257. else:
  258. print("没有需要提交的更改")
  259. except Exception as e:
  260. print(f"Git 操作失败: {str(e)}")
  261. exit(1)
  262. print("脚本执行完成")