From 1c749079a2f222a7a99c8ad27753a6179fd087b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AC=A7=E9=98=B3=E6=B7=87=E6=B7=87?= <35864297+hyb-oyqq@users.noreply.github.com> Date: Sat, 2 Aug 2025 16:12:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E5=A2=9E=E5=BC=BAIPv6=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=92=8C=E4=BC=98=E5=8C=96=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加IPv6可用性检查,优化用户界面反馈。 - 实现IPv6检测方法,确保用户在启用IPv6时获得准确提示。 - 更新下载线程设置,修正aria2c参数以提高兼容性。 --- source/core/cloudflare_optimizer.py | 6 +- source/core/ui_manager.py | 132 +++++++++++++++++++++++++--- source/utils/helpers.py | 7 +- source/workers/download.py | 2 +- 4 files changed, 127 insertions(+), 20 deletions(-) diff --git a/source/core/cloudflare_optimizer.py b/source/core/cloudflare_optimizer.py index fb357a8..6039e81 100644 --- a/source/core/cloudflare_optimizer.py +++ b/source/core/cloudflare_optimizer.py @@ -319,18 +319,18 @@ class CloudflareOptimizer: success_message += f"IPv6: {self.optimized_ipv6}\n" if hostname: - # 先清理可能存在的旧记录 + # 先清理可能存在的旧记录(只清理一次) self.hosts_manager.clean_hostname_entries(hostname) success = False # 应用优选IP到hosts文件 if ipv4_success: - success = self.hosts_manager.apply_ip(hostname, self.optimized_ip) or success + success = self.hosts_manager.apply_ip(hostname, self.optimized_ip, clean=False) or success # 如果启用IPv6并且找到了IPv6地址,也应用到hosts if ipv6_success: - success = self.hosts_manager.apply_ip(hostname, self.optimized_ipv6) or success + success = self.hosts_manager.apply_ip(hostname, self.optimized_ipv6, clean=False) or success if success: msg_box = QtWidgets.QMessageBox(self.main_window) diff --git a/source/core/ui_manager.py b/source/core/ui_manager.py index f7bdad9..2e7dbe2 100644 --- a/source/core/ui_manager.py +++ b/source/core/ui_manager.py @@ -283,11 +283,25 @@ class UIManager: self.ipv6_action = QAction("启用IPv6支持", self.main_window, checkable=True) self.ipv6_action.setFont(menu_font) + # 检查IPv6是否可用 + ipv6_available = self._check_ipv6_availability() + if not ipv6_available: + self.ipv6_action.setText("启用IPv6支持 (不可用)") + self.ipv6_action.setEnabled(False) + self.ipv6_action.setToolTip("未检测到可用的IPv6连接") + # 检查配置中是否已启用IPv6 config = getattr(self.main_window, 'config', {}) ipv6_enabled = False if isinstance(config, dict): ipv6_enabled = config.get("ipv6_enabled", False) + # 如果配置中启用了IPv6但实际不可用,则强制禁用 + if ipv6_enabled and not ipv6_available: + config["ipv6_enabled"] = False + ipv6_enabled = False + # 使用utils.save_config直接保存配置 + from utils import save_config + save_config(config) self.ipv6_action.setChecked(ipv6_enabled) @@ -367,6 +381,104 @@ class UIManager: self.dev_menu.addMenu(self.hosts_submenu) # 添加hosts文件选项子菜单 self.dev_menu.addAction(self.ipv6_action) # 添加IPv6支持选项 + def _check_ipv6_availability(self): + """检查IPv6是否可用 + + Returns: + bool: IPv6是否可用 + """ + import socket + import subprocess + import sys + import re + + # 方法1: 检查本地IPv6地址配置 + def has_ipv6_address(): + try: + # 尝试获取本机所有IPv6地址 + addrs = socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET6) + + for addr in addrs: + # 检查是否是全局IPv6地址(非本地链路地址) + ipv6 = addr[4][0] + if not ipv6.startswith('fe80:') and not ipv6 == '::1': + print(f"检测到IPv6地址: {ipv6}") + return True + return False + except Exception as e: + print(f"获取IPv6地址时出错: {e}") + return False + + # 方法2: 尝试创建IPv6套接字 + def can_create_ipv6_socket(): + try: + socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + return True + except Exception as e: + print(f"创建IPv6套接字失败: {e}") + return False + + # 方法3: ping6测试(仅适用于Windows) + def can_ping_ipv6(): + if sys.platform != 'win32': + return False + + try: + # 尝试ping Cloudflare的IPv6地址 + result = subprocess.run( + ['ping', '-6', '-n', '1', '-w', '2000', '2606:4700:4700::1111'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + creationflags=subprocess.CREATE_NO_WINDOW + ) + + # 检查是否ping成功 + success = result.returncode == 0 and "回复" in result.stdout + if success: + print("IPv6 ping测试成功") + return success + except Exception as e: + print(f"IPv6 ping测试失败: {e}") + return False + + # 方法4: 尝试TCP连接到公共IPv6服务器 + def can_connect_ipv6_tcp(): + # 尝试连接的IPv6服务器列表 + ipv6_servers = [ + ('2606:4700:4700::1111', 53), # Cloudflare DNS + ('2001:4860:4860::8888', 53), # Google DNS + ] + + for server, port in ipv6_servers: + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.settimeout(2) + sock.connect((server, port)) + sock.close() + print(f"成功连接到IPv6服务器: {server}:{port}") + return True + except Exception as e: + print(f"连接IPv6服务器{server}:{port}失败: {e}") + + return False + + # 运行所有检测方法 + methods = [ + has_ipv6_address, + can_create_ipv6_socket, + can_ping_ipv6, + can_connect_ipv6_tcp + ] + + for method in methods: + if method(): + print(f"IPv6检测通过: {method.__name__}") + return True + + print("所有IPv6检测方法均失败") + return False + def show_menu(self, menu, button): """显示菜单 @@ -565,21 +677,13 @@ class UIManager: # 如果用户尝试启用IPv6,检查系统是否支持IPv6 if enabled: - # 检查是否可以访问IPv6 - import socket - ipv6_supported = False - - try: - # 尝试创建一个IPv6套接字 - socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) - ipv6_supported = True - except Exception: - ipv6_supported = False + # 使用改进的IPv6检测方法 + ipv6_available = self._check_ipv6_availability() - if not ipv6_supported: + if not ipv6_available: msg_box = msgbox_frame( f"错误 - {APP_NAME}", - "\n找不到IPv6地址,您的系统可能不支持IPv6。\n", + "\n未检测到可用的IPv6连接,无法启用IPv6支持。\n\n请确保您的网络环境支持IPv6且已正确配置。\n", QMessageBox.StandardButton.Ok, ) msg_box.exec() @@ -591,7 +695,9 @@ class UIManager: # 保存设置到配置 if hasattr(self.main_window, 'config'): self.main_window.config["ipv6_enabled"] = enabled - self.main_window.save_config(self.main_window.config) + # 直接使用utils.save_config保存配置 + from utils import save_config + save_config(self.main_window.config) # 显示设置已保存的消息 status = "启用" if enabled else "禁用" diff --git a/source/utils/helpers.py b/source/utils/helpers.py index 1a3e16c..e56a1b7 100644 --- a/source/utils/helpers.py +++ b/source/utils/helpers.py @@ -333,7 +333,7 @@ class HostsManager: print(f"清理hosts文件失败: {e}") return False - def apply_ip(self, hostname, ip_address): + def apply_ip(self, hostname, ip_address, clean=True): if not self.original_content: if not self.backup(): return False @@ -347,8 +347,9 @@ class HostsManager: return False try: - # 首先清理已有的同域名记录 - self.clean_hostname_entries(hostname) + # 首先清理已有的同域名记录(如果需要) + if clean: + self.clean_hostname_entries(hostname) # 然后添加新记录 lines = self.original_content.splitlines() diff --git a/source/workers/download.py b/source/workers/download.py index 048fd71..5024bb2 100644 --- a/source/workers/download.py +++ b/source/workers/download.py @@ -204,7 +204,7 @@ class DownloadThread(QThread): '--header', 'Sec-Fetch-Site: same-origin', '--http-accept-gzip=true', '--console-log-level=notice', - '--summary-interval=0.5', # 减小摘要间隔到0.5秒,提高进度更新频率 + '--summary-interval=1', # 设置为最小整数值1秒,原值0.5不被aria2c支持 '--log-level=notice', '--max-tries=3', '--retry-wait=2',