|
|
@@ -1,57 +1,74 @@
|
|
|
from pathlib import Path
|
|
|
+import random
|
|
|
from typing import Optional
|
|
|
+
|
|
|
+import httpx
|
|
|
from utils.process_mgr import process_manager
|
|
|
+from utils.mihomo import get_external_controller, get_provider_name, get_sub_file_info, async_get_sub, async_proxy_delay
|
|
|
import yaml
|
|
|
|
|
|
class ProxyManager:
|
|
|
- """管理代理进程的生命周期,包括启动、停止和重启"""
|
|
|
-
|
|
|
- def start_proxy(self, exe_path: str, config_path: str) -> int:
|
|
|
- """
|
|
|
- 启动代理进程
|
|
|
- Args:
|
|
|
- exe_path (str): 代理可执行文件路径
|
|
|
- config_path (str): 配置文件路径
|
|
|
- Returns:
|
|
|
- int: 进程ID
|
|
|
- """
|
|
|
- command = [exe_path, "-f", config_path]
|
|
|
- return process_manager.start_process(command, f"mimo_{Path(config_path).stem}")
|
|
|
+ prefix = "mimo_"
|
|
|
|
|
|
- def stop_proxy(self, config_path: str):
|
|
|
- """
|
|
|
- 停止代理进程
|
|
|
- Args:
|
|
|
- config_path (str): 配置文件路径
|
|
|
- """
|
|
|
- process_manager.stop_process(f"mimo_{Path(config_path).stem}")
|
|
|
+ def __init__(
|
|
|
+ self,
|
|
|
+ exe_path: str,
|
|
|
+ config_path: str,
|
|
|
+ process_manager=process_manager
|
|
|
+ ):
|
|
|
+ self.exe_path = Path(exe_path)
|
|
|
+ self.config_path = Path(config_path)
|
|
|
+ self.process_manager = process_manager
|
|
|
+ self.process_name = f"{self.prefix}{self.config_path.stem}"
|
|
|
+ self.external_controller = get_external_controller(self.config_path)
|
|
|
+ self.provider_name = get_provider_name(self.config_path)
|
|
|
|
|
|
- def restart_proxy(self, exe_path: str, config_path: str) -> int:
|
|
|
- """
|
|
|
- 重启代理进程
|
|
|
- Args:
|
|
|
- exe_path (str): 代理可执行文件路径
|
|
|
- config_path (str): 配置文件路径
|
|
|
- Returns:
|
|
|
- int: 新的进程ID
|
|
|
+ async def start_proxy(self) -> int:
|
|
|
+ """启动代理进程"""
|
|
|
+ command = [str(self.exe_path), "-f", str(self.config_path)]
|
|
|
+ return await self.process_manager.start_process(self.process_name, command)
|
|
|
+
|
|
|
+ async def stop_proxy(self):
|
|
|
+ """停止代理进程"""
|
|
|
+ return await self.process_manager.stop_process(self.process_name)
|
|
|
+
|
|
|
+ async def restart_proxy(self) -> int:
|
|
|
+ """重启代理进程"""
|
|
|
+ await self.stop_proxy()
|
|
|
+ return await self.start_proxy()
|
|
|
+
|
|
|
+ def get_management_url(self) -> Optional[str]:
|
|
|
+
|
|
|
+ host, port = self.external_controller.split(":")
|
|
|
+ return f"https://yacd.metacubex.one/?hostname={host}&port={port}&secret=#/proxies"
|
|
|
+
|
|
|
+ async def ping_proxies(self):
|
|
|
+ """异步获取代理延迟
|
|
|
+ 失败: {'message': 'get delay: all proxies timeout'}
|
|
|
+ 成功: {'自动选择': 34, '🇦🇺澳大利亚悉尼': 159, '🇦🇺澳大利亚悉尼2': 1577,...}
|
|
|
"""
|
|
|
- self.stop_proxy(config_path)
|
|
|
- return self.start_proxy(exe_path, config_path)
|
|
|
+ url = f"http://{self.external_controller}/group/{self.provider_name}/delay?url=https%3A%2F%2Fwww.gstatic.com%2Fgenerate_204&timeout=2000"
|
|
|
+ async with httpx.AsyncClient() as client:
|
|
|
+ response = await client.get(url, timeout=10)
|
|
|
+ response.raise_for_status()
|
|
|
+ return response.json()
|
|
|
|
|
|
- def get_management_url(self, config_path: Path) -> Optional[str]:
|
|
|
+ async def select_proxy(self, name: str='') -> int:
|
|
|
"""
|
|
|
- 获取代理管理页面URL
|
|
|
- Args:
|
|
|
- config_path (Path): 配置文件路径
|
|
|
- Returns:
|
|
|
- Optional[str]: 管理页面URL,如果配置无效则返回None
|
|
|
+ 选择代理,如果 name 为空,则随机选择一个代理
|
|
|
"""
|
|
|
- with open(config_path, "r", encoding="utf-8") as f:
|
|
|
- config = yaml.safe_load(f)
|
|
|
+ if not name:
|
|
|
+ proxy = await self.ping_proxies()
|
|
|
+ name = random.choice(list(proxy.keys()))
|
|
|
+ url = f"http://{self.external_controller}/proxies/{self.provider_name}"
|
|
|
+ payload = {"name": name}
|
|
|
|
|
|
- if "external-controller" not in config:
|
|
|
- return None
|
|
|
-
|
|
|
- host, port = config["external-controller"].split(":")
|
|
|
- return f"https://yacd.metacubex.one/?hostname={host}&port={port}&secret=#/proxies"
|
|
|
-
|
|
|
+ async with httpx.AsyncClient() as client:
|
|
|
+ response = await client.put(url, json=payload)
|
|
|
+ response.raise_for_status()
|
|
|
+ return response.json()
|
|
|
+ @classmethod
|
|
|
+ def get_all_running_proxies(cls) -> list:
|
|
|
+ """获取所有正在运行的代理进程"""
|
|
|
+ return [p for p in process_manager.processes.values()
|
|
|
+ if p["name"].startswith(cls.prefix)]
|