mirror of
https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT.git
synced 2025-12-16 03:40:27 +00:00
feat(download): 初步实现ip优选
This commit is contained in:
BIN
source/cfst.exe
Normal file
BIN
source/cfst.exe
Normal file
Binary file not shown.
@@ -14,11 +14,12 @@ class DownloadThread(QThread):
|
||||
progress = Signal(dict)
|
||||
finished = Signal(bool, str)
|
||||
|
||||
def __init__(self, url, _7z_path, game_version, parent=None):
|
||||
def __init__(self, url, _7z_path, game_version, preferred_ip=None, parent=None):
|
||||
super().__init__(parent)
|
||||
self.url = url
|
||||
self._7z_path = _7z_path
|
||||
self.game_version = game_version
|
||||
self.preferred_ip = preferred_ip
|
||||
self.process = None
|
||||
self.is_running = True
|
||||
|
||||
@@ -40,6 +41,16 @@ class DownloadThread(QThread):
|
||||
|
||||
command = [
|
||||
aria2c_path,
|
||||
]
|
||||
|
||||
# 如果有优选IP,则添加到 aaric2 命令中
|
||||
if self.preferred_ip:
|
||||
hostname = parsed_url.hostname
|
||||
port = parsed_url.port or (443 if parsed_url.scheme == 'https' else 80)
|
||||
command.extend(['--resolve', f'{hostname}:{port}:{self.preferred_ip}'])
|
||||
print(f"已应用优选IP: {hostname} -> {self.preferred_ip}")
|
||||
|
||||
command.extend([
|
||||
'--dir', download_dir,
|
||||
'--out', file_name,
|
||||
'--user-agent', UA,
|
||||
@@ -66,7 +77,7 @@ class DownloadThread(QThread):
|
||||
'--split=16',
|
||||
'--max-connection-per-server=16',
|
||||
self.url
|
||||
]
|
||||
])
|
||||
|
||||
creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
|
||||
self.process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='utf-8', errors='replace', creationflags=creation_flags)
|
||||
|
||||
25
source/ip.txt
Normal file
25
source/ip.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
173.245.48.0/20
|
||||
103.21.244.0/22
|
||||
103.22.200.0/22
|
||||
103.31.4.0/22
|
||||
141.101.64.0/18
|
||||
108.162.192.0/18
|
||||
190.93.240.0/20
|
||||
188.114.96.0/20
|
||||
197.234.240.0/22
|
||||
198.41.128.0/17
|
||||
162.158.0.0/15
|
||||
104.16.0.0/12
|
||||
172.64.0.0/17
|
||||
172.64.128.0/18
|
||||
172.64.192.0/19
|
||||
172.64.224.0/22
|
||||
172.64.229.0/24
|
||||
172.64.230.0/23
|
||||
172.64.232.0/21
|
||||
172.64.240.0/21
|
||||
172.64.248.0/21
|
||||
172.65.0.0/16
|
||||
172.66.0.0/16
|
||||
172.67.0.0/16
|
||||
131.0.72.0/22
|
||||
130
source/ip_optimizer.py
Normal file
130
source/ip_optimizer.py
Normal file
@@ -0,0 +1,130 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from utils import resource_path
|
||||
|
||||
def get_optimal_ip(url: str) -> str | None:
|
||||
"""
|
||||
使用 CloudflareSpeedTest 工具获取给定 URL 的最优 Cloudflare IP。
|
||||
|
||||
Args:
|
||||
url: 需要进行优选的下载链接。
|
||||
|
||||
Returns:
|
||||
最优的 IP 地址字符串,如果找不到则返回 None。
|
||||
"""
|
||||
try:
|
||||
# 1. 定位 CloudflareSpeedTest 工具路径,使用新的文件名 cfst.exe
|
||||
cst_path = resource_path("cfst.exe")
|
||||
if not os.path.exists(cst_path):
|
||||
print(f"错误: cfst.exe 未在资源路径中找到。")
|
||||
return None
|
||||
|
||||
# 2. 构建命令行参数
|
||||
# -p 1: 只输出最快的一个 IP
|
||||
# -o "": 不生成 result.csv 文件
|
||||
# -url: 指定我们自己的测速链接
|
||||
# -f: 指定 ip.txt 的路径
|
||||
ip_txt_path = resource_path("ip.txt")
|
||||
command = [
|
||||
cst_path,
|
||||
"-p", "1",
|
||||
"-o", "",
|
||||
"-url", url,
|
||||
"-f", ip_txt_path,
|
||||
"-dd",
|
||||
]
|
||||
|
||||
# 3. 执行命令并捕获输出
|
||||
# 使用 CREATE_NO_WINDOW 标志来隐藏控制台窗口
|
||||
creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
encoding='utf-8',
|
||||
errors='replace',
|
||||
creationflags=creation_flags,
|
||||
bufsize=1, # 使用行缓冲
|
||||
)
|
||||
|
||||
# 4. 实时读取、打印并解析输出
|
||||
print("--- CloudflareSpeedTest 实时输出 ---")
|
||||
|
||||
if not process.stdout:
|
||||
print("错误: 无法获取子进程的输出流。")
|
||||
return None
|
||||
|
||||
# 根据用户提供的最新格式更新正则表达式
|
||||
# 格式: IP Sent Recv Loss Avg-Latency DL-Speed Region
|
||||
ip_pattern = re.compile(r'^\s*([\d\.]+)\s+\d+\s+\d+\s+[\d\.]+%?\s+[\d\.]+\s+[\d\.]+\s+.*$')
|
||||
fd = process.stdout.fileno()
|
||||
buffer = b''
|
||||
|
||||
while process.poll() is None:
|
||||
try:
|
||||
chunk = os.read(fd, 1024)
|
||||
if not chunk:
|
||||
break
|
||||
buffer += chunk
|
||||
|
||||
while b'\n' in buffer or b'\r' in buffer:
|
||||
end_index_n = buffer.find(b'\n')
|
||||
end_index_r = buffer.find(b'\r')
|
||||
end_index = min(end_index_n, end_index_r) if end_index_n != -1 and end_index_r != -1 else max(end_index_n, end_index_r)
|
||||
|
||||
line_bytes = buffer[:end_index]
|
||||
line = line_bytes.decode('utf-8', errors='replace').strip()
|
||||
|
||||
if line:
|
||||
print(line)
|
||||
match = ip_pattern.match(line)
|
||||
if match:
|
||||
optimal_ip = match.group(1)
|
||||
print(f"找到最优 IP: {optimal_ip}, 正在终止测速进程...")
|
||||
print("------------------------------------")
|
||||
process.terminate() # 终止进程
|
||||
return optimal_ip
|
||||
|
||||
buffer = buffer[end_index+1:]
|
||||
|
||||
except (IOError, OSError):
|
||||
break
|
||||
|
||||
# 处理可能残留在缓冲区的数据
|
||||
if buffer:
|
||||
line = buffer.decode('utf-8', errors='replace').strip()
|
||||
if line:
|
||||
print(line)
|
||||
match = ip_pattern.match(line)
|
||||
if match:
|
||||
optimal_ip = match.group(1)
|
||||
print(f"找到最优 IP: {optimal_ip}")
|
||||
print("------------------------------------")
|
||||
process.terminate() # 确保在返回前终止进程
|
||||
return optimal_ip
|
||||
|
||||
print("------------------------------------")
|
||||
|
||||
# 5. 在循环结束后,检查是否找到了 IP
|
||||
# (IP 在循环内部找到并返回)
|
||||
process.wait() # 等待进程完全终止
|
||||
print("警告: 未能在 CloudflareSpeedTest 输出中找到最优 IP。")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"执行 CloudflareSpeedTest 时发生错误: {e}")
|
||||
return None
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 用于直接测试此模块
|
||||
test_url = "https://speed.cloudflare.com/__down?during=download&bytes=104857600"
|
||||
ip = get_optimal_ip(test_url)
|
||||
if ip:
|
||||
print(f"为 {test_url} 找到的最优 IP 是: {ip}")
|
||||
else:
|
||||
print(f"未能为 {test_url} 找到最优 IP。")
|
||||
97
source/ipv6.txt
Normal file
97
source/ipv6.txt
Normal file
@@ -0,0 +1,97 @@
|
||||
2400:cb00:2049::/48
|
||||
2400:cb00:f00e::/48
|
||||
2606:4700::/32
|
||||
2606:4700:10::/48
|
||||
2606:4700:130::/48
|
||||
2606:4700:3000::/48
|
||||
2606:4700:3001::/48
|
||||
2606:4700:3002::/48
|
||||
2606:4700:3003::/48
|
||||
2606:4700:3004::/48
|
||||
2606:4700:3005::/48
|
||||
2606:4700:3006::/48
|
||||
2606:4700:3007::/48
|
||||
2606:4700:3008::/48
|
||||
2606:4700:3009::/48
|
||||
2606:4700:3010::/48
|
||||
2606:4700:3011::/48
|
||||
2606:4700:3012::/48
|
||||
2606:4700:3013::/48
|
||||
2606:4700:3014::/48
|
||||
2606:4700:3015::/48
|
||||
2606:4700:3016::/48
|
||||
2606:4700:3017::/48
|
||||
2606:4700:3018::/48
|
||||
2606:4700:3019::/48
|
||||
2606:4700:3020::/48
|
||||
2606:4700:3021::/48
|
||||
2606:4700:3022::/48
|
||||
2606:4700:3023::/48
|
||||
2606:4700:3024::/48
|
||||
2606:4700:3025::/48
|
||||
2606:4700:3026::/48
|
||||
2606:4700:3027::/48
|
||||
2606:4700:3028::/48
|
||||
2606:4700:3029::/48
|
||||
2606:4700:3030::/48
|
||||
2606:4700:3031::/48
|
||||
2606:4700:3032::/48
|
||||
2606:4700:3033::/48
|
||||
2606:4700:3034::/48
|
||||
2606:4700:3035::/48
|
||||
2606:4700:3036::/48
|
||||
2606:4700:3037::/48
|
||||
2606:4700:3038::/48
|
||||
2606:4700:3039::/48
|
||||
2606:4700:a0::/48
|
||||
2606:4700:a1::/48
|
||||
2606:4700:a8::/48
|
||||
2606:4700:a9::/48
|
||||
2606:4700:a::/48
|
||||
2606:4700:b::/48
|
||||
2606:4700:c::/48
|
||||
2606:4700:d0::/48
|
||||
2606:4700:d1::/48
|
||||
2606:4700:d::/48
|
||||
2606:4700:e0::/48
|
||||
2606:4700:e1::/48
|
||||
2606:4700:e2::/48
|
||||
2606:4700:e3::/48
|
||||
2606:4700:e4::/48
|
||||
2606:4700:e5::/48
|
||||
2606:4700:e6::/48
|
||||
2606:4700:e7::/48
|
||||
2606:4700:e::/48
|
||||
2606:4700:f1::/48
|
||||
2606:4700:f2::/48
|
||||
2606:4700:f3::/48
|
||||
2606:4700:f4::/48
|
||||
2606:4700:f5::/48
|
||||
2606:4700:f::/48
|
||||
2803:f800:50::/48
|
||||
2803:f800:51::/48
|
||||
2a06:98c1:3100::/48
|
||||
2a06:98c1:3101::/48
|
||||
2a06:98c1:3102::/48
|
||||
2a06:98c1:3103::/48
|
||||
2a06:98c1:3104::/48
|
||||
2a06:98c1:3105::/48
|
||||
2a06:98c1:3106::/48
|
||||
2a06:98c1:3107::/48
|
||||
2a06:98c1:3108::/48
|
||||
2a06:98c1:3109::/48
|
||||
2a06:98c1:310a::/48
|
||||
2a06:98c1:310b::/48
|
||||
2a06:98c1:310c::/48
|
||||
2a06:98c1:310d::/48
|
||||
2a06:98c1:310e::/48
|
||||
2a06:98c1:310f::/48
|
||||
2a06:98c1:3120::/48
|
||||
2a06:98c1:3121::/48
|
||||
2a06:98c1:3122::/48
|
||||
2a06:98c1:3123::/48
|
||||
2a06:98c1:3200::/48
|
||||
2a06:98c1:50::/48
|
||||
2a06:98c1:51::/48
|
||||
2a06:98c1:54::/48
|
||||
2a06:98c1:58::/48
|
||||
@@ -20,6 +20,7 @@ from utils import (
|
||||
load_base64_image, HashManager, AdminPrivileges, msgbox_frame
|
||||
)
|
||||
from download import DownloadThread, ProgressWindow
|
||||
from ip_optimizer import get_optimal_ip
|
||||
from pic_data import img_data
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
@@ -165,8 +166,20 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
|
||||
self.progress_window = ProgressWindow(self)
|
||||
|
||||
# --- IP 优选逻辑 ---
|
||||
self.progress_window.game_label.setText("正在优化下载线路,请稍候...")
|
||||
QApplication.processEvents() # 刷新UI以显示上述消息
|
||||
|
||||
self.current_download_thread = DownloadThread(url, _7z_path, game_version, self)
|
||||
preferred_ip = get_optimal_ip(url)
|
||||
|
||||
if preferred_ip:
|
||||
print(f"已为 {game_version} 获取到优选IP: {preferred_ip}")
|
||||
else:
|
||||
print(f"未能为 {game_version} 获取优选IP,将使用默认线路。")
|
||||
# --- IP 优选逻辑结束 ---
|
||||
|
||||
self.current_download_thread = DownloadThread(url, _7z_path, game_version, preferred_ip, self)
|
||||
self.current_download_thread.progress.connect(self.progress_window.update_progress)
|
||||
self.current_download_thread.finished.connect(
|
||||
lambda success, error: self.install_setting(
|
||||
|
||||
Reference in New Issue
Block a user