Files
FRAISEMOE-Addons-Installer-…/source/ip_optimizer.py

146 lines
4.8 KiB
Python
Raw Normal View History

2025-07-17 11:49:29 +08:00
import os
import re
import subprocess
import sys
import time
2025-07-17 11:49:29 +08:00
from urllib.parse import urlparse
from utils import resource_path
class IpOptimizer:
def __init__(self):
self.process = None
2025-07-17 11:49:29 +08:00
def get_optimal_ip(self, url: str) -> str | None:
"""
使用 CloudflareSpeedTest 工具获取给定 URL 的最优 Cloudflare IP
2025-07-17 11:49:29 +08:00
Args:
url: 需要进行优选的下载链接
2025-07-17 11:49:29 +08:00
Returns:
最优的 IP 地址字符串如果找不到则返回 None
"""
try:
cst_path = resource_path("cfst.exe")
if not os.path.exists(cst_path):
print(f"错误: cfst.exe 未在资源路径中找到。")
return None
2025-07-17 11:49:29 +08:00
ip_txt_path = resource_path("ip.txt")
command = [
cst_path,
"-p", "1",
"-o", "",
"-url", url,
"-f", ip_txt_path,
"-dd",
]
2025-07-17 11:49:29 +08:00
creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
print("--- CloudflareSpeedTest 开始执行 ---")
self.process = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
encoding='utf-8',
errors='replace',
creationflags=creation_flags,
bufsize=0
)
2025-07-17 11:49:29 +08:00
# 立即向 stdin 发送换行符,以便程序在 Windows 下正常退出
if self.process.stdin:
try:
self.process.stdin.write('\n')
self.process.stdin.flush()
except:
pass
finally:
self.process.stdin.close()
ip_pattern = re.compile(r'^\s*([\d\.]+)\s+\d+\s+\d+\s+[\d\.]+%?\s+[\d\.]+\s+[\d\.]+\s+.*$')
stdout = self.process.stdout
if not stdout:
print("错误: 无法获取子进程的输出流。")
return None
optimal_ip = None
timeout_counter = 0
max_timeout = 60
while True:
if self.process.poll() is not None:
2025-07-17 11:49:29 +08:00
break
try:
ready = True
try:
line = stdout.readline()
except:
ready = False
if not ready or not line:
timeout_counter += 1
if timeout_counter > max_timeout:
print("超时: CloudflareSpeedTest 响应超时")
break
time.sleep(1)
continue
2025-07-17 11:49:29 +08:00
timeout_counter = 0
2025-07-17 11:49:29 +08:00
cleaned_line = line.strip()
if cleaned_line:
print(cleaned_line)
match = ip_pattern.match(cleaned_line)
2025-07-17 11:49:29 +08:00
if match:
optimal_ip = match.group(1)
print(f"找到最优 IP: {optimal_ip}, 正在终止测速进程...")
break
except Exception as e:
print(f"读取输出时发生错误: {e}")
break
self.stop()
print("--- CloudflareSpeedTest 执行结束 ---")
return optimal_ip
2025-07-17 11:49:29 +08:00
except Exception as e:
print(f"执行 CloudflareSpeedTest 时发生错误: {e}")
return None
2025-07-17 11:49:29 +08:00
def stop(self):
if self.process and self.process.poll() is None:
print("正在终止 CloudflareSpeedTest 进程...")
try:
if self.process.stdin and not self.process.stdin.closed:
self.process.stdin.write('\n')
self.process.stdin.flush()
self.process.stdin.close()
except:
pass
try:
self.process.terminate()
self.process.wait(timeout=5)
except subprocess.TimeoutExpired:
self.process.kill()
self.process.wait()
print("CloudflareSpeedTest 进程已终止。")
2025-07-17 11:49:29 +08:00
if __name__ == '__main__':
# 用于直接测试此模块
test_url = "https://speed.cloudflare.com/__down?during=download&bytes=104857600"
optimizer = IpOptimizer()
ip = optimizer.get_optimal_ip(test_url)
2025-07-17 11:49:29 +08:00
if ip:
print(f"{test_url} 找到的最优 IP 是: {ip}")
else:
print(f"未能为 {test_url} 找到最优 IP。")