import os from urllib.parse import urlparse from PySide6 import QtWidgets from PySide6.QtCore import Qt, QTimer from PySide6.QtGui import QIcon, QPixmap from utils import msgbox_frame, resource_path from workers import IpOptimizerThread from utils.logger import setup_logger # 初始化logger logger = setup_logger("cloudflare_optimizer") class CloudflareOptimizer: """Cloudflare IP优化器,负责处理IP优化和Cloudflare加速相关功能""" def __init__(self, main_window, hosts_manager): """初始化Cloudflare优化器 Args: main_window: 主窗口实例,用于访问UI和状态 hosts_manager: Hosts文件管理器实例 """ self.main_window = main_window self.hosts_manager = hosts_manager self.optimized_ip = None self.optimized_ipv6 = None self.optimization_done = False # 标记是否已执行过优选 self.countdown_finished = False # 标记倒计时是否结束 self.optimizing_msg_box = None 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): """检查是否已完成优化 Returns: bool: 是否已完成优化 """ return self.optimization_done def is_countdown_finished(self): """检查倒计时是否已完成 Returns: bool: 倒计时是否已完成 """ return self.countdown_finished def get_optimized_ip(self): """获取优选的IP地址 Returns: str: 优选的IP地址,如果未优选则为None """ return self.optimized_ip def get_optimized_ipv6(self): """获取优选的IPv6地址 Returns: str: 优选的IPv6地址,如果未优选则为None """ return self.optimized_ipv6 def start_ip_optimization(self, url): """开始IP优化过程 Args: url: 用于优化的URL """ # 解析域名 hostname = urlparse(url).hostname # 判断是否继续优选的逻辑 if self.has_optimized_in_session: # 如果本次会话中已执行过优选,则跳过优选过程 logger.info("本次会话已执行过优选,跳过优选过程") # 设置标记为已优选完成 self.optimization_done = True self.countdown_finished = True return True else: # 如果本次会话尚未优选过,则清理可能存在的旧记录 if hostname: # 检查hosts文件中是否已有该域名的IP记录 existing_ips = self.hosts_manager.get_hostname_entries(hostname) if existing_ips: logger.info(f"发现hosts文件中已有域名 {hostname} 的优选IP记录,但本次会话尚未优选过") # 清理已有的hosts记录,准备重新优选 self.hosts_manager.clean_hostname_entries(hostname) # 创建取消状态标记 self.optimization_cancelled = False self.countdown_finished = False # 检查是否启用了IPv6 use_ipv6 = False if hasattr(self.main_window, 'config'): use_ipv6 = self.main_window.config.get("ipv6_enabled", False) # 如果启用了IPv6,显示警告消息 if use_ipv6: ipv6_warning = QtWidgets.QMessageBox(self.main_window) ipv6_warning.setWindowTitle(f"IPv6优选警告 - {self.main_window.APP_NAME}") ipv6_warning.setText("\nIPv6优选比IPv4耗时更长且感知不强(预计耗时10分钟以上),不建议使用。\n\n确定要同时执行IPv6优选吗?\n") ipv6_warning.setIcon(QtWidgets.QMessageBox.Icon.Warning) # 设置图标 icon_path = resource_path(os.path.join("assets", "images", "ICO", "icon.png")) if os.path.exists(icon_path): pixmap = QPixmap(icon_path) if not pixmap.isNull(): ipv6_warning.setWindowIcon(QIcon(pixmap)) yes_button = ipv6_warning.addButton("是", QtWidgets.QMessageBox.ButtonRole.YesRole) no_button = ipv6_warning.addButton("否,仅使用IPv4", QtWidgets.QMessageBox.ButtonRole.NoRole) cancel_button = ipv6_warning.addButton("取消优选", QtWidgets.QMessageBox.ButtonRole.RejectRole) ipv6_warning.setDefaultButton(no_button) ipv6_warning.exec() if ipv6_warning.clickedButton() == cancel_button: # 用户取消了优选 self.optimization_cancelled = True return # 根据用户选择调整IPv6设置 if ipv6_warning.clickedButton() == no_button: use_ipv6 = False # 临时覆盖配置(不保存到文件) if hasattr(self.main_window, 'config'): self.main_window.config["ipv6_enabled"] = False # 准备提示信息 optimization_msg = "\n正在优选Cloudflare IP,请稍候...\n\n" if use_ipv6: optimization_msg += "已启用IPv6支持,同时进行IPv4和IPv6优选。\n这可能需要10分钟以上,请耐心等待喵~\n" else: optimization_msg += "这可能需要5-10分钟,请耐心等待喵~\n" # 使用Cloudflare图标创建消息框 self.optimizing_msg_box = msgbox_frame( f"通知 - {self.main_window.APP_NAME}", optimization_msg ) # 设置Cloudflare图标 cf_icon_path = resource_path("assets/images/ICO/cloudflare_logo_icon.ico") if os.path.exists(cf_icon_path): cf_pixmap = QPixmap(cf_icon_path) if not cf_pixmap.isNull(): self.optimizing_msg_box.setWindowIcon(QIcon(cf_pixmap)) self.optimizing_msg_box.setIconPixmap(cf_pixmap.scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)) # 添加取消按钮 self.optimizing_msg_box.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Cancel) self.optimizing_msg_box.buttonClicked.connect(self._on_optimization_dialog_clicked) self.optimizing_msg_box.setWindowModality(Qt.WindowModality.ApplicationModal) # 创建并启动优化线程 self.ip_optimizer_thread = IpOptimizerThread(url) self.ip_optimizer_thread.finished.connect(self.on_ipv4_optimization_finished) # 如果启用IPv6,同时启动IPv6优化线程 if use_ipv6: logger.info("IPv6已启用,将同时优选IPv6地址") self.ipv6_optimizer_thread = IpOptimizerThread(url, use_ipv6=True) self.ipv6_optimizer_thread.finished.connect(self.on_ipv6_optimization_finished) self.ipv6_optimizer_thread.start() # 启动IPv4优化线程 self.ip_optimizer_thread.start() # 显示消息框(非模态,不阻塞) self.optimizing_msg_box.open() def _on_optimization_dialog_clicked(self, button): """处理优化对话框按钮点击 Args: button: 被点击的按钮 """ if button.text() == "Cancel": # 如果是取消按钮 # 标记已取消 self.optimization_cancelled = True # 停止优化线程 if hasattr(self, 'ip_optimizer_thread') and self.ip_optimizer_thread and self.ip_optimizer_thread.isRunning(): self.ip_optimizer_thread.stop() if hasattr(self, 'ipv6_optimizer_thread') and self.ipv6_optimizer_thread and self.ipv6_optimizer_thread.isRunning(): self.ipv6_optimizer_thread.stop() # 恢复主窗口状态 self.main_window.setEnabled(True) if hasattr(self.main_window, 'window_manager'): self.main_window.window_manager.change_window_state(self.main_window.window_manager.STATE_READY) # 显示取消消息 QtWidgets.QMessageBox.information( self.main_window, f"已取消 - {self.main_window.APP_NAME}", "\n已取消IP优选和安装过程。\n" ) def on_ipv4_optimization_finished(self, ip): """IPv4优化完成后的处理 Args: ip: 优选的IP地址,如果失败则为空字符串 """ # 如果已经取消,则不继续处理 if hasattr(self, 'optimization_cancelled') and self.optimization_cancelled: return self.optimized_ip = ip logger.info(f"IPv4优选完成,结果: {ip if ip else '未找到合适的IP'}") # 检查是否还有IPv6优化正在运行 if hasattr(self, 'ipv6_optimizer_thread') and self.ipv6_optimizer_thread and self.ipv6_optimizer_thread.isRunning(): logger.info("等待IPv6优选完成...") return # 所有优选都已完成,继续处理 self.optimization_done = True self.countdown_finished = False # 确保倒计时标志重置 # 关闭提示框 if hasattr(self, 'optimizing_msg_box') and self.optimizing_msg_box: if self.optimizing_msg_box.isVisible(): self.optimizing_msg_box.accept() self.optimizing_msg_box = None # 处理优选结果 self._process_optimization_results() def on_ipv6_optimization_finished(self, ipv6): """IPv6优化完成后的处理 Args: ipv6: 优选的IPv6地址,如果失败则为空字符串 """ # 如果已经取消,则不继续处理 if hasattr(self, 'optimization_cancelled') and self.optimization_cancelled: return self.optimized_ipv6 = ipv6 logger.info(f"IPv6优选完成,结果: {ipv6 if ipv6 else '未找到合适的IPv6'}") # 检查IPv4优化是否已完成 if hasattr(self, 'ip_optimizer_thread') and self.ip_optimizer_thread and self.ip_optimizer_thread.isRunning(): logger.info("等待IPv4优选完成...") return # 所有优选都已完成,继续处理 self.optimization_done = True self.countdown_finished = False # 确保倒计时标志重置 # 关闭提示框 if hasattr(self, 'optimizing_msg_box') and self.optimizing_msg_box: if self.optimizing_msg_box.isVisible(): self.optimizing_msg_box.accept() self.optimizing_msg_box = None # 处理优选结果 self._process_optimization_results() def _process_optimization_results(self): """处理优选的IP结果,显示相应提示""" # 无论优选结果如何,都标记本次会话已执行过优选 self.has_optimized_in_session = True use_ipv6 = False if hasattr(self.main_window, 'config'): use_ipv6 = self.main_window.config.get("ipv6_enabled", False) # 判断优选结果 ipv4_success = bool(self.optimized_ip) ipv6_success = bool(self.optimized_ipv6) if use_ipv6 else False # 临时启用窗口以显示对话框 self.main_window.setEnabled(True) hostname = urlparse(self.main_window.current_url).hostname if hasattr(self.main_window, 'current_url') else None if not ipv4_success and (not use_ipv6 or not ipv6_success): # 两种IP都没有优选成功 msg_box = QtWidgets.QMessageBox(self.main_window) msg_box.setWindowTitle(f"优选失败 - {self.main_window.APP_NAME}") fail_message = "\n未能找到合适的Cloudflare " if use_ipv6: fail_message += "IPv4和IPv6地址" else: fail_message += "IP地址" fail_message += ",将使用默认网络进行下载。\n\n10秒后自动继续..." msg_box.setText(fail_message) msg_box.setIcon(QtWidgets.QMessageBox.Icon.Warning) ok_button = msg_box.addButton("确定 (10)", QtWidgets.QMessageBox.ButtonRole.AcceptRole) cancel_button = msg_box.addButton("取消安装", QtWidgets.QMessageBox.ButtonRole.RejectRole) # 创建计时器实现倒计时 countdown = 10 timer = QTimer(self.main_window) def update_countdown(): nonlocal countdown countdown -= 1 ok_button.setText(f"确定 ({countdown})") if countdown <= 0: timer.stop() if msg_box.isVisible(): msg_box.accept() timer.timeout.connect(update_countdown) timer.start(1000) # 每秒更新一次 # 显示对话框并等待用户响应 result = msg_box.exec() # 停止计时器 timer.stop() # 如果用户点击了取消安装 if msg_box.clickedButton() == cancel_button: # 恢复主窗口状态 self.main_window.setEnabled(True) if hasattr(self.main_window, 'window_manager'): self.main_window.window_manager.change_window_state(self.main_window.window_manager.STATE_READY) return False # 用户点击了继续,重新禁用主窗口 self.main_window.setEnabled(False) # 标记倒计时已完成 self.countdown_finished = True return True else: # 至少有一种IP优选成功 success_message = "" if ipv4_success: success_message += f"IPv4: {self.optimized_ip}\n" if ipv6_success: 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, clean=False) or success # 如果启用IPv6并且找到了IPv6地址,也应用到hosts 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) if success: msg_box = QtWidgets.QMessageBox(self.main_window) msg_box.setWindowTitle(f"成功 - {self.main_window.APP_NAME}") msg_box.setText(f"\n已将优选IP应用到hosts文件:\n{success_message}\n10秒后自动继续...") msg_box.setIcon(QtWidgets.QMessageBox.Icon.Information) ok_button = msg_box.addButton("确定 (10)", QtWidgets.QMessageBox.ButtonRole.AcceptRole) cancel_button = msg_box.addButton("取消安装", QtWidgets.QMessageBox.ButtonRole.RejectRole) # 创建计时器实现倒计时 countdown = 10 timer = QTimer(self.main_window) def update_countdown(): nonlocal countdown countdown -= 1 ok_button.setText(f"确定 ({countdown})") if countdown <= 0: timer.stop() if msg_box.isVisible(): msg_box.accept() timer.timeout.connect(update_countdown) timer.start(1000) # 每秒更新一次 # 显示对话框并等待用户响应 result = msg_box.exec() # 停止计时器 timer.stop() # 如果用户点击了取消安装 if msg_box.clickedButton() == cancel_button: # 恢复主窗口状态 self.main_window.setEnabled(True) if hasattr(self.main_window, 'window_manager'): self.main_window.window_manager.change_window_state(self.main_window.window_manager.STATE_READY) return False else: QtWidgets.QMessageBox.critical( self.main_window, f"错误 - {self.main_window.APP_NAME}", "\n修改hosts文件失败,请检查程序是否以管理员权限运行。\n" ) # 恢复主窗口状态 if hasattr(self.main_window, 'window_manager'): self.main_window.window_manager.change_window_state(self.main_window.window_manager.STATE_READY) return False # 用户点击了继续,重新禁用主窗口 self.main_window.setEnabled(False) # 标记倒计时已完成 self.countdown_finished = True return True def stop_optimization(self): """停止正在进行的IP优化""" if hasattr(self, 'ip_optimizer_thread') and self.ip_optimizer_thread and self.ip_optimizer_thread.isRunning(): self.ip_optimizer_thread.stop() self.ip_optimizer_thread.wait() if hasattr(self, 'ipv6_optimizer_thread') and self.ipv6_optimizer_thread and self.ipv6_optimizer_thread.isRunning(): self.ipv6_optimizer_thread.stop() self.ipv6_optimizer_thread.wait() if hasattr(self, 'optimizing_msg_box') and self.optimizing_msg_box: if self.optimizing_msg_box.isVisible(): self.optimizing_msg_box.accept() self.optimizing_msg_box = None