From 98bfddeb041978817413957295e91f830bba80d1 Mon Sep 17 00:00:00 2001 From: hyb-oyqq <1512383570@qq.com> Date: Mon, 4 Aug 2025 11:44:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E5=A2=9E=E5=BC=BAhosts=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD=E5=92=8C=E8=87=AA=E5=8A=A8=E8=BF=98?= =?UTF-8?q?=E5=8E=9F=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加对hosts文件优选IP记录的检查,避免重复优选。 - 添加禁用自动还原hosts的选项,允许用户自定义设置。 - 更新HostsManager以支持自动还原状态的设置和检查,优化hosts文件的管理逻辑。 --- source/core/cloudflare_optimizer.py | 43 +++++++++++++ source/core/download_manager.py | 32 ++++++++++ source/core/ui_manager.py | 62 ++++++++++++++++++ source/data/config.py | 2 +- source/main_window.py | 4 +- source/utils/helpers.py | 97 +++++++++++++++++++++++++++++ 6 files changed, 237 insertions(+), 3 deletions(-) diff --git a/source/core/cloudflare_optimizer.py b/source/core/cloudflare_optimizer.py index 6039e81..f2e3033 100644 --- a/source/core/cloudflare_optimizer.py +++ b/source/core/cloudflare_optimizer.py @@ -28,6 +28,7 @@ class CloudflareOptimizer: self.optimization_cancelled = False self.ip_optimizer_thread = None self.ipv6_optimizer_thread = None + self.has_optimized_in_session = False # 本次启动是否已执行过优选 def is_optimization_done(self): """检查是否已完成优化 @@ -67,6 +68,39 @@ class CloudflareOptimizer: Args: url: 用于优化的URL """ + # 解析域名 + hostname = urlparse(url).hostname + + # 检查hosts文件中是否已有该域名的IP记录 + existing_ips = self.hosts_manager.get_hostname_entries(hostname) if hostname else [] + + # 判断是否继续优选的逻辑 + if existing_ips and self.has_optimized_in_session: + # 如果本次会话中已执行过优选且hosts中存在记录,则跳过优选过程 + print(f"发现hosts文件中已有域名 {hostname} 的优选IP记录且本次会话已优选过,跳过优选过程") + + # 设置标记为已优选完成 + self.optimization_done = True + self.countdown_finished = True + + # 尝试获取现有的IPv4和IPv6地址 + ipv4_entries = [ip for ip in existing_ips if ':' not in ip] # IPv4地址不含冒号 + ipv6_entries = [ip for ip in existing_ips if ':' in ip] # IPv6地址包含冒号 + + if ipv4_entries: + self.optimized_ip = ipv4_entries[0] + if ipv6_entries: + self.optimized_ipv6 = ipv6_entries[0] + + print(f"使用已存在的优选IP - IPv4: {self.optimized_ip}, IPv6: {self.optimized_ipv6}") + return True + else: + # 如果本次会话尚未优选过,或hosts中没有记录,则显示优选窗口 + if existing_ips: + print(f"发现hosts文件中已有域名 {hostname} 的优选IP记录,但本次会话尚未优选过") + # 清理已有的hosts记录,准备重新优选 + self.hosts_manager.clean_hostname_entries(hostname) + # 创建取消状态标记 self.optimization_cancelled = False self.countdown_finished = False @@ -332,6 +366,15 @@ class CloudflareOptimizer: if ipv6_success: success = self.hosts_manager.apply_ip(hostname, self.optimized_ipv6, clean=False) or success + # 记录此次优选操作对hosts文件进行了更新 + if hasattr(self.main_window, 'config'): + self.main_window.config['last_hosts_optimized_hostname'] = hostname + from utils import save_config + save_config(self.main_window.config) + + # 记录本次会话已执行过优选 + self.has_optimized_in_session = True + if success: msg_box = QtWidgets.QMessageBox(self.main_window) msg_box.setWindowTitle(f"成功 - {self.main_window.APP_NAME}") diff --git a/source/core/download_manager.py b/source/core/download_manager.py index 4bf0a2d..80c7107 100644 --- a/source/core/download_manager.py +++ b/source/core/download_manager.py @@ -406,6 +406,38 @@ class DownloadManager: def _show_cloudflare_option(self): """显示Cloudflare加速选择对话框""" + # 首先检查队列中第一个URL的域名是否已在hosts中有优选IP + if self.download_queue: + first_url = self.download_queue[0][0] + hostname = urlparse(first_url).hostname + + # 检查hosts文件中是否已有该域名的IP记录 + if hostname: + existing_ips = self.cloudflare_optimizer.hosts_manager.get_hostname_entries(hostname) + + if existing_ips: + print(f"发现hosts文件中已有域名 {hostname} 的优选IP记录,跳过询问直接使用") + + # 设置标记为已优选完成 + self.cloudflare_optimizer.optimization_done = True + self.cloudflare_optimizer.countdown_finished = True + + # 尝试获取现有的IPv4和IPv6地址 + ipv4_entries = [ip for ip in existing_ips if ':' not in ip] + ipv6_entries = [ip for ip in existing_ips if ':' in ip] + + if ipv4_entries: + self.cloudflare_optimizer.optimized_ip = ipv4_entries[0] + if ipv6_entries: + self.cloudflare_optimizer.optimized_ipv6 = ipv6_entries[0] + + # 保存当前URL供CloudflareOptimizer使用 + self.main_window.current_url = first_url + + # 直接开始下载,跳过询问 + self.next_download_task() + return + # 临时启用窗口以显示对话框 self.main_window.setEnabled(True) diff --git a/source/core/ui_manager.py b/source/core/ui_manager.py index 9350580..28dca08 100644 --- a/source/core/ui_manager.py +++ b/source/core/ui_manager.py @@ -341,12 +341,26 @@ class UIManager: self.clean_hosts_action.setFont(menu_font) self.clean_hosts_action.triggered.connect(self.clean_hosts_entries) + # 添加禁用自动还原hosts的选项 + self.disable_auto_restore_action = QAction("禁用关闭/重启自动还原hosts", self.main_window, checkable=True) + self.disable_auto_restore_action.setFont(menu_font) + + # 从配置中读取当前状态 + config = getattr(self.main_window, 'config', {}) + disable_auto_restore = False + if isinstance(config, dict): + disable_auto_restore = config.get("disable_auto_restore_hosts", False) + + self.disable_auto_restore_action.setChecked(disable_auto_restore) + self.disable_auto_restore_action.triggered.connect(self.toggle_disable_auto_restore_hosts) + # 添加打开hosts文件选项 self.open_hosts_action = QAction("打开hosts文件", self.main_window) self.open_hosts_action.setFont(menu_font) self.open_hosts_action.triggered.connect(self.open_hosts_file) # 添加到hosts子菜单 + self.hosts_submenu.addAction(self.disable_auto_restore_action) self.hosts_submenu.addAction(self.restore_hosts_action) self.hosts_submenu.addAction(self.clean_hosts_action) self.hosts_submenu.addAction(self.open_hosts_action) @@ -645,6 +659,54 @@ class UIManager: msg_box = self._create_message_box("错误", f"\n打开hosts文件时发生错误:\n\n{str(e)}\n") msg_box.exec() + def toggle_disable_auto_restore_hosts(self, checked): + """切换禁用自动还原hosts的状态 + + Args: + checked: 是否禁用自动还原 + """ + if hasattr(self.main_window, 'download_manager') and hasattr(self.main_window.download_manager, 'hosts_manager'): + try: + # 调用HostsManager的方法设置自动还原标志 + result = self.main_window.download_manager.hosts_manager.set_auto_restore_disabled(checked) + + if result: + # 同时更新内部配置,确保立即生效 + if hasattr(self.main_window, 'config'): + self.main_window.config['disable_auto_restore_hosts'] = checked + + # 显示成功提示 + status = "禁用" if checked else "启用" + msg_box = self._create_message_box( + "设置已更新", + f"\n已{status}关闭/重启时自动还原hosts。\n\n{'hosts将被保留' if checked else 'hosts将在关闭时自动还原'}。\n" + ) + msg_box.exec() + else: + # 如果设置失败,恢复复选框状态 + self.disable_auto_restore_action.setChecked(not checked) + msg_box = self._create_message_box( + "设置失败", + "\n更新设置时发生错误,请稍后再试。\n" + ) + msg_box.exec() + except Exception as e: + # 如果发生异常,恢复复选框状态 + self.disable_auto_restore_action.setChecked(not checked) + msg_box = self._create_message_box( + "错误", + f"\n更新设置时发生异常:\n\n{str(e)}\n" + ) + msg_box.exec() + else: + # 如果hosts管理器不可用,恢复复选框状态 + self.disable_auto_restore_action.setChecked(not checked) + msg_box = self._create_message_box( + "错误", + "\nhosts管理器不可用,无法更新设置。\n" + ) + msg_box.exec() + def show_about_dialog(self): """显示关于对话框""" about_text = f""" diff --git a/source/data/config.py b/source/data/config.py index 1f82655..7614f72 100644 --- a/source/data/config.py +++ b/source/data/config.py @@ -3,7 +3,7 @@ import base64 # 配置信息 app_data = { - "APP_VERSION": "1.3.1", + "APP_VERSION": "1.3.2", "APP_NAME": "FRAISEMOE Addons Installer NEXT", "TEMP": "TEMP", "CACHE": "FRAISEMOE", diff --git a/source/main_window.py b/source/main_window.py index a6b64c5..6a0072c 100644 --- a/source/main_window.py +++ b/source/main_window.py @@ -456,10 +456,10 @@ class MainWindow(QMainWindow): event.ignore() return - # 恢复hosts文件 + # 恢复hosts文件(如果未禁用自动还原) self.download_manager.hosts_manager.restore() - # 额外检查并清理hosts文件中的残留记录 + # 额外检查并清理hosts文件中的残留记录(如果未禁用自动还原) self.download_manager.hosts_manager.check_and_clean_all_entries() # 停止日志记录 diff --git a/source/utils/helpers.py b/source/utils/helpers.py index e56a1b7..e0c3e43 100644 --- a/source/utils/helpers.py +++ b/source/utils/helpers.py @@ -273,7 +273,53 @@ class HostsManager: self.original_content = None self.modified = False self.modified_hostnames = set() # 跟踪被修改的主机名 + self.auto_restore_disabled = False # 是否禁用自动还原hosts + def get_hostname_entries(self, hostname): + """获取hosts文件中指定域名的所有IP记录 + + Args: + hostname: 要查询的域名 + + Returns: + list: 域名对应的IP地址列表,如果未找到则返回空列表 + """ + try: + # 如果original_content为空,先读取hosts文件 + if not self.original_content: + try: + with open(self.hosts_path, 'r', encoding='utf-8') as f: + self.original_content = f.read() + except Exception as e: + print(f"读取hosts文件失败: {e}") + return [] + + # 解析hosts文件中的每一行 + ip_addresses = [] + lines = self.original_content.splitlines() + + for line in lines: + # 跳过注释和空行 + line = line.strip() + if not line or line.startswith('#'): + continue + + # 分割行内容获取IP和域名 + parts = line.split() + if len(parts) >= 2: # 至少包含IP和一个域名 + ip = parts[0] + domains = parts[1:] + + # 如果当前行包含目标域名 + if hostname in domains: + ip_addresses.append(ip) + + return ip_addresses + + except Exception as e: + print(f"获取hosts记录失败: {e}") + return [] + def backup(self): if not AdminPrivileges().is_admin(): print("需要管理员权限来备份hosts文件。") @@ -373,12 +419,58 @@ class HostsManager: msg_box.exec() return False + def set_auto_restore_disabled(self, disabled): + """设置是否禁用自动还原hosts + + Args: + disabled: 是否禁用自动还原hosts + + Returns: + bool: 操作是否成功 + """ + try: + # 更新状态 + self.auto_restore_disabled = disabled + + # 从配置文件读取当前配置 + from utils import load_config, save_config + config = load_config() + + # 更新配置 + config['disable_auto_restore_hosts'] = disabled + + # 保存配置 + save_config(config) + + print(f"已{'禁用' if disabled else '启用'}自动还原hosts") + return True + except Exception as e: + print(f"设置自动还原hosts状态失败: {e}") + return False + + def is_auto_restore_disabled(self): + """检查是否禁用了自动还原hosts + + Returns: + bool: 是否禁用自动还原hosts + """ + from utils import load_config + config = load_config() + auto_restore_disabled = config.get('disable_auto_restore_hosts', False) + self.auto_restore_disabled = auto_restore_disabled + return auto_restore_disabled + def check_and_clean_all_entries(self): """检查并清理所有由本应用程序添加的hosts记录 Returns: bool: 清理是否成功 """ + # 如果禁用了自动还原,则不执行清理操作 + if self.is_auto_restore_disabled(): + print("已禁用自动还原hosts,跳过清理操作") + return True + if not AdminPrivileges().is_admin(): print("需要管理员权限来检查和清理hosts文件。") return False @@ -423,6 +515,11 @@ class HostsManager: return False def restore(self): + # 如果禁用了自动还原,则不执行还原操作 + if self.is_auto_restore_disabled(): + print("已禁用自动还原hosts,跳过还原操作") + return True + if not self.modified: if os.path.exists(self.backup_path): try: