diff --git a/.github/workflows/update_hosts.yml b/.github/workflows/update_hosts.yml new file mode 100644 index 0000000..891541c --- /dev/null +++ b/.github/workflows/update_hosts.yml @@ -0,0 +1,53 @@ +name: Update hosts + +on: + push: + paths: + - 'setHosts.py' + - 'requirements.txt' + schedule: + - cron: '0 */4 * * *' + workflow_dispatch: # 允许手动触发 + +jobs: + update-hosts: + runs-on: ubuntu-latest + + # 添加并发限制,避免同时运行多个工作流 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 # 减少克隆深度,加快检出速度 + + - name: Set up Python + uses: actions/setup-python@v5 # 使用最新的 v5 版本 + with: + python-version: '3.11' + cache: 'pip' # 启用 pip 缓存 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Run hosts update script + run: | + sudo python setHosts.py + + - name: Commit and push changes + run: | + git config --global user.email "sinspired@gmail.com" + git config --global user.name "action_bot" + + if [[ -n $(git status -s) ]]; then + git add . + git commit -m "更新 hosts 内容" + git push + else + echo "No changes to commit" + fi \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9bf14eb..b115bf4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ __pycache__ build dist *test*.* +*_temp*.* +*_tmp*.* .idea/ *cache/ @@ -23,3 +25,6 @@ debhelper-build-stamp *.substvars *.csv .vagrant + +# 下方文件包含进仓库 +!README_template.md diff --git a/README.md b/README.md index e87fc5c..0a10839 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,157 @@ > [!NOTE] > 首次运行大约需要2分钟以获取DNS主机,请耐心等待。后续运行速度大约10秒左右 -# 安装 +## 一、使用方法 + +### 1.1 自动操作 + +直接下载下方文件,解压后双击运行,enjoy❤! + +[![Release Detail](https://img.shields.io/github/v/release/sinspired/cnNetTool?sort=date&display_name=release&logo=github&label=Release)](https://github.com/sinspired/cnNetTool/releases/latest) + +> 强烈建议采用本方法,如果喜欢折腾,可以继续往下看。 + +### 1.2 手动操作 + +#### 1.2.1 复制下面的内容 + +```bash + +# cnNetTool Start in 2024-11-14 03:55:51 +08:00 +140.82.112.26 alive.github.com +20.205.243.168 api.github.com +140.82.113.21 central.github.com +20.205.243.165 codeload.github.com +140.82.113.22 collector.github.com +140.82.113.3 gist.github.com +20.205.243.166 github.com +140.82.112.17 github.community +151.101.65.194 github.global.ssl.fastly.net +54.231.162.1 github-com.s3.amazonaws.com +3.5.27.154 github-production-release-asset-2e65be.s3.amazonaws.com +140.82.113.25 live.github.com +13.107.42.16 pipelines.actions.githubusercontent.com +2620:1ec:21::16 pipelines.actions.githubusercontent.com +185.199.111.154 github.githubassets.com +185.199.109.153 github.io +2606:50c0:8000::153 github.io +185.199.109.153 githubstatus.com +2606:50c0:8000::153 githubstatus.com +185.199.109.153 assets-cdn.github.com +2606:50c0:8000::153 assets-cdn.github.com +185.199.111.133 avatars.githubusercontent.com +2606:50c0:8002::154 avatars.githubusercontent.com +185.199.111.133 avatars0.githubusercontent.com +2606:50c0:8002::154 avatars0.githubusercontent.com +185.199.111.133 avatars1.githubusercontent.com +2606:50c0:8002::154 avatars1.githubusercontent.com +185.199.111.133 avatars2.githubusercontent.com +2606:50c0:8002::154 avatars2.githubusercontent.com +185.199.111.133 avatars3.githubusercontent.com +2606:50c0:8002::154 avatars3.githubusercontent.com +185.199.111.133 avatars4.githubusercontent.com +2606:50c0:8002::154 avatars4.githubusercontent.com +185.199.111.133 avatars5.githubusercontent.com +2606:50c0:8002::154 avatars5.githubusercontent.com +185.199.111.133 camo.githubusercontent.com +2606:50c0:8002::154 camo.githubusercontent.com +185.199.111.133 cloud.githubusercontent.com +2606:50c0:8002::154 cloud.githubusercontent.com +185.199.111.133 desktop.githubusercontent.com +2606:50c0:8002::154 desktop.githubusercontent.com +185.199.111.133 favicons.githubusercontent.com +2606:50c0:8002::154 favicons.githubusercontent.com +185.199.111.133 github.map.fastly.net +2606:50c0:8002::154 github.map.fastly.net +185.199.111.133 media.githubusercontent.com +2606:50c0:8002::154 media.githubusercontent.com +185.199.111.133 objects.githubusercontent.com +2606:50c0:8002::154 objects.githubusercontent.com +185.199.111.133 private-user-images.githubusercontent.com +2606:50c0:8002::154 private-user-images.githubusercontent.com +185.199.111.133 raw.githubusercontent.com +2606:50c0:8002::154 raw.githubusercontent.com +185.199.111.133 user-images.githubusercontent.com +2606:50c0:8002::154 user-images.githubusercontent.com +18.244.18.65 tmdb.org +2600:9000:21ee:ce00:5:da10:7440:93a1 tmdb.org +18.244.18.65 api.tmdb.org +2600:9000:21ee:ce00:5:da10:7440:93a1 api.tmdb.org +18.244.18.65 files.tmdb.org +2600:9000:21ee:ce00:5:da10:7440:93a1 files.tmdb.org +3.160.150.29 themoviedb.org +2600:9000:275b:b800:16:e4a1:eb00:93a1 themoviedb.org +3.160.150.29 api.themoviedb.org +2600:9000:275b:b800:16:e4a1:eb00:93a1 api.themoviedb.org +3.160.150.29 www.themoviedb.org +2600:9000:275b:b800:16:e4a1:eb00:93a1 www.themoviedb.org +3.160.150.29 auth.themoviedb.org +2600:9000:275b:b800:16:e4a1:eb00:93a1 auth.themoviedb.org +143.244.50.213 image.tmdb.org +2400:52e0:1a01::987:1 image.tmdb.org +143.244.50.213 images.tmdb.org +2400:52e0:1a01::987:1 images.tmdb.org +52.94.225.248 imdb.com +13.226.254.49 www.imdb.com +52.94.228.167 secure.imdb.com +13.226.254.49 s.media-imdb.com +52.94.228.167 us.dd.imdb.com +13.226.254.49 www.imdb.to +52.94.237.74 imdb-webservice.amazon.com +98.82.155.134 origin-www.imdb.com +151.101.1.16 m.media-amazon.com +2600:1417:8400:4::173c:6094 m.media-amazon.com +61.213.189.194 Images-na.ssl-images-amazon.com +2600:1417:8400:4::173c:6094 Images-na.ssl-images-amazon.com +61.213.189.194 images-fe.ssl-images-amazon.com +2600:1417:8400:4::173c:6095 images-fe.ssl-images-amazon.com +61.213.189.194 images-eu.ssl-images-amazon.com +2600:1417:8400:4::173c:6094 images-eu.ssl-images-amazon.com +61.213.189.194 ia.media-imdb.com +2600:1417:8400:4::173c:6095 ia.media-imdb.com +151.101.89.16 f.media-amazon.com +2a04:4e42:15::272 f.media-amazon.com +3.168.147.92 imdb-video.media-imdb.com +18.173.117.77 dqpnq362acqdi.cloudfront.net +2600:9000:25f2:3800:5:ce70:a180:21 dqpnq362acqdi.cloudfront.net +209.85.232.195 translate.google.com +2a00:1450:4001:829::201a translate.google.com +209.85.232.195 translate.googleapis.com +2a00:1450:4001:829::201a translate.googleapis.com +209.85.232.195 translate-pa.googleapis.com +2a00:1450:4001:829::201a translate-pa.googleapis.com +18.65.168.9 plugins.jetbrains.com +2600:9000:24bb:5e00:12:7c44:15c0:93a1 plugins.jetbrains.com +18.65.168.9 download.jetbrains.com +2600:9000:24bb:5e00:12:7c44:15c0:93a1 download.jetbrains.com +18.65.168.9 cache-redirector.jetbrains.com +2600:9000:24bb:5e00:12:7c44:15c0:93a1 cache-redirector.jetbrains.com + +# Update time: 2024-11-14 03:55:51 +08:00 +# GitHub仓库: https://github.com/sinspired/cnNetTool +# cnNetTool End + +``` + +该内容会自动定时更新, 数据更新时间:2024-11-14 03:55:51 +08:00 + +#### 1.2.2 修改 hosts 文件 + +hosts 文件在每个系统的位置不一,详情如下: +- Windows 系统:`C:\Windows\System32\drivers\etc\hosts` +- Linux 系统:`/etc/hosts` +- Mac(苹果电脑)系统:`/etc/hosts` +- Android(安卓)系统:`/system/etc/hosts` +- iPhone(iOS)系统:`/etc/hosts` + +修改方法,把第一步的内容复制到文本末尾: + +1. Windows 使用记事本。 +2. Linux、Mac 使用 Root 权限:`sudo vi /etc/hosts`。 +3. iPhone、iPad 须越狱、Android 必须要 root。 + + +## 二、安装 首先安装 python,然后在终端中运行以下命令: @@ -25,7 +175,7 @@ pip install -r requirements.txt ``` 这将安装所有依赖项 -# 参数说明 +## 参数说明 **cnNetTool** 可以接受以下参数: @@ -39,9 +189,10 @@ pip install -r requirements.txt ### Hosts文件工具 `SetHosts.py` -* --log-level 设置日志输出等级,'DEBUG', 'INFO', 'WARNING', 'ERROR' -* --num-fastest 限定Hosts主机 ip 数量 -* --max-latency 设置允许的最大延迟(毫秒) +* -log 设置日志输出等级,'DEBUG', 'INFO', 'WARNING', 'ERROR' +* -num --num-fastest 限定Hosts主机 ip 数量 +* -max --max-latency 设置允许的最大延迟(毫秒) +* -v --verbose 打印运行信息 命令行键入 `-h` `help` 获取帮助 @@ -49,7 +200,7 @@ pip install -r requirements.txt `py SetHosts.py -h` -# 运行 +## 三、运行 请使用管理员权限,在项目目录运行,分别设置解析最快的DNS服务器,更新hosts文件。 **接受传递参数,大部分时候直接运行即可**。 @@ -57,16 +208,9 @@ pip install -r requirements.txt py SetDNS.py py SetHosts.py ``` - -# 最新发行版下载 - -Windows下载可执行文件双击运行即可,注意使用管理员权限。linux系统使用sudo。 - -或在命令行设置参数运行: - +可执行文件也可带参数运行 ```pwsh ./SetDNS.exe --best-dns-num 10 ./SetHosts.exe --num-fastest 3 --max-latency 500 ``` -[![Release Detail](https://img.shields.io/github/v/release/sinspired/cnNetTool?sort=date&display_name=release&logo=github&label=Release)](https://github.com/sinspired/cnNetTool/releases/latest) \ No newline at end of file diff --git a/README_template.md b/README_template.md new file mode 100644 index 0000000..372acba --- /dev/null +++ b/README_template.md @@ -0,0 +1,102 @@ +# cnNetTool + +[![Release Version](https://img.shields.io/github/v/release/sinspired/cnNetTool?display_name=tag&logo=github&label=Release)](https://github.com/sinspired/cnNetTool/releases/latest) +[![GitHub repo size](https://img.shields.io/github/repo-size/sinspired/cnNetTool?logo=github) +](https://github.com/sinspired/cnNetTool) +[![GitHub last commit](https://img.shields.io/github/last-commit/sinspired/cnNetTool?logo=github&label=最后提交:)](ttps://github.com/sinspired/cnNetTool) + +全面解锁Github,解决加载慢、无法访问等问题!解锁Google翻译,支持chrome网页翻译及插件,解锁划词翻译,以及依赖Google翻译API的各种平台插件。解锁tinyMediaManager影视刮削。 + +自动设置最佳DNS服务器。 + +> 适合部分地区饱受dns污染困扰,访问 GitHub 卡顿、抽风、图裂,无法使用Chrome浏览器 自带翻译功能,无法刮削影视封面等问题。分别使用 `setDNS` 自动查找最快服务器并设置,使用 `setHosts` 自动查找DNS映射主机并设置。支持Windows、Linux、MacOS。Enjoy!❤ + +> [!NOTE] +> 首次运行大约需要2分钟以获取DNS主机,请耐心等待。后续运行速度大约10秒左右 + +## 一、使用方法 + +### 1.1 自动操作 + +直接下载下方文件,解压后双击运行,enjoy❤! + +[![Release Detail](https://img.shields.io/github/v/release/sinspired/cnNetTool?sort=date&display_name=release&logo=github&label=Release)](https://github.com/sinspired/cnNetTool/releases/latest) + +> 强烈建议采用本方法,如果喜欢折腾,可以继续往下看。 + +### 1.2 手动操作 + +#### 1.2.1 复制下面的内容 + +```bash +{hosts_str} +``` + +该内容会自动定时更新, 数据更新时间:{update_time} + +#### 1.2.2 修改 hosts 文件 + +hosts 文件在每个系统的位置不一,详情如下: +- Windows 系统:`C:\Windows\System32\drivers\etc\hosts` +- Linux 系统:`/etc/hosts` +- Mac(苹果电脑)系统:`/etc/hosts` +- Android(安卓)系统:`/system/etc/hosts` +- iPhone(iOS)系统:`/etc/hosts` + +修改方法,把第一步的内容复制到文本末尾: + +1. Windows 使用记事本。 +2. Linux、Mac 使用 Root 权限:`sudo vi /etc/hosts`。 +3. iPhone、iPad 须越狱、Android 必须要 root。 + + +## 二、安装 + +首先安装 python,然后在终端中运行以下命令: + +```bash +git clone https://github.com/sinspired/cnNetTool.git +cd cnNetTool +pip install -r requirements.txt +``` +这将安装所有依赖项 + +## 参数说明 + +**cnNetTool** 可以接受以下参数: + +### DNS 服务器工具 `SetDNS.py` + +* --debug 启用调试日志 +* --show-availbale-list, --list 显示可用dns列表,通过 --num 控制显示数量 +* --best-dns-num BEST_DNS_NUM, --num 显示最佳DNS服务器的数量 +* --algorithm --mode {region,overall} 默认 `region` 平衡IPv4和ipv6 DNS +* --show-resolutions, --show 显示域名解析结果 + +### Hosts文件工具 `SetHosts.py` + +* -log 设置日志输出等级,'DEBUG', 'INFO', 'WARNING', 'ERROR' +* -num --num-fastest 限定Hosts主机 ip 数量 +* -max --max-latency 设置允许的最大延迟(毫秒) +* -v --verbose 打印运行信息 + +命令行键入 `-h` `help` 获取帮助 + +`py SetDNS.py -h` + +`py SetHosts.py -h` + +## 三、运行 + +请使用管理员权限,在项目目录运行,分别设置解析最快的DNS服务器,更新hosts文件。 **接受传递参数,大部分时候直接运行即可**。 + +```bash +py SetDNS.py +py SetHosts.py +``` +可执行文件也可带参数运行 +```pwsh +./SetDNS.exe --best-dns-num 10 +./SetHosts.exe --num-fastest 3 --max-latency 500 +``` + diff --git a/SetHosts.py b/SetHosts.py index 1a6da34..0f0eec5 100644 --- a/SetHosts.py +++ b/SetHosts.py @@ -19,15 +19,12 @@ BarColumn, TaskID, TimeRemainingColumn, - SpinnerColumn, - TextColumn, ) from rich import print as rprint import ctypes import re from functools import wraps -import wcwidth # -------------------- 常量设置 -------------------- # RESOLVER_TIMEOUT = 1 # DNS 解析超时时间 秒 @@ -59,25 +56,31 @@ def parse_args(): ) parser.add_argument( - "--log", + "-log", default="info", choices=["debug", "info", "warning", "error"], help="设置日志输出等级", ) parser.add_argument( + "-num", "--hosts-num", - "--num", default=HOSTS_NUM, type=int, help="限定Hosts主机 ip 数量", ) parser.add_argument( + "-max", "--max-latency", - "--max", default=MAX_LATENCY, type=int, help="设置允许的最大延迟(毫秒)", ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + help="打印运行信息", + ) return parser.parse_args() @@ -110,6 +113,50 @@ def backup_hosts_file(hosts_file_path: str): f"\n[blue]已备份 [underline]{hosts_file_path}[/underline] 到 [underline]{backup_path}[/underline][/blue]" ) + @staticmethod + def write_readme_file( + hosts_content: List[str], temp_file_path: str, update_time: str + ): + """ + 根据模板文件生成 README.md 文件,并将 hosts 文件内容写入其中。 + + 参数: + hosts_content (List[str]): hosts 文件的内容,以列表形式传入 + temp_file_path (str): 输出的 README.md 文件路径 + update_time (str): hosts 文件的更新时间,格式为 "YYYY-MM-DD HH:MM:SS +0800" + """ + try: + # 获取template文件的绝对路径 + current_dir = os.path.dirname(os.path.abspath(__file__)) + template_path = os.path.join(current_dir, temp_file_path) + + if not os.path.exists(template_path): + raise FileNotFoundError(f"模板文件未找到: {template_path}") + + # 读取模板文件 + with open(template_path, "r", encoding="utf-8") as temp_fb: + template_content = temp_fb.read() + + # 将hosts内容转换为字符串 + hosts_str = "\n".join(hosts_content) + + # 使用替换方法而不是format + readme_content = template_content.replace("{hosts_str}", hosts_str) + readme_content = readme_content.replace("{update_time}", update_time) + + # 写入新文件 + with open("README.md", "w", encoding="utf-8") as output_fb: + output_fb.write(readme_content) + + rprint( + f"[blue]已更新 README.md 文件,位于: [underline]README.md[/underline][/blue]\n" + ) + + except FileNotFoundError as e: + print(f"错误: {str(e)}") + except Exception as e: + print(f"生成 README.md 文件时发生错误: {str(e)}") + def get_formatted_line(char="-", color="green", width_percentage=0.97): """ 生成格式化的分隔线 @@ -252,7 +299,7 @@ async def resolve_domain(self, domain: str) -> Set[str]: ips.update(ipaddress_ips) if ips: - logging.debug(f"成功解析 {domain}, 找到 {len(ips)} 个 DNS 主机") + logging.debug(f"成功解析 {domain}, 发现 {len(ips)} 个 DNS 主机") else: logging.debug(f"警告: 无法解析 {domain}") @@ -348,8 +395,8 @@ async def _resolve_via_ipaddress(self, domain: str) -> Set[str]: ) logging.debug(f"DNS_records:\n {ips}") else: - logging.warning( - f"ipaddress.com 未找到 {domain} 的 DNS_records 地址" + logging.debug( + f"ipaddress.com 未解析到 {domain} 的 DNS_records 地址" ) except Exception as e: @@ -368,6 +415,11 @@ def __init__(self, resolver: DomainResolver, hosts_num: int): self.progress = None self.current_task = None + def set_progress(self, progress, task): + """设置进度显示器和当前任务""" + self.progress = progress + self.current_task = task + async def get_latency(self, ip: str, port: int = 443) -> float: try: # 使用 getaddrinfo 来获取正确的地址格式 @@ -414,62 +466,71 @@ async def get_host_average_latency( logging.error(f"{ip} 平均延迟为 0 ms,视为无效") return ip, float("inf") + if self.progress and self.current_task: + self.progress.update(self.current_task, advance=1) + logging.debug(f"{ip} 平均延迟: {average_response_time:.2f} ms") return ip, average_response_time except Exception as e: logging.debug(f"ping {ip} 时出错: {e}") return ip, float("inf") - def set_progress(self, progress: Progress, task_description: str = None): - """设置进度显示实例""" - self.progress = progress - if task_description: - self.current_task = self.progress.add_task(task_description, total=1) - async def get_lowest_latency_hosts( - self, group_name:str,domains: List[str], file_ips: Set[str], latency_limit: int + self, + group_name: str, + domains: List[str], + file_ips: Set[str], + latency_limit: int, + latency_task_id: TaskID, ) -> List[Tuple[str, float]]: - all_ips = set() + all_ips = file_ips + total_ips = len(all_ips) - # 解析域名 - if self.progress and self.current_task: - self.progress.update(self.current_task, description="正在解析域名...") - tasks = [self.resolver.resolve_domain(domain) for domain in domains] - for ips in await asyncio.gather(*tasks): - all_ips.update(ips) - all_ips.update(file_ips) - else: - tasks = [self.resolver.resolve_domain(domain) for domain in domains] - for ips in await asyncio.gather(*tasks): - all_ips.update(ips) - all_ips.update(file_ips) - - rprint( - f"[bright_black]- 找到 [bold bright_green]{len(all_ips):2}[/bold bright_green] 个唯一IP地址 [{group_name}][/bright_black]" - ) + # 更新进度条描述和总数 + if self.progress and latency_task_id: + self.progress.update( + latency_task_id, + total=total_ips, + visible=False, + ) + if args.verbose: + rprint( + f"[bright_black]- 解析到 [bold bright_green]{len(all_ips):2}[/bold bright_green] 个唯一IP地址 [{group_name}][/bright_black]" + ) # Ping所有IP - if self.progress and self.current_task: + ping_tasks = [self.get_host_average_latency(ip) for ip in all_ips] + + results = [] + # 使用 asyncio.as_completed 确保每个任务完成时立即处理 + for coro in asyncio.as_completed(ping_tasks): + result = await coro + results.append(result) + + # 每完成一个任务立即更新进度 + if self.progress and latency_task_id: + self.progress.update( + latency_task_id, + advance=1, + visible=True, + total=total_ips, + ) + + if self.progress and latency_task_id: + # 确保进度完结 self.progress.update( - self.current_task, description="正在 ping 所有IP地址..." + latency_task_id, + completed=total_ips, + visible=True, ) - ping_tasks = [self.get_host_average_latency(ip) for ip in all_ips] - results = [] - for result in await asyncio.gather(*ping_tasks): - results.append(result) - else: - ping_tasks = [self.get_host_average_latency(ip) for ip in all_ips] - results = [] - for result in await asyncio.gather(*ping_tasks): - results.append(result) valid_results = [result for result in results if result[1] < latency_limit] if not valid_results: - logging.warning(f"未找到延迟小于 {latency_limit}ms 的IP。") + logging.warning(f"未发现延迟小于 {latency_limit}ms 的IP。") if results: latency_limit = latency_limit * 2 - logging.info(f"放宽延迟限制为 {latency_limit}ms 重新搜索...") + logging.info(f"放宽延迟限制为 {latency_limit}ms 重新挑选...") valid_results = [ result for result in results if result[1] < latency_limit ] @@ -486,16 +547,16 @@ async def get_lowest_latency_hosts( else: best_hosts = sorted(valid_results, key=lambda x: x[1])[: self.hosts_num] - if self.progress and self.current_task: - self.progress.update(self.current_task, advance=1) - - rprint( - f"[bold yellow]最快的 DNS主机 IP(优先选择 IPv6) 延迟 < {latency_limit}ms 丨 [{group_name}] :[/bold yellow]" - ) - for ip, time in best_hosts: + if args.verbose: rprint( - f" [green]{ip}[/green] [bright_black]{time:.2f} ms[/bright_black]" + f"[bold yellow]最快DNS主机 {'(IPv4/IPv6)' if ipv6_results else '(IPv4 Only)'} 延迟 < {latency_limit}ms | [{group_name}] " + f"{domains[0] if len(domains) == 1 else f'{len(domains)} 域名合用 IP'}:[/bold yellow]" ) + + for ip, time in best_hosts: + rprint( + f" [green]{ip}[/green] [bright_black]{time:.2f} ms[/bright_black]" + ) return best_hosts @@ -565,8 +626,11 @@ def write_to_hosts_file(self, new_entries: List[str]): rprint("\n[bold yellow]正在更新 hosts 文件...[/bold yellow]") + save_hosts_content = [] # 提取新内容文本 + # 1. 添加标题 - new_content.append("\n# cnNetTool Start\n") + new_content.append(f"\n# cnNetTool Start in {update_time}") + save_hosts_content.append(f"\n# cnNetTool Start in {update_time}") # 2. 添加主机条目 for entry in new_entries: @@ -589,6 +653,7 @@ def write_to_hosts_file(self, new_entries: List[str]): # 返回格式化后的条目 formatedEntry = f"{ip}{tabs}{domain}" new_content.append(formatedEntry) + save_hosts_content.append(formatedEntry) rprint(f"+ {formatedEntry}") # 3. 添加项目描述 @@ -599,11 +664,29 @@ def write_to_hosts_file(self, new_entries: List[str]): "# cnNetTool End\n", ] ) + save_hosts_content.extend( + [ + f"\n# Update time: {update_time}", + "# GitHub仓库: https://github.com/sinspired/cnNetTool", + "# cnNetTool End\n", + ] + ) # 4. 写入hosts文件 with open(self.hosts_file_path, "w") as f: f.write("\n".join(new_content)) + # 保存 hosts 文本 + with open("hosts", "w") as f: + f.write("\n".join(save_hosts_content)) + rprint( + f"\n[blue]已生成 hosts 文件,位于: [underline]hosts[/underline][/blue]" + ) + + Utils.write_readme_file( + save_hosts_content, "README_template.md", f"{update_time}" + ) + # -------------------- 主控制模块 -------------------- # class HostsUpdater: @@ -619,11 +702,7 @@ def __init__( self.tester = tester self.hosts_manager = hosts_manager # 添加并发限制 - self.semaphore = asyncio.Semaphore(5) # 限制并发请求数 - # 添加计数器用于控制ipaddress.com的访问频率 - self.ipaddress_counter = 0 - self.ipaddress_limit = 20 # 每批次最大请求数 - self.ipaddress_delay = 60 # 批次之间的延迟(秒) + self.semaphore = asyncio.Semaphore(200) # 限制并发请求数 # 添加进度显示实例 self.progress = Progress( @@ -634,29 +713,42 @@ def __init__( ) async def _resolve_domains_batch( - self, domains: List[str], task_id: TaskID + self, domains: List[str], resolve_task_id: TaskID ) -> Dict[str, Set[str]]: """批量解析域名,带进度更新""" results = {} total_domains = len(domains) + # 更新进度条描述和总数 + if self.progress and resolve_task_id: + self.progress.update( + resolve_task_id, + total=total_domains, + ) + async with self.semaphore: for i, domain in enumerate(domains, 1): try: ips = await self.resolver.resolve_domain(domain) results[domain] = ips - # 更新进度 - self.progress.update(task_id, advance=1) except Exception as e: logging.error(f"解析域名 {domain} 失败: {e}") results[domain] = set() - if "_resolve_via_ipaddress" in str(self.resolver.resolve_domain): - self.ipaddress_counter += 1 - if self.ipaddress_counter >= self.ipaddress_limit: - await asyncio.sleep(self.ipaddress_delay) - self.ipaddress_counter = 0 + # 更新进度 + self.progress.update( + resolve_task_id, + advance=1, + visible=True, + ) + if self.progress and resolve_task_id: + # 确保进度完结 + self.progress.update( + resolve_task_id, + completed=total_domains, + visible=True, + ) return results async def _process_domain_group(self, group: DomainGroup, index: int) -> List[str]: @@ -664,21 +756,48 @@ async def _process_domain_group(self, group: DomainGroup, index: int) -> List[st entries = [] all_ips = group.ips.copy() - # 创建该组的进度任务 - task_id = self.progress.add_task( - f"处理组 {group.name}", total=len(group.domains) + # 创建 seperateGroup 的主进度任务 + seperateGroup_task_id = self.progress.add_task( + f"处理组 {group.name}", + total=len(group.domains), + visible=False, + ) + + # 创建 seperateGroup 的主进度任务 + shareGroup_task_id = self.progress.add_task( + f"处理组 {group.name}", + total=100, + visible=False, ) - # 为 LatencyTester 设置进度显示 - self.tester.set_progress(self.progress, f"处理组 {group.name} 的延迟测试") + # 为 _resolve_domains_batch 设置 [域名解析] 子任务进度显示 + resolve_task_id = self.progress.add_task( + f"- [域名解析] {group.name}", + total=0, # 初始设为0,后续会更新 + visible=False, # 初始隐藏,等需要时显示 + ) + + # 为 LatencyTester 设置子任务进度显示 + latency_task_id = self.progress.add_task( + f"- [测试延迟] {group.name}", + total=0, # 初始设为0,后续会更新 + visible=False, # 初始隐藏,等需要时显示 + ) + + self.tester.set_progress(self.progress, latency_task_id) if group.group_type == GroupType.SEPARATE: for domain in group.domains: - resolved_ips = await self._resolve_domains_batch([domain], task_id) + resolved_ips = await self._resolve_domains_batch( + [domain], resolve_task_id + ) domain_ips = resolved_ips.get(domain, set()) + # 隐藏域名解析进度条 + self.progress.update(resolve_task_id, visible=False) + if not domain_ips: - logging.warning(f"{domain} 未找到任何可用IP。跳过该域名。") + logging.warning(f"{domain} 未解析到任何可用IP。跳过该域名。") continue fastest_ips = await self.tester.get_lowest_latency_hosts( @@ -686,64 +805,99 @@ async def _process_domain_group(self, group: DomainGroup, index: int) -> List[st [domain], domain_ips, self.resolver.max_latency, + latency_task_id, ) if fastest_ips: entries.extend(f"{ip}\t{domain}" for ip, latency in fastest_ips) else: - logging.warning(f"{domain} 未找到延迟满足要求的IP。") + logging.warning(f"{domain} 未发现满足延迟检测要求的IP。") + # 隐藏延迟测试进度条 + self.progress.update(latency_task_id, visible=False) + # 主进度更新 + self.progress.update( + seperateGroup_task_id, + advance=1, + visible=True, + ) + + self.progress.update( + seperateGroup_task_id, + visible=False, + ) + + # 标记该组处理完成 + self.progress.update( + seperateGroup_task_id, + description=f"处理组 {group.name}", + completed=len(group.domains), + visible=True, + ) + else: + # 共用主机的域名组 resolved_ips_dict = await self._resolve_domains_batch( - group.domains, task_id + group.domains, resolve_task_id + ) + # 隐藏域名解析进度条 + self.progress.update(resolve_task_id, visible=False) + self.progress.update( + shareGroup_task_id, + visible=True, + advance=40, ) for ips in resolved_ips_dict.values(): all_ips.update(ips) if not all_ips: - logging.warning(f"组 {group.name} 未找到任何可用IP。跳过该组。") + logging.warning(f"组 {group.name} 未解析到任何可用IP。跳过该组。") return entries - logging.info(f"组 {group.name} 找到 {len(all_ips)} 个 DNS 主机记录") + logging.debug(f"组 {group.name} 解析到 {len(all_ips)} 个 DNS 主机记录") fastest_ips = await self.tester.get_lowest_latency_hosts( group.name, group.domains, all_ips, self.resolver.max_latency, + latency_task_id, + ) + self.progress.update( + shareGroup_task_id, + visible=True, + advance=40, ) + # 隐藏延迟测试进度条 + self.progress.update(latency_task_id, visible=False) + if fastest_ips: for domain in group.domains: entries.extend(f"{ip}\t{domain}" for ip, latency in fastest_ips) - logging.info(f"已处理域名: {domain}") + # logging.info(f"已处理域名: {domain}") else: - logging.warning(f"组 {group.name} 未找到延迟满足要求的IP。") + logging.warning(f"组 {group.name} 未发现满足延迟检测要求的IP。") + + self.progress.update( + shareGroup_task_id, + visible=True, + advance=20, + ) - # 标记该组处理完成 - self.progress.update(task_id, completed=len(group.domains)) return entries async def update_hosts(self): - """优化后的主更新函数,支持并发进度显示""" + """主更新函数,支持并发进度显示""" cache_valid = self.resolver._is_dns_cache_valid() with self.progress: - if cache_valid: - # 并发处理所有组 - tasks = [ - self._process_domain_group(group, i) - for i, group in enumerate(self.domain_groups, 1) - ] - all_entries_lists = await asyncio.gather(*tasks) - all_entries = [ - entry for entries in all_entries_lists for entry in entries - ] - else: - # 顺序处理所有组 - all_entries = [] - for i, group in enumerate(self.domain_groups, 1): - entries = await self._process_domain_group(group, i) - all_entries.extend(entries) + # 并发处理所有组 + tasks = [ + self._process_domain_group(group, i) + for i, group in enumerate(self.domain_groups, 1) + ] + all_entries_lists = await asyncio.gather(*tasks) + all_entries = [entry for entries in all_entries_lists for entry in entries] if all_entries: self.hosts_manager.write_to_hosts_file(all_entries) @@ -787,25 +941,20 @@ class Config: name="GitHub Services", group_type=GroupType.SEPARATE, domains=[ - "github.com", - "api.github.com", - "gist.github.com", "alive.github.com", - "github.community", + "api.github.com", "central.github.com", "codeload.github.com", "collector.github.com", - "vscode.dev", - "github.blog", - "live.github.com", - "education.github.com", + "gist.github.com", + "github.com", + "github.community", "github.global.ssl.fastly.net", - "pipelines.actions.githubusercontent.com", "github-com.s3.amazonaws.com", - "github-cloud.s3.amazonaws.com", - "github-production-user-asset-6210df.s3.amazonaws.com", "github-production-release-asset-2e65be.s3.amazonaws.com", - "github-production-repository-file-5c1aeb.s3.amazonaws.com", + "live.github.com", + "pipelines.actions.githubusercontent.com", + "github.githubassets.com", ], ips={}, ), @@ -816,7 +965,6 @@ class Config: "github.io", "githubstatus.com", "assets-cdn.github.com", - "github.githubassets.com", ], ips={}, ), @@ -836,11 +984,11 @@ class Config: "desktop.githubusercontent.com", "favicons.githubusercontent.com", "github.map.fastly.net", - "raw.githubusercontent.com", "media.githubusercontent.com", "objects.githubusercontent.com", - "user-images.githubusercontent.com", "private-user-images.githubusercontent.com", + "raw.githubusercontent.com", + "user-images.githubusercontent.com", ], ips={}, ), @@ -885,7 +1033,7 @@ class Config: ips={}, ), DomainGroup( - name="IMDB 图片/视频/js脚本", + name="IMDB CDN", group_type=GroupType.SEPARATE, domains=[ "m.media-amazon.com", @@ -926,7 +1074,7 @@ class Config: }, ), DomainGroup( - name="JetBrain 插件下载", + name="JetBrain 插件", domains=[ "plugins.jetbrains.com", "download.jetbrains.com", diff --git a/SetHosts_Classic.py b/SetHosts_Classic.py index e7300d4..c528544 100644 --- a/SetHosts_Classic.py +++ b/SetHosts_Classic.py @@ -535,10 +535,10 @@ def write_to_hosts_file(self, new_entries: List[str]): .replace("+0800", "+08:00") ) - rprint("\n[bold yellow]正在更新hosts文件...[/bold yellow]") + rprint("\n[bold yellow]正在更新 hosts 文件...[/bold yellow]") # 1. 添加标题 - new_content.append("\n# cnNetTool Start\n") + new_content.append(f"\n# cnNetTool Start in {update_time}") # 2. 添加主机条目 for entry in new_entries: @@ -696,25 +696,20 @@ class Config: name="GitHub Services", group_type=GroupType.SEPARATE, domains=[ - "github.com", - "api.github.com", - "gist.github.com", "alive.github.com", - "github.community", + "api.github.com", "central.github.com", "codeload.github.com", "collector.github.com", - "vscode.dev", - "github.blog", - "live.github.com", - "education.github.com", + "gist.github.com", + "github.com", + "github.community", "github.global.ssl.fastly.net", - "pipelines.actions.githubusercontent.com", "github-com.s3.amazonaws.com", - "github-cloud.s3.amazonaws.com", - "github-production-user-asset-6210df.s3.amazonaws.com", "github-production-release-asset-2e65be.s3.amazonaws.com", - "github-production-repository-file-5c1aeb.s3.amazonaws.com", + "live.github.com", + "pipelines.actions.githubusercontent.com", + "github.githubassets.com", ], ips={}, ), @@ -725,7 +720,6 @@ class Config: "github.io", "githubstatus.com", "assets-cdn.github.com", - "github.githubassets.com", ], ips={}, ), @@ -745,11 +739,11 @@ class Config: "desktop.githubusercontent.com", "favicons.githubusercontent.com", "github.map.fastly.net", - "raw.githubusercontent.com", "media.githubusercontent.com", "objects.githubusercontent.com", - "user-images.githubusercontent.com", "private-user-images.githubusercontent.com", + "raw.githubusercontent.com", + "user-images.githubusercontent.com", ], ips={}, ), @@ -921,7 +915,7 @@ class Config: ips={}, ), DomainGroup( - name="IMDB 图片/视频/js脚本", + name="IMDB CDN", group_type=GroupType.SEPARATE, domains=[ "m.media-amazon.com", @@ -962,7 +956,7 @@ class Config: }, ), DomainGroup( - name="JetBrain 插件下载", + name="JetBrain 插件", domains=[ "plugins.jetbrains.com", "download.jetbrains.com", diff --git a/hosts b/hosts new file mode 100644 index 0000000..cfd42ef --- /dev/null +++ b/hosts @@ -0,0 +1,114 @@ + +# cnNetTool Start in 2024-11-14 03:55:51 +08:00 +140.82.112.26 alive.github.com +20.205.243.168 api.github.com +140.82.113.21 central.github.com +20.205.243.165 codeload.github.com +140.82.113.22 collector.github.com +140.82.113.3 gist.github.com +20.205.243.166 github.com +140.82.112.17 github.community +151.101.65.194 github.global.ssl.fastly.net +54.231.162.1 github-com.s3.amazonaws.com +3.5.27.154 github-production-release-asset-2e65be.s3.amazonaws.com +140.82.113.25 live.github.com +13.107.42.16 pipelines.actions.githubusercontent.com +2620:1ec:21::16 pipelines.actions.githubusercontent.com +185.199.111.154 github.githubassets.com +185.199.109.153 github.io +2606:50c0:8000::153 github.io +185.199.109.153 githubstatus.com +2606:50c0:8000::153 githubstatus.com +185.199.109.153 assets-cdn.github.com +2606:50c0:8000::153 assets-cdn.github.com +185.199.111.133 avatars.githubusercontent.com +2606:50c0:8002::154 avatars.githubusercontent.com +185.199.111.133 avatars0.githubusercontent.com +2606:50c0:8002::154 avatars0.githubusercontent.com +185.199.111.133 avatars1.githubusercontent.com +2606:50c0:8002::154 avatars1.githubusercontent.com +185.199.111.133 avatars2.githubusercontent.com +2606:50c0:8002::154 avatars2.githubusercontent.com +185.199.111.133 avatars3.githubusercontent.com +2606:50c0:8002::154 avatars3.githubusercontent.com +185.199.111.133 avatars4.githubusercontent.com +2606:50c0:8002::154 avatars4.githubusercontent.com +185.199.111.133 avatars5.githubusercontent.com +2606:50c0:8002::154 avatars5.githubusercontent.com +185.199.111.133 camo.githubusercontent.com +2606:50c0:8002::154 camo.githubusercontent.com +185.199.111.133 cloud.githubusercontent.com +2606:50c0:8002::154 cloud.githubusercontent.com +185.199.111.133 desktop.githubusercontent.com +2606:50c0:8002::154 desktop.githubusercontent.com +185.199.111.133 favicons.githubusercontent.com +2606:50c0:8002::154 favicons.githubusercontent.com +185.199.111.133 github.map.fastly.net +2606:50c0:8002::154 github.map.fastly.net +185.199.111.133 media.githubusercontent.com +2606:50c0:8002::154 media.githubusercontent.com +185.199.111.133 objects.githubusercontent.com +2606:50c0:8002::154 objects.githubusercontent.com +185.199.111.133 private-user-images.githubusercontent.com +2606:50c0:8002::154 private-user-images.githubusercontent.com +185.199.111.133 raw.githubusercontent.com +2606:50c0:8002::154 raw.githubusercontent.com +185.199.111.133 user-images.githubusercontent.com +2606:50c0:8002::154 user-images.githubusercontent.com +18.244.18.65 tmdb.org +2600:9000:21ee:ce00:5:da10:7440:93a1 tmdb.org +18.244.18.65 api.tmdb.org +2600:9000:21ee:ce00:5:da10:7440:93a1 api.tmdb.org +18.244.18.65 files.tmdb.org +2600:9000:21ee:ce00:5:da10:7440:93a1 files.tmdb.org +3.160.150.29 themoviedb.org +2600:9000:275b:b800:16:e4a1:eb00:93a1 themoviedb.org +3.160.150.29 api.themoviedb.org +2600:9000:275b:b800:16:e4a1:eb00:93a1 api.themoviedb.org +3.160.150.29 www.themoviedb.org +2600:9000:275b:b800:16:e4a1:eb00:93a1 www.themoviedb.org +3.160.150.29 auth.themoviedb.org +2600:9000:275b:b800:16:e4a1:eb00:93a1 auth.themoviedb.org +143.244.50.213 image.tmdb.org +2400:52e0:1a01::987:1 image.tmdb.org +143.244.50.213 images.tmdb.org +2400:52e0:1a01::987:1 images.tmdb.org +52.94.225.248 imdb.com +13.226.254.49 www.imdb.com +52.94.228.167 secure.imdb.com +13.226.254.49 s.media-imdb.com +52.94.228.167 us.dd.imdb.com +13.226.254.49 www.imdb.to +52.94.237.74 imdb-webservice.amazon.com +98.82.155.134 origin-www.imdb.com +151.101.1.16 m.media-amazon.com +2600:1417:8400:4::173c:6094 m.media-amazon.com +61.213.189.194 Images-na.ssl-images-amazon.com +2600:1417:8400:4::173c:6094 Images-na.ssl-images-amazon.com +61.213.189.194 images-fe.ssl-images-amazon.com +2600:1417:8400:4::173c:6095 images-fe.ssl-images-amazon.com +61.213.189.194 images-eu.ssl-images-amazon.com +2600:1417:8400:4::173c:6094 images-eu.ssl-images-amazon.com +61.213.189.194 ia.media-imdb.com +2600:1417:8400:4::173c:6095 ia.media-imdb.com +151.101.89.16 f.media-amazon.com +2a04:4e42:15::272 f.media-amazon.com +3.168.147.92 imdb-video.media-imdb.com +18.173.117.77 dqpnq362acqdi.cloudfront.net +2600:9000:25f2:3800:5:ce70:a180:21 dqpnq362acqdi.cloudfront.net +209.85.232.195 translate.google.com +2a00:1450:4001:829::201a translate.google.com +209.85.232.195 translate.googleapis.com +2a00:1450:4001:829::201a translate.googleapis.com +209.85.232.195 translate-pa.googleapis.com +2a00:1450:4001:829::201a translate-pa.googleapis.com +18.65.168.9 plugins.jetbrains.com +2600:9000:24bb:5e00:12:7c44:15c0:93a1 plugins.jetbrains.com +18.65.168.9 download.jetbrains.com +2600:9000:24bb:5e00:12:7c44:15c0:93a1 download.jetbrains.com +18.65.168.9 cache-redirector.jetbrains.com +2600:9000:24bb:5e00:12:7c44:15c0:93a1 cache-redirector.jetbrains.com + +# Update time: 2024-11-14 03:55:51 +08:00 +# GitHubֿ: https://github.com/sinspired/cnNetTool +# cnNetTool End