chore: 项目文件结构重构

删除多个不再使用的源文件,包括动画、下载、配置、UI 相关文件及图标,清理代码库以提高可维护性。
This commit is contained in:
hyb-oyqq
2025-07-18 18:59:19 +08:00
parent 2e6f71d962
commit f202925333
49 changed files with 2876 additions and 684 deletions

979
result.csv Normal file
View File

@@ -0,0 +1,979 @@
IP 地址,已发送,已接收,丢包率,平均延迟,下载速度(MB/s),地区码
141.101.121.206,4,4,0.00,170.61,0.00,N/A
141.101.120.141,4,4,0.00,172.19,0.00,N/A
172.64.34.217,4,4,0.00,172.73,0.00,N/A
104.18.232.108,4,4,0.00,175.13,0.00,N/A
104.19.49.47,4,4,0.00,175.76,0.00,N/A
104.18.210.110,4,4,0.00,176.37,0.00,N/A
104.18.133.38,4,4,0.00,176.87,0.00,N/A
172.65.200.235,4,4,0.00,180.18,0.00,N/A
172.65.206.215,4,4,0.00,181.09,0.00,N/A
172.65.88.110,4,4,0.00,181.92,0.00,N/A
104.18.87.58,4,4,0.00,182.15,0.00,N/A
104.27.195.221,4,4,0.00,182.32,0.00,N/A
172.64.35.209,4,4,0.00,182.80,0.00,N/A
172.65.83.97,4,4,0.00,183.80,0.00,N/A
172.67.115.45,4,4,0.00,184.35,0.00,N/A
172.67.109.61,4,4,0.00,184.42,0.00,N/A
104.27.199.104,4,4,0.00,184.61,0.00,N/A
104.27.55.140,4,4,0.00,184.95,0.00,N/A
104.23.126.50,4,4,0.00,186.35,0.00,N/A
104.23.97.94,4,4,0.00,186.58,0.00,N/A
172.65.80.97,4,4,0.00,187.11,0.00,N/A
172.65.141.175,4,4,0.00,187.34,0.00,N/A
104.27.49.243,4,4,0.00,188.22,0.00,N/A
108.162.195.23,4,4,0.00,189.54,0.00,N/A
172.65.146.228,4,4,0.00,189.77,0.00,N/A
172.65.89.157,4,4,0.00,189.96,0.00,N/A
104.25.4.81,4,4,0.00,190.07,0.00,N/A
104.25.11.89,4,4,0.00,190.35,0.00,N/A
172.65.90.242,4,4,0.00,190.36,0.00,N/A
104.25.138.144,4,4,0.00,190.39,0.00,N/A
104.27.200.172,4,4,0.00,190.83,0.00,N/A
172.67.88.239,4,4,0.00,191.16,0.00,N/A
104.18.250.196,4,4,0.00,192.35,0.00,N/A
172.65.240.126,4,4,0.00,192.80,0.00,N/A
104.27.118.3,4,4,0.00,192.95,0.00,N/A
104.25.63.97,4,4,0.00,192.96,0.00,N/A
104.27.204.236,4,4,0.00,193.05,0.00,N/A
104.25.47.196,4,4,0.00,193.90,0.00,N/A
172.66.135.31,4,4,0.00,194.02,0.00,N/A
104.16.201.188,4,4,0.00,194.24,0.00,N/A
104.27.201.28,4,4,0.00,195.17,0.00,N/A
104.27.66.223,4,4,0.00,195.48,0.00,N/A
172.65.81.92,4,4,0.00,196.15,0.00,N/A
172.65.138.75,4,4,0.00,196.45,0.00,N/A
162.159.9.55,4,4,0.00,196.60,0.00,N/A
104.19.77.26,4,4,0.00,197.43,0.00,N/A
172.65.125.141,4,4,0.00,197.70,0.00,N/A
104.25.101.238,4,4,0.00,199.28,0.00,N/A
141.101.113.135,4,4,0.00,199.94,0.00,N/A
104.24.253.165,4,4,0.00,201.58,0.00,N/A
104.25.43.12,4,4,0.00,201.72,0.00,N/A
104.27.64.178,4,4,0.00,202.07,0.00,N/A
104.25.40.112,4,4,0.00,202.49,0.00,N/A
172.65.85.42,4,4,0.00,203.00,0.00,N/A
104.27.206.177,4,4,0.00,203.81,0.00,N/A
104.27.194.185,4,4,0.00,204.52,0.00,N/A
104.27.57.176,4,4,0.00,204.72,0.00,N/A
162.159.6.99,4,4,0.00,205.14,0.00,N/A
104.16.208.207,4,4,0.00,205.96,0.00,N/A
172.65.106.7,4,4,0.00,206.00,0.00,N/A
104.25.100.152,4,4,0.00,206.14,0.00,N/A
104.25.9.10,4,4,0.00,206.22,0.00,N/A
104.25.2.103,4,4,0.00,206.67,0.00,N/A
104.25.6.214,4,4,0.00,207.38,0.00,N/A
104.27.125.248,4,4,0.00,207.56,0.00,N/A
104.23.99.171,4,4,0.00,208.38,0.00,N/A
162.159.20.67,4,4,0.00,208.69,0.00,N/A
172.65.137.158,4,4,0.00,208.89,0.00,N/A
141.101.90.252,4,4,0.00,208.91,0.00,N/A
104.17.27.5,4,4,0.00,209.43,0.00,N/A
104.23.96.103,4,4,0.00,209.71,0.00,N/A
198.41.200.113,4,4,0.00,210.96,0.00,N/A
104.25.3.135,4,4,0.00,211.53,0.00,N/A
172.67.192.153,4,4,0.00,213.75,0.00,N/A
172.67.128.121,4,4,0.00,216.03,0.00,N/A
104.16.192.233,4,4,0.00,216.10,0.00,N/A
104.29.158.150,4,4,0.00,216.16,0.00,N/A
104.19.27.221,4,4,0.00,217.16,0.00,N/A
104.16.238.110,4,4,0.00,219.89,0.00,N/A
104.21.110.121,4,4,0.00,220.02,0.00,N/A
172.67.142.189,4,4,0.00,221.18,0.00,N/A
162.159.10.98,4,4,0.00,221.72,0.00,N/A
172.67.141.146,4,4,0.00,222.05,0.00,N/A
104.21.21.9,4,4,0.00,222.20,0.00,N/A
104.29.157.254,4,4,0.00,223.36,0.00,N/A
104.21.44.171,4,4,0.00,223.49,0.00,N/A
104.21.33.67,4,4,0.00,224.51,0.00,N/A
162.159.14.250,4,4,0.00,225.19,0.00,N/A
172.67.149.124,4,4,0.00,225.46,0.00,N/A
104.21.60.76,4,4,0.00,226.39,0.00,N/A
104.21.34.167,4,4,0.00,228.03,0.00,N/A
104.21.66.44,4,4,0.00,228.14,0.00,N/A
198.41.193.202,4,4,0.00,228.58,0.00,N/A
172.67.231.90,4,4,0.00,229.40,0.00,N/A
172.65.60.144,4,4,0.00,229.46,0.00,N/A
162.159.11.102,4,4,0.00,229.99,0.00,N/A
188.114.97.221,4,4,0.00,230.32,0.00,N/A
104.21.31.187,4,4,0.00,230.39,0.00,N/A
198.41.206.251,4,4,0.00,230.57,0.00,N/A
172.67.169.129,4,4,0.00,231.16,0.00,N/A
104.18.221.160,4,4,0.00,231.31,0.00,N/A
172.67.251.106,4,4,0.00,231.38,0.00,N/A
104.21.19.88,4,4,0.00,232.40,0.00,N/A
104.21.76.1,4,4,0.00,233.29,0.00,N/A
104.21.85.131,4,4,0.00,233.30,0.00,N/A
104.21.25.159,4,4,0.00,233.59,0.00,N/A
172.67.208.10,4,4,0.00,234.32,0.00,N/A
172.64.92.242,4,4,0.00,234.75,0.00,N/A
172.65.5.190,4,4,0.00,235.78,0.00,N/A
173.245.59.112,4,4,0.00,237.44,0.00,N/A
104.21.108.113,4,4,0.00,237.57,0.00,N/A
104.18.245.3,4,4,0.00,238.08,0.00,N/A
162.159.40.247,4,4,0.00,239.48,0.00,N/A
104.19.0.13,4,4,0.00,240.12,0.00,N/A
104.21.42.59,4,4,0.00,240.75,0.00,N/A
104.19.15.109,4,4,0.00,248.82,0.00,N/A
104.21.49.195,4,4,0.00,249.41,0.00,N/A
104.19.14.123,4,4,0.00,250.93,0.00,N/A
198.41.199.9,4,4,0.00,253.58,0.00,N/A
104.29.159.206,4,4,0.00,255.12,0.00,N/A
108.162.198.59,4,4,0.00,260.18,0.00,N/A
104.29.144.155,4,4,0.00,261.55,0.00,N/A
198.41.216.166,4,4,0.00,263.88,0.00,N/A
162.159.25.79,4,4,0.00,265.49,0.00,N/A
104.16.173.251,4,4,0.00,265.54,0.00,N/A
104.16.219.16,4,4,0.00,267.19,0.00,N/A
103.21.244.66,4,4,0.00,267.32,0.00,N/A
198.41.192.140,4,4,0.00,270.43,0.00,N/A
162.159.16.8,4,4,0.00,281.10,0.00,N/A
104.29.145.249,4,4,0.00,284.12,0.00,N/A
104.17.14.107,4,4,0.00,284.74,0.00,N/A
162.159.33.9,4,3,0.25,163.50,0.00,N/A
104.19.1.203,4,3,0.25,165.66,0.00,N/A
104.17.10.246,4,3,0.25,168.12,0.00,N/A
172.65.204.110,4,3,0.25,168.44,0.00,N/A
104.16.251.149,4,3,0.25,169.01,0.00,N/A
104.19.24.233,4,3,0.25,169.23,0.00,N/A
141.101.122.200,4,3,0.25,169.24,0.00,N/A
104.16.222.196,4,3,0.25,169.37,0.00,N/A
104.17.18.41,4,3,0.25,169.59,0.00,N/A
104.19.59.153,4,3,0.25,170.84,0.00,N/A
104.17.3.191,4,3,0.25,171.07,0.00,N/A
172.65.212.42,4,3,0.25,171.19,0.00,N/A
172.65.194.74,4,3,0.25,171.33,0.00,N/A
104.16.194.52,4,3,0.25,171.42,0.00,N/A
198.41.203.196,4,3,0.25,173.66,0.00,N/A
104.19.40.143,4,3,0.25,173.79,0.00,N/A
104.16.224.56,4,3,0.25,174.55,0.00,N/A
104.16.171.114,4,3,0.25,174.63,0.00,N/A
104.17.16.183,4,3,0.25,174.76,0.00,N/A
141.101.115.123,4,3,0.25,175.35,0.00,N/A
198.41.217.76,4,3,0.25,175.82,0.00,N/A
141.101.123.213,4,3,0.25,176.23,0.00,N/A
198.41.201.13,4,3,0.25,176.44,0.00,N/A
104.19.136.126,4,3,0.25,176.96,0.00,N/A
104.16.193.195,4,3,0.25,177.00,0.00,N/A
104.21.224.85,4,3,0.25,177.85,0.00,N/A
172.65.238.127,4,3,0.25,177.96,0.00,N/A
104.18.206.194,4,3,0.25,178.00,0.00,N/A
172.65.233.45,4,3,0.25,178.90,0.00,N/A
104.19.50.237,4,3,0.25,179.04,0.00,N/A
104.19.61.11,4,3,0.25,179.38,0.00,N/A
172.65.216.254,4,3,0.25,179.63,0.00,N/A
173.245.58.254,4,3,0.25,179.74,0.00,N/A
198.41.222.188,4,3,0.25,179.95,0.00,N/A
173.245.49.150,4,3,0.25,180.27,0.00,N/A
104.16.195.175,4,3,0.25,180.62,0.00,N/A
104.19.57.218,4,3,0.25,182.05,0.00,N/A
190.93.247.8,4,3,0.25,182.22,0.00,N/A
104.16.191.64,4,3,0.25,183.41,0.00,N/A
172.64.42.188,4,3,0.25,183.54,0.00,N/A
104.25.158.236,4,3,0.25,183.65,0.00,N/A
162.159.23.138,4,3,0.25,183.81,0.00,N/A
172.64.159.95,4,3,0.25,184.51,0.00,N/A
104.27.115.83,4,3,0.25,185.01,0.00,N/A
104.27.93.122,4,3,0.25,185.08,0.00,N/A
162.159.27.175,4,3,0.25,185.26,0.00,N/A
104.27.90.5,4,3,0.25,185.65,0.00,N/A
104.25.134.158,4,3,0.25,185.71,0.00,N/A
172.65.197.229,4,3,0.25,185.75,0.00,N/A
104.25.104.84,4,3,0.25,185.96,0.00,N/A
104.27.192.21,4,3,0.25,186.04,0.00,N/A
104.16.216.68,4,3,0.25,186.26,0.00,N/A
198.41.207.148,4,3,0.25,186.59,0.00,N/A
104.27.198.29,4,3,0.25,186.64,0.00,N/A
104.27.117.34,4,3,0.25,186.68,0.00,N/A
172.65.148.94,4,3,0.25,186.96,0.00,N/A
104.25.121.4,4,3,0.25,187.30,0.00,N/A
104.25.49.216,4,3,0.25,187.42,0.00,N/A
104.25.84.187,4,3,0.25,187.48,0.00,N/A
104.27.116.178,4,3,0.25,188.87,0.00,N/A
104.16.215.44,4,3,0.25,188.89,0.00,N/A
104.18.234.235,4,3,0.25,188.90,0.00,N/A
104.25.108.235,4,3,0.25,188.91,0.00,N/A
104.27.197.249,4,3,0.25,189.45,0.00,N/A
104.27.122.29,4,3,0.25,189.92,0.00,N/A
104.16.189.44,4,3,0.25,190.13,0.00,N/A
104.19.58.126,4,3,0.25,190.13,0.00,N/A
104.18.238.3,4,3,0.25,190.26,0.00,N/A
172.65.128.134,4,3,0.25,190.66,0.00,N/A
104.27.51.248,4,3,0.25,190.79,0.00,N/A
104.16.178.48,4,3,0.25,191.00,0.00,N/A
172.65.203.83,4,3,0.25,191.08,0.00,N/A
104.25.112.128,4,3,0.25,191.28,0.00,N/A
104.25.83.3,4,3,0.25,191.51,0.00,N/A
172.64.82.38,4,3,0.25,191.55,0.00,N/A
104.27.193.89,4,3,0.25,191.69,0.00,N/A
104.25.5.141,4,3,0.25,192.19,0.00,N/A
162.159.4.237,4,3,0.25,192.21,0.00,N/A
104.25.45.112,4,3,0.25,192.49,0.00,N/A
104.16.184.4,4,3,0.25,192.59,0.00,N/A
172.65.103.133,4,3,0.25,193.07,0.00,N/A
104.25.136.115,4,3,0.25,193.45,0.00,N/A
104.25.135.203,4,3,0.25,193.77,0.00,N/A
104.27.62.142,4,3,0.25,194.12,0.00,N/A
104.27.120.219,4,3,0.25,194.18,0.00,N/A
104.19.55.100,4,3,0.25,194.19,0.00,N/A
104.20.62.129,4,3,0.25,194.26,0.00,N/A
104.24.249.90,4,3,0.25,194.30,0.00,N/A
104.25.24.7,4,3,0.25,194.43,0.00,N/A
104.25.198.124,4,3,0.25,194.78,0.00,N/A
104.16.170.74,4,3,0.25,195.30,0.00,N/A
104.25.139.149,4,3,0.25,195.68,0.00,N/A
104.27.47.127,4,3,0.25,196.12,0.00,N/A
104.25.80.93,4,3,0.25,196.20,0.00,N/A
104.25.25.78,4,3,0.25,196.23,0.00,N/A
104.20.24.111,4,3,0.25,196.53,0.00,N/A
104.27.96.63,4,3,0.25,196.87,0.00,N/A
104.25.64.161,4,3,0.25,197.45,0.00,N/A
172.65.108.167,4,3,0.25,198.03,0.00,N/A
172.65.198.92,4,3,0.25,198.90,0.00,N/A
104.27.203.151,4,3,0.25,199.26,0.00,N/A
172.65.207.20,4,3,0.25,199.30,0.00,N/A
172.65.220.142,4,3,0.25,199.39,0.00,N/A
104.25.131.218,4,3,0.25,199.47,0.00,N/A
172.65.182.19,4,3,0.25,200.05,0.00,N/A
104.19.42.3,4,3,0.25,200.06,0.00,N/A
104.25.22.250,4,3,0.25,200.09,0.00,N/A
104.25.155.139,4,3,0.25,200.19,0.00,N/A
104.25.7.77,4,3,0.25,200.55,0.00,N/A
104.18.240.134,4,3,0.25,200.85,0.00,N/A
104.23.138.193,4,3,0.25,201.23,0.00,N/A
104.16.255.166,4,3,0.25,201.57,0.00,N/A
172.65.171.208,4,3,0.25,201.79,0.00,N/A
104.19.51.187,4,3,0.25,201.87,0.00,N/A
104.25.67.102,4,3,0.25,201.96,0.00,N/A
172.64.37.142,4,3,0.25,202.65,0.00,N/A
104.25.15.172,4,3,0.25,202.72,0.00,N/A
104.16.183.157,4,3,0.25,202.80,0.00,N/A
104.25.16.44,4,3,0.25,202.92,0.00,N/A
104.27.119.20,4,3,0.25,203.03,0.00,N/A
104.27.202.72,4,3,0.25,203.32,0.00,N/A
104.27.205.247,4,3,0.25,204.24,0.00,N/A
104.27.44.147,4,3,0.25,204.95,0.00,N/A
172.65.162.180,4,3,0.25,205.18,0.00,N/A
104.25.27.102,4,3,0.25,205.50,0.00,N/A
104.25.20.71,4,3,0.25,205.55,0.00,N/A
104.25.118.22,4,3,0.25,205.63,0.00,N/A
104.27.196.211,4,3,0.25,205.69,0.00,N/A
162.159.2.235,4,3,0.25,205.82,0.00,N/A
104.24.242.122,4,3,0.25,205.83,0.00,N/A
172.65.169.247,4,3,0.25,205.93,0.00,N/A
104.25.137.129,4,3,0.25,206.03,0.00,N/A
108.162.192.52,4,3,0.25,206.74,0.00,N/A
172.65.87.122,4,3,0.25,206.76,0.00,N/A
172.65.102.200,4,3,0.25,206.82,0.00,N/A
104.25.77.56,4,3,0.25,206.91,0.00,N/A
104.25.160.115,4,3,0.25,207.14,0.00,N/A
104.16.226.129,4,3,0.25,207.18,0.00,N/A
104.25.115.147,4,3,0.25,207.34,0.00,N/A
172.65.84.239,4,3,0.25,207.39,0.00,N/A
104.25.154.250,4,3,0.25,207.40,0.00,N/A
104.16.190.135,4,3,0.25,207.60,0.00,N/A
172.65.101.45,4,3,0.25,207.72,0.00,N/A
104.27.48.85,4,3,0.25,207.91,0.00,N/A
104.25.152.164,4,3,0.25,208.62,0.00,N/A
172.65.166.48,4,3,0.25,208.71,0.00,N/A
104.25.146.130,4,3,0.25,208.94,0.00,N/A
104.25.28.221,4,3,0.25,209.34,0.00,N/A
172.65.237.6,4,3,0.25,209.59,0.00,N/A
104.24.252.194,4,3,0.25,209.71,0.00,N/A
104.25.46.121,4,3,0.25,210.02,0.00,N/A
104.25.169.246,4,3,0.25,210.61,0.00,N/A
104.25.161.66,4,3,0.25,210.67,0.00,N/A
104.27.207.21,4,3,0.25,210.90,0.00,N/A
104.16.198.181,4,3,0.25,210.97,0.00,N/A
104.23.100.117,4,3,0.25,211.25,0.00,N/A
162.159.1.252,4,3,0.25,211.26,0.00,N/A
172.67.23.7,4,3,0.25,211.28,0.00,N/A
162.159.8.88,4,3,0.25,211.35,0.00,N/A
172.65.135.249,4,3,0.25,211.63,0.00,N/A
172.65.165.6,4,3,0.25,211.69,0.00,N/A
104.19.64.35,4,3,0.25,212.13,0.00,N/A
104.19.46.41,4,3,0.25,212.23,0.00,N/A
104.25.120.222,4,3,0.25,212.38,0.00,N/A
104.21.69.157,4,3,0.25,212.45,0.00,N/A
190.93.244.83,4,3,0.25,212.70,0.00,N/A
104.19.178.229,4,3,0.25,212.79,0.00,N/A
104.25.122.149,4,3,0.25,213.01,0.00,N/A
104.25.17.46,4,3,0.25,213.05,0.00,N/A
104.17.8.199,4,3,0.25,213.24,0.00,N/A
104.25.109.0,4,3,0.25,213.25,0.00,N/A
104.16.207.56,4,3,0.25,213.36,0.00,N/A
172.67.130.27,4,3,0.25,214.47,0.00,N/A
172.64.38.0,4,3,0.25,215.75,0.00,N/A
198.41.198.1,4,3,0.25,216.23,0.00,N/A
104.18.205.192,4,3,0.25,216.30,0.00,N/A
104.18.239.237,4,3,0.25,216.80,0.00,N/A
172.65.163.87,4,3,0.25,216.98,0.00,N/A
104.19.39.46,4,3,0.25,217.28,0.00,N/A
104.21.46.108,4,3,0.25,217.74,0.00,N/A
104.19.8.219,4,3,0.25,217.91,0.00,N/A
162.159.13.95,4,3,0.25,218.47,0.00,N/A
172.65.232.188,4,3,0.25,218.78,0.00,N/A
104.17.22.192,4,3,0.25,219.23,0.00,N/A
172.67.252.113,4,3,0.25,219.49,0.00,N/A
104.19.56.193,4,3,0.25,220.02,0.00,N/A
104.21.26.80,4,3,0.25,220.23,0.00,N/A
104.21.24.120,4,3,0.25,221.44,0.00,N/A
104.19.33.84,4,3,0.25,221.66,0.00,N/A
172.67.143.82,4,3,0.25,222.08,0.00,N/A
172.67.254.234,4,3,0.25,222.35,0.00,N/A
104.21.112.16,4,3,0.25,223.11,0.00,N/A
104.19.21.218,4,3,0.25,223.37,0.00,N/A
104.19.73.167,4,3,0.25,223.90,0.00,N/A
104.21.23.135,4,3,0.25,224.35,0.00,N/A
104.16.234.1,4,3,0.25,224.35,0.00,N/A
104.19.48.44,4,3,0.25,224.48,0.00,N/A
172.67.207.103,4,3,0.25,224.66,0.00,N/A
172.67.221.88,4,3,0.25,224.72,0.00,N/A
104.21.88.25,4,3,0.25,225.10,0.00,N/A
104.16.218.194,4,3,0.25,225.40,0.00,N/A
104.17.134.211,4,3,0.25,225.62,0.00,N/A
172.67.159.242,4,3,0.25,225.83,0.00,N/A
104.17.21.1,4,3,0.25,227.12,0.00,N/A
104.18.209.181,4,3,0.25,227.46,0.00,N/A
172.67.244.246,4,3,0.25,227.66,0.00,N/A
172.67.253.56,4,3,0.25,228.01,0.00,N/A
172.67.247.4,4,3,0.25,228.89,0.00,N/A
104.16.169.161,4,3,0.25,228.97,0.00,N/A
172.67.218.10,4,3,0.25,229.20,0.00,N/A
104.21.70.108,4,3,0.25,229.34,0.00,N/A
172.67.223.187,4,3,0.25,229.65,0.00,N/A
172.65.213.174,4,3,0.25,230.05,0.00,N/A
104.21.43.192,4,3,0.25,230.10,0.00,N/A
104.21.14.60,4,3,0.25,230.30,0.00,N/A
172.67.241.43,4,3,0.25,230.34,0.00,N/A
104.17.33.86,4,3,0.25,231.27,0.00,N/A
172.67.140.236,4,3,0.25,231.73,0.00,N/A
104.19.36.71,4,3,0.25,232.24,0.00,N/A
104.18.204.121,4,3,0.25,232.55,0.00,N/A
172.67.199.61,4,3,0.25,232.65,0.00,N/A
104.21.9.13,4,3,0.25,233.36,0.00,N/A
104.19.160.32,4,3,0.25,234.72,0.00,N/A
104.21.58.175,4,3,0.25,234.77,0.00,N/A
172.67.220.236,4,3,0.25,234.79,0.00,N/A
162.159.7.161,4,3,0.25,235.58,0.00,N/A
104.19.84.157,4,3,0.25,235.61,0.00,N/A
104.16.211.208,4,3,0.25,235.86,0.00,N/A
104.18.243.77,4,3,0.25,237.12,0.00,N/A
172.67.217.240,4,3,0.25,238.03,0.00,N/A
172.67.219.51,4,3,0.25,238.64,0.00,N/A
104.19.75.223,4,3,0.25,239.77,0.00,N/A
104.21.39.161,4,3,0.25,239.78,0.00,N/A
172.67.250.234,4,3,0.25,239.85,0.00,N/A
104.21.125.50,4,3,0.25,240.77,0.00,N/A
104.17.40.37,4,3,0.25,241.11,0.00,N/A
104.21.61.36,4,3,0.25,241.76,0.00,N/A
172.67.246.106,4,3,0.25,242.09,0.00,N/A
172.67.255.212,4,3,0.25,243.02,0.00,N/A
104.16.228.223,4,3,0.25,243.10,0.00,N/A
104.21.63.40,4,3,0.25,243.37,0.00,N/A
104.19.79.26,4,3,0.25,243.66,0.00,N/A
104.21.90.89,4,3,0.25,245.12,0.00,N/A
104.21.91.252,4,3,0.25,248.01,0.00,N/A
104.19.99.114,4,3,0.25,248.71,0.00,N/A
104.18.220.135,4,3,0.25,248.95,0.00,N/A
104.17.156.56,4,3,0.25,249.01,0.00,N/A
104.16.156.57,4,3,0.25,249.55,0.00,N/A
104.19.13.228,4,3,0.25,250.32,0.00,N/A
104.16.223.248,4,3,0.25,250.33,0.00,N/A
104.21.84.7,4,3,0.25,253.21,0.00,N/A
104.21.32.10,4,3,0.25,254.79,0.00,N/A
104.19.52.148,4,3,0.25,255.58,0.00,N/A
188.114.99.114,4,3,0.25,256.25,0.00,N/A
162.159.26.85,4,3,0.25,257.47,0.00,N/A
104.17.24.239,4,3,0.25,259.24,0.00,N/A
104.17.46.40,4,3,0.25,259.94,0.00,N/A
104.16.237.232,4,3,0.25,263.49,0.00,N/A
104.16.172.243,4,3,0.25,266.25,0.00,N/A
104.16.244.83,4,3,0.25,269.50,0.00,N/A
104.16.248.22,4,3,0.25,270.58,0.00,N/A
104.19.9.231,4,3,0.25,274.11,0.00,N/A
104.17.26.25,4,3,0.25,277.95,0.00,N/A
104.18.143.96,4,3,0.25,280.35,0.00,N/A
104.16.254.154,4,3,0.25,290.76,0.00,N/A
104.16.128.24,4,3,0.25,295.17,0.00,N/A
104.16.217.215,4,3,0.25,296.90,0.00,N/A
104.16.243.152,4,3,0.25,298.05,0.00,N/A
104.19.74.232,4,3,0.25,301.24,0.00,N/A
104.16.186.132,4,3,0.25,301.86,0.00,N/A
104.19.22.68,4,3,0.25,305.49,0.00,N/A
104.17.25.230,4,3,0.25,305.60,0.00,N/A
162.159.12.250,4,3,0.25,333.08,0.00,N/A
104.16.232.127,4,2,0.50,165.77,0.00,N/A
172.65.211.195,4,2,0.50,166.03,0.00,N/A
104.16.210.63,4,2,0.50,166.28,0.00,N/A
104.18.202.245,4,2,0.50,167.10,0.00,N/A
104.19.32.244,4,2,0.50,168.25,0.00,N/A
104.19.47.130,4,2,0.50,168.65,0.00,N/A
104.19.28.39,4,2,0.50,168.73,0.00,N/A
104.19.62.128,4,2,0.50,169.06,0.00,N/A
104.18.236.37,4,2,0.50,169.51,0.00,N/A
104.16.213.127,4,2,0.50,169.63,0.00,N/A
104.19.5.114,4,2,0.50,169.63,0.00,N/A
172.65.214.236,4,2,0.50,169.80,0.00,N/A
104.19.34.192,4,2,0.50,170.53,0.00,N/A
172.65.217.13,4,2,0.50,170.61,0.00,N/A
104.18.255.70,4,2,0.50,171.30,0.00,N/A
104.18.228.127,4,2,0.50,171.34,0.00,N/A
104.16.196.113,4,2,0.50,171.73,0.00,N/A
104.18.200.77,4,2,0.50,171.82,0.00,N/A
104.19.29.149,4,2,0.50,172.26,0.00,N/A
104.18.246.66,4,2,0.50,172.34,0.00,N/A
104.19.53.109,4,2,0.50,172.43,0.00,N/A
104.17.9.181,4,2,0.50,172.74,0.00,N/A
172.65.208.209,4,2,0.50,173.03,0.00,N/A
104.18.219.64,4,2,0.50,173.10,0.00,N/A
104.19.6.132,4,2,0.50,173.37,0.00,N/A
104.19.66.0,4,2,0.50,173.63,0.00,N/A
172.65.193.184,4,2,0.50,173.81,0.00,N/A
104.19.25.178,4,2,0.50,174.11,0.00,N/A
104.16.230.87,4,2,0.50,174.42,0.00,N/A
104.16.225.15,4,2,0.50,174.97,0.00,N/A
104.17.6.82,4,2,0.50,175.80,0.00,N/A
104.16.168.243,4,2,0.50,176.35,0.00,N/A
162.159.46.227,4,2,0.50,176.41,0.00,N/A
104.18.247.48,4,2,0.50,176.50,0.00,N/A
104.19.19.210,4,2,0.50,177.17,0.00,N/A
172.65.215.191,4,2,0.50,177.38,0.00,N/A
172.65.228.103,4,2,0.50,177.92,0.00,N/A
104.19.54.104,4,2,0.50,178.14,0.00,N/A
172.65.209.80,4,2,0.50,178.23,0.00,N/A
172.64.149.174,4,2,0.50,178.96,0.00,N/A
108.162.194.75,4,2,0.50,179.04,0.00,N/A
104.27.113.13,4,2,0.50,179.52,0.00,N/A
104.18.198.55,4,2,0.50,180.07,0.00,N/A
104.19.60.74,4,2,0.50,180.28,0.00,N/A
104.17.67.158,4,2,0.50,180.45,0.00,N/A
104.19.241.60,4,2,0.50,180.59,0.00,N/A
172.65.131.127,4,2,0.50,180.84,0.00,N/A
104.16.181.177,4,2,0.50,181.33,0.00,N/A
172.67.111.160,4,2,0.50,181.90,0.00,N/A
104.27.89.177,4,2,0.50,182.25,0.00,N/A
104.25.1.205,4,2,0.50,182.37,0.00,N/A
104.27.121.46,4,2,0.50,182.55,0.00,N/A
104.25.156.2,4,2,0.50,182.89,0.00,N/A
172.65.242.204,4,2,0.50,183.62,0.00,N/A
172.65.161.140,4,2,0.50,183.64,0.00,N/A
104.27.94.162,4,2,0.50,184.37,0.00,N/A
104.25.145.241,4,2,0.50,184.61,0.00,N/A
104.25.167.30,4,2,0.50,185.29,0.00,N/A
104.25.61.52,4,2,0.50,185.51,0.00,N/A
104.16.236.16,4,2,0.50,186.96,0.00,N/A
172.65.210.45,4,2,0.50,186.99,0.00,N/A
104.16.204.13,4,2,0.50,187.11,0.00,N/A
172.64.146.66,4,2,0.50,187.42,0.00,N/A
172.65.134.158,4,2,0.50,187.58,0.00,N/A
172.65.226.202,4,2,0.50,187.86,0.00,N/A
104.25.103.134,4,2,0.50,189.11,0.00,N/A
172.65.190.155,4,2,0.50,189.11,0.00,N/A
104.27.73.8,4,2,0.50,189.20,0.00,N/A
172.65.92.58,4,2,0.50,189.23,0.00,N/A
104.25.125.82,4,2,0.50,189.24,0.00,N/A
104.27.124.170,4,2,0.50,189.25,0.00,N/A
104.27.114.18,4,2,0.50,189.58,0.00,N/A
104.25.204.1,4,2,0.50,189.60,0.00,N/A
104.25.147.35,4,2,0.50,189.62,0.00,N/A
104.25.30.42,4,2,0.50,189.70,0.00,N/A
104.27.53.231,4,2,0.50,189.94,0.00,N/A
104.23.112.114,4,2,0.50,190.05,0.00,N/A
104.25.150.43,4,2,0.50,190.41,0.00,N/A
104.17.28.28,4,2,0.50,190.48,0.00,N/A
104.25.163.122,4,2,0.50,190.63,0.00,N/A
104.25.92.129,4,2,0.50,190.84,0.00,N/A
104.16.174.195,4,2,0.50,191.23,0.00,N/A
172.65.180.91,4,2,0.50,191.24,0.00,N/A
172.65.191.61,4,2,0.50,191.38,0.00,N/A
104.25.129.229,4,2,0.50,191.48,0.00,N/A
172.65.140.27,4,2,0.50,191.66,0.00,N/A
104.27.59.198,4,2,0.50,191.79,0.00,N/A
104.27.86.147,4,2,0.50,191.95,0.00,N/A
104.27.123.120,4,2,0.50,192.21,0.00,N/A
162.159.51.244,4,2,0.50,192.76,0.00,N/A
104.16.197.186,4,2,0.50,193.16,0.00,N/A
104.25.79.107,4,2,0.50,193.32,0.00,N/A
172.65.130.170,4,2,0.50,194.25,0.00,N/A
104.27.72.254,4,2,0.50,194.69,0.00,N/A
172.65.189.137,4,2,0.50,195.25,0.00,N/A
104.25.114.125,4,2,0.50,195.28,0.00,N/A
104.25.153.60,4,2,0.50,195.49,0.00,N/A
104.27.95.5,4,2,0.50,196.06,0.00,N/A
172.65.82.218,4,2,0.50,196.10,0.00,N/A
104.16.202.120,4,2,0.50,196.33,0.00,N/A
104.27.54.21,4,2,0.50,196.33,0.00,N/A
172.65.127.66,4,2,0.50,196.40,0.00,N/A
172.66.145.171,4,2,0.50,196.63,0.00,N/A
104.27.112.128,4,2,0.50,196.68,0.00,N/A
104.25.107.63,4,2,0.50,197.10,0.00,N/A
104.23.115.58,4,2,0.50,197.14,0.00,N/A
172.67.147.131,4,2,0.50,197.27,0.00,N/A
104.27.65.58,4,2,0.50,197.46,0.00,N/A
104.25.38.160,4,2,0.50,197.66,0.00,N/A
104.16.235.17,4,2,0.50,197.72,0.00,N/A
104.25.166.221,4,2,0.50,197.78,0.00,N/A
104.25.39.109,4,2,0.50,197.83,0.00,N/A
104.25.21.254,4,2,0.50,198.45,0.00,N/A
172.65.172.134,4,2,0.50,198.48,0.00,N/A
104.25.102.240,4,2,0.50,198.58,0.00,N/A
104.25.202.146,4,2,0.50,198.61,0.00,N/A
104.25.128.235,4,2,0.50,198.76,0.00,N/A
104.25.19.188,4,2,0.50,199.01,0.00,N/A
104.25.91.90,4,2,0.50,199.07,0.00,N/A
104.16.206.246,4,2,0.50,199.23,0.00,N/A
172.65.168.169,4,2,0.50,199.43,0.00,N/A
172.65.139.219,4,2,0.50,200.38,0.00,N/A
104.27.127.17,4,2,0.50,200.40,0.00,N/A
172.67.114.185,4,2,0.50,201.63,0.00,N/A
104.24.245.37,4,2,0.50,201.73,0.00,N/A
104.18.233.82,4,2,0.50,201.99,0.00,N/A
104.23.134.70,4,2,0.50,202.04,0.00,N/A
104.27.45.21,4,2,0.50,202.21,0.00,N/A
104.25.76.224,4,2,0.50,202.43,0.00,N/A
104.25.69.222,4,2,0.50,202.52,0.00,N/A
172.65.244.105,4,2,0.50,202.69,0.00,N/A
172.66.196.154,4,2,0.50,202.74,0.00,N/A
172.65.167.167,4,2,0.50,202.94,0.00,N/A
104.25.117.55,4,2,0.50,203.07,0.00,N/A
104.25.132.250,4,2,0.50,203.24,0.00,N/A
104.19.23.120,4,2,0.50,203.25,0.00,N/A
172.67.193.52,4,2,0.50,203.42,0.00,N/A
104.16.231.173,4,2,0.50,203.75,0.00,N/A
104.25.111.179,4,2,0.50,203.81,0.00,N/A
172.65.145.26,4,2,0.50,203.95,0.00,N/A
172.67.122.116,4,2,0.50,204.07,0.00,N/A
104.27.87.198,4,2,0.50,204.25,0.00,N/A
104.16.250.187,4,2,0.50,204.32,0.00,N/A
172.65.192.175,4,2,0.50,204.45,0.00,N/A
172.65.178.57,4,2,0.50,204.62,0.00,N/A
104.27.43.169,4,2,0.50,204.75,0.00,N/A
172.67.112.161,4,2,0.50,204.87,0.00,N/A
162.159.42.97,4,2,0.50,205.12,0.00,N/A
172.67.238.4,4,2,0.50,205.90,0.00,N/A
172.67.243.192,4,2,0.50,205.92,0.00,N/A
104.23.98.14,4,2,0.50,205.99,0.00,N/A
104.27.71.131,4,2,0.50,206.35,0.00,N/A
104.25.182.99,4,2,0.50,206.55,0.00,N/A
104.17.15.149,4,2,0.50,206.97,0.00,N/A
104.25.89.4,4,2,0.50,207.06,0.00,N/A
104.27.88.10,4,2,0.50,207.06,0.00,N/A
104.25.78.37,4,2,0.50,208.03,0.00,N/A
104.23.101.18,4,2,0.50,208.19,0.00,N/A
104.25.162.191,4,2,0.50,208.30,0.00,N/A
104.16.209.136,4,2,0.50,208.41,0.00,N/A
104.27.46.19,4,2,0.50,209.08,0.00,N/A
104.25.148.28,4,2,0.50,209.50,0.00,N/A
104.25.72.41,4,2,0.50,209.60,0.00,N/A
104.27.69.216,4,2,0.50,209.81,0.00,N/A
172.65.124.65,4,2,0.50,209.92,0.00,N/A
104.25.123.30,4,2,0.50,210.09,0.00,N/A
104.25.143.37,4,2,0.50,210.44,0.00,N/A
104.27.74.218,4,2,0.50,210.49,0.00,N/A
104.25.71.180,4,2,0.50,211.95,0.00,N/A
104.25.110.235,4,2,0.50,212.26,0.00,N/A
172.67.230.47,4,2,0.50,212.26,0.00,N/A
104.25.106.176,4,2,0.50,212.27,0.00,N/A
104.25.119.239,4,2,0.50,212.62,0.00,N/A
104.25.18.74,4,2,0.50,213.15,0.00,N/A
104.24.254.26,4,2,0.50,213.23,0.00,N/A
172.67.226.119,4,2,0.50,213.24,0.00,N/A
172.67.179.228,4,2,0.50,213.75,0.00,N/A
104.25.159.108,4,2,0.50,214.22,0.00,N/A
172.67.117.58,4,2,0.50,214.58,0.00,N/A
104.27.56.251,4,2,0.50,214.91,0.00,N/A
172.67.224.75,4,2,0.50,215.10,0.00,N/A
172.65.129.88,4,2,0.50,215.35,0.00,N/A
172.67.249.143,4,2,0.50,215.54,0.00,N/A
172.67.43.168,4,2,0.50,216.97,0.00,N/A
172.67.227.148,4,2,0.50,217.04,0.00,N/A
172.67.36.28,4,2,0.50,218.39,0.00,N/A
104.18.254.179,4,2,0.50,218.56,0.00,N/A
172.67.144.231,4,2,0.50,219.65,0.00,N/A
104.21.100.208,4,2,0.50,219.97,0.00,N/A
104.25.70.253,4,2,0.50,220.19,0.00,N/A
104.21.94.224,4,2,0.50,220.34,0.00,N/A
104.21.89.3,4,2,0.50,220.83,0.00,N/A
172.67.229.25,4,2,0.50,222.55,0.00,N/A
104.21.68.236,4,2,0.50,222.66,0.00,N/A
172.67.233.144,4,2,0.50,222.73,0.00,N/A
104.21.62.90,4,2,0.50,223.24,0.00,N/A
172.67.245.123,4,2,0.50,223.47,0.00,N/A
104.21.36.9,4,2,0.50,223.48,0.00,N/A
198.41.214.181,4,2,0.50,223.61,0.00,N/A
172.67.196.9,4,2,0.50,223.67,0.00,N/A
172.67.171.87,4,2,0.50,224.33,0.00,N/A
104.21.73.62,4,2,0.50,224.38,0.00,N/A
104.18.214.143,4,2,0.50,225.16,0.00,N/A
104.19.16.151,4,2,0.50,225.38,0.00,N/A
172.67.133.180,4,2,0.50,225.94,0.00,N/A
104.21.45.36,4,2,0.50,226.22,0.00,N/A
104.17.11.9,4,2,0.50,227.71,0.00,N/A
172.67.150.56,4,2,0.50,227.74,0.00,N/A
104.21.22.214,4,2,0.50,227.81,0.00,N/A
104.21.79.9,4,2,0.50,228.13,0.00,N/A
104.17.12.216,4,2,0.50,228.19,0.00,N/A
104.21.51.135,4,2,0.50,228.24,0.00,N/A
104.21.35.76,4,2,0.50,228.26,0.00,N/A
104.21.47.241,4,2,0.50,228.45,0.00,N/A
172.67.236.31,4,2,0.50,228.80,0.00,N/A
172.67.148.83,4,2,0.50,229.02,0.00,N/A
104.16.227.253,4,2,0.50,229.10,0.00,N/A
172.67.239.243,4,2,0.50,229.11,0.00,N/A
172.67.184.224,4,2,0.50,229.43,0.00,N/A
172.67.200.95,4,2,0.50,230.74,0.00,N/A
104.21.117.130,4,2,0.50,231.99,0.00,N/A
104.21.38.25,4,2,0.50,232.07,0.00,N/A
172.67.235.165,4,2,0.50,232.51,0.00,N/A
172.67.191.89,4,2,0.50,232.92,0.00,N/A
104.21.78.31,4,2,0.50,233.48,0.00,N/A
172.64.235.13,4,2,0.50,233.52,0.00,N/A
104.17.5.111,4,2,0.50,234.38,0.00,N/A
172.67.163.174,4,2,0.50,234.60,0.00,N/A
172.65.23.28,4,2,0.50,235.39,0.00,N/A
104.21.77.247,4,2,0.50,235.64,0.00,N/A
104.21.95.112,4,2,0.50,235.92,0.00,N/A
172.67.234.190,4,2,0.50,236.33,0.00,N/A
104.21.80.214,4,2,0.50,236.43,0.00,N/A
172.67.187.174,4,2,0.50,236.78,0.00,N/A
104.21.72.115,4,2,0.50,236.98,0.00,N/A
104.16.249.247,4,2,0.50,237.12,0.00,N/A
172.67.222.130,4,2,0.50,237.44,0.00,N/A
172.67.232.134,4,2,0.50,237.45,0.00,N/A
172.67.186.23,4,2,0.50,237.52,0.00,N/A
172.67.146.102,4,2,0.50,237.54,0.00,N/A
104.21.71.65,4,2,0.50,238.21,0.00,N/A
172.67.240.219,4,2,0.50,238.31,0.00,N/A
104.19.31.159,4,2,0.50,238.99,0.00,N/A
172.67.209.179,4,2,0.50,239.14,0.00,N/A
104.18.203.126,4,2,0.50,240.80,0.00,N/A
172.67.225.16,4,2,0.50,241.65,0.00,N/A
104.19.20.215,4,2,0.50,241.67,0.00,N/A
104.18.199.178,4,2,0.50,241.70,0.00,N/A
104.21.93.155,4,2,0.50,242.14,0.00,N/A
104.21.105.247,4,2,0.50,242.39,0.00,N/A
172.67.237.203,4,2,0.50,243.20,0.00,N/A
104.21.67.19,4,2,0.50,244.70,0.00,N/A
172.67.129.156,4,2,0.50,245.99,0.00,N/A
172.67.242.188,4,2,0.50,246.07,0.00,N/A
172.67.201.160,4,2,0.50,246.09,0.00,N/A
172.67.228.250,4,2,0.50,246.47,0.00,N/A
172.67.195.214,4,2,0.50,247.36,0.00,N/A
104.17.23.35,4,2,0.50,250.13,0.00,N/A
104.16.252.241,4,2,0.50,251.28,0.00,N/A
104.18.249.186,4,2,0.50,252.02,0.00,N/A
172.67.248.187,4,2,0.50,252.07,0.00,N/A
172.67.131.46,4,2,0.50,252.28,0.00,N/A
104.17.0.9,4,2,0.50,252.95,0.00,N/A
172.67.204.103,4,2,0.50,254.53,0.00,N/A
104.16.179.117,4,2,0.50,254.91,0.00,N/A
172.65.12.220,4,2,0.50,256.55,0.00,N/A
172.67.176.156,4,2,0.50,256.81,0.00,N/A
172.65.231.8,4,2,0.50,258.67,0.00,N/A
104.16.203.254,4,2,0.50,262.04,0.00,N/A
104.19.41.46,4,2,0.50,265.39,0.00,N/A
104.16.221.213,4,2,0.50,268.02,0.00,N/A
104.19.97.228,4,2,0.50,269.77,0.00,N/A
104.19.38.43,4,2,0.50,275.62,0.00,N/A
104.16.205.95,4,2,0.50,282.59,0.00,N/A
162.159.0.137,4,2,0.50,283.38,0.00,N/A
162.159.32.3,4,2,0.50,291.78,0.00,N/A
162.159.36.48,4,2,0.50,296.85,0.00,N/A
104.16.182.109,4,2,0.50,308.08,0.00,N/A
104.17.1.92,4,2,0.50,315.93,0.00,N/A
104.19.72.245,4,2,0.50,319.16,0.00,N/A
104.16.229.45,4,2,0.50,321.22,0.00,N/A
190.93.245.152,4,2,0.50,336.90,0.00,N/A
104.17.7.145,4,2,0.50,349.57,0.00,N/A
104.18.213.14,4,1,0.75,163.67,0.00,N/A
104.19.30.199,4,1,0.75,164.04,0.00,N/A
104.18.252.219,4,1,0.75,164.51,0.00,N/A
104.18.237.110,4,1,0.75,165.68,0.00,N/A
104.16.188.172,4,1,0.75,166.77,0.00,N/A
104.18.217.241,4,1,0.75,167.06,0.00,N/A
172.65.246.132,4,1,0.75,168.13,0.00,N/A
104.19.65.194,4,1,0.75,168.13,0.00,N/A
104.16.214.194,4,1,0.75,168.14,0.00,N/A
104.16.199.146,4,1,0.75,169.13,0.00,N/A
104.16.187.85,4,1,0.75,169.21,0.00,N/A
104.18.201.59,4,1,0.75,169.94,0.00,N/A
104.16.200.21,4,1,0.75,170.40,0.00,N/A
104.19.7.172,4,1,0.75,171.16,0.00,N/A
104.16.233.87,4,1,0.75,171.19,0.00,N/A
104.18.241.152,4,1,0.75,171.42,0.00,N/A
104.18.212.123,4,1,0.75,171.42,0.00,N/A
104.16.239.52,4,1,0.75,171.47,0.00,N/A
104.18.244.113,4,1,0.75,171.79,0.00,N/A
172.65.227.240,4,1,0.75,171.86,0.00,N/A
172.65.251.201,4,1,0.75,172.89,0.00,N/A
104.17.75.226,4,1,0.75,173.25,0.00,N/A
104.18.223.101,4,1,0.75,173.25,0.00,N/A
104.18.229.11,4,1,0.75,173.40,0.00,N/A
104.19.37.75,4,1,0.75,173.70,0.00,N/A
104.16.177.246,4,1,0.75,174.08,0.00,N/A
104.18.218.96,4,1,0.75,174.15,0.00,N/A
104.16.240.184,4,1,0.75,174.20,0.00,N/A
104.16.241.21,4,1,0.75,174.25,0.00,N/A
104.21.237.45,4,1,0.75,176.08,0.00,N/A
104.17.30.19,4,1,0.75,176.25,0.00,N/A
104.18.225.76,4,1,0.75,176.46,0.00,N/A
172.65.229.96,4,1,0.75,176.57,0.00,N/A
104.19.26.118,4,1,0.75,176.57,0.00,N/A
104.17.45.234,4,1,0.75,176.78,0.00,N/A
172.65.219.91,4,1,0.75,176.96,0.00,N/A
198.41.223.198,4,1,0.75,177.06,0.00,N/A
172.65.222.206,4,1,0.75,178.64,0.00,N/A
190.93.246.193,4,1,0.75,179.66,0.00,N/A
104.25.75.19,4,1,0.75,179.71,0.00,N/A
104.18.226.224,4,1,0.75,180.63,0.00,N/A
108.162.193.248,4,1,0.75,181.13,0.00,N/A
104.25.31.119,4,1,0.75,181.94,0.00,N/A
172.67.110.213,4,1,0.75,182.15,0.00,N/A
162.159.41.63,4,1,0.75,182.80,0.00,N/A
104.25.8.235,4,1,0.75,182.84,0.00,N/A
104.25.105.177,4,1,0.75,182.97,0.00,N/A
104.25.29.162,4,1,0.75,183.02,0.00,N/A
172.67.118.15,4,1,0.75,183.07,0.00,N/A
172.65.174.192,4,1,0.75,183.14,0.00,N/A
104.25.50.20,4,1,0.75,183.59,0.00,N/A
104.21.239.56,4,1,0.75,183.60,0.00,N/A
172.65.159.113,4,1,0.75,183.75,0.00,N/A
104.25.41.41,4,1,0.75,183.86,0.00,N/A
104.25.168.83,4,1,0.75,184.24,0.00,N/A
104.23.123.173,4,1,0.75,184.85,0.00,N/A
104.23.116.70,4,1,0.75,184.85,0.00,N/A
104.25.170.252,4,1,0.75,184.94,0.00,N/A
104.27.42.251,4,1,0.75,185.19,0.00,N/A
172.65.234.41,4,1,0.75,185.35,0.00,N/A
104.16.7.148,4,1,0.75,185.90,0.00,N/A
162.159.128.78,4,1,0.75,185.91,0.00,N/A
104.25.85.217,4,1,0.75,186.13,0.00,N/A
104.25.23.29,4,1,0.75,186.50,0.00,N/A
104.23.141.211,4,1,0.75,186.78,0.00,N/A
104.27.76.127,4,1,0.75,187.87,0.00,N/A
104.16.157.22,4,1,0.75,188.21,0.00,N/A
104.25.178.192,4,1,0.75,189.04,0.00,N/A
104.17.241.221,4,1,0.75,190.77,0.00,N/A
104.19.96.75,4,1,0.75,190.80,0.00,N/A
162.159.245.6,4,1,0.75,191.48,0.00,N/A
104.23.129.59,4,1,0.75,192.33,0.00,N/A
162.159.243.54,4,1,0.75,192.36,0.00,N/A
104.27.92.55,4,1,0.75,192.62,0.00,N/A
104.25.65.123,4,1,0.75,192.95,0.00,N/A
104.25.157.69,4,1,0.75,193.41,0.00,N/A
172.65.158.237,4,1,0.75,194.02,0.00,N/A
104.19.2.132,4,1,0.75,194.04,0.00,N/A
104.23.114.211,4,1,0.75,194.68,0.00,N/A
104.27.52.65,4,1,0.75,194.82,0.00,N/A
104.25.116.110,4,1,0.75,194.86,0.00,N/A
172.65.179.128,4,1,0.75,194.91,0.00,N/A
104.25.124.114,4,1,0.75,195.15,0.00,N/A
104.25.0.192,4,1,0.75,195.22,0.00,N/A
104.25.86.81,4,1,0.75,195.43,0.00,N/A
104.25.144.144,4,1,0.75,195.68,0.00,N/A
104.25.10.30,4,1,0.75,195.76,0.00,N/A
172.65.173.33,4,1,0.75,195.82,0.00,N/A
104.25.173.172,4,1,0.75,196.16,0.00,N/A
104.25.73.32,4,1,0.75,196.44,0.00,N/A
104.27.75.93,4,1,0.75,196.77,0.00,N/A
104.27.91.117,4,1,0.75,197.12,0.00,N/A
104.25.140.229,4,1,0.75,197.32,0.00,N/A
172.65.185.64,4,1,0.75,197.33,0.00,N/A
104.25.66.201,4,1,0.75,197.39,0.00,N/A
172.65.142.109,4,1,0.75,197.51,0.00,N/A
172.65.175.0,4,1,0.75,197.54,0.00,N/A
172.65.164.223,4,1,0.75,197.54,0.00,N/A
104.16.102.58,4,1,0.75,197.81,0.00,N/A
172.65.133.234,4,1,0.75,197.83,0.00,N/A
104.25.133.24,4,1,0.75,197.83,0.00,N/A
104.25.81.166,4,1,0.75,197.91,0.00,N/A
172.67.134.70,4,1,0.75,198.12,0.00,N/A
104.25.127.129,4,1,0.75,198.41,0.00,N/A
104.27.68.180,4,1,0.75,198.52,0.00,N/A
104.25.113.193,4,1,0.75,198.67,0.00,N/A
104.27.61.7,4,1,0.75,198.77,0.00,N/A
172.65.156.55,4,1,0.75,198.83,0.00,N/A
172.65.132.228,4,1,0.75,199.10,0.00,N/A
104.23.106.123,4,1,0.75,199.24,0.00,N/A
172.67.172.253,4,1,0.75,199.41,0.00,N/A
172.67.113.106,4,1,0.75,199.41,0.00,N/A
104.18.151.71,4,1,0.75,199.45,0.00,N/A
104.25.44.230,4,1,0.75,199.53,0.00,N/A
172.65.183.239,4,1,0.75,199.54,0.00,N/A
104.21.16.108,4,1,0.75,200.13,0.00,N/A
172.65.188.8,4,1,0.75,200.15,0.00,N/A
172.65.160.83,4,1,0.75,200.30,0.00,N/A
104.27.78.3,4,1,0.75,200.46,0.00,N/A
104.27.80.42,4,1,0.75,200.69,0.00,N/A
104.27.82.19,4,1,0.75,200.93,0.00,N/A
104.18.150.215,4,1,0.75,201.21,0.00,N/A
104.25.82.130,4,1,0.75,201.26,0.00,N/A
172.67.132.213,4,1,0.75,201.42,0.00,N/A
104.25.165.110,4,1,0.75,203.09,0.00,N/A
172.67.116.7,4,1,0.75,204.09,0.00,N/A
172.67.79.153,4,1,0.75,205.89,0.00,N/A
104.25.177.29,4,1,0.75,206.47,0.00,N/A
104.23.127.84,4,1,0.75,206.79,0.00,N/A
172.65.157.26,4,1,0.75,206.81,0.00,N/A
172.65.143.100,4,1,0.75,207.04,0.00,N/A
172.67.213.100,4,1,0.75,207.71,0.00,N/A
172.67.120.9,4,1,0.75,207.72,0.00,N/A
172.67.153.129,4,1,0.75,208.63,0.00,N/A
172.67.125.200,4,1,0.75,208.65,0.00,N/A
104.25.149.106,4,1,0.75,208.83,0.00,N/A
162.159.34.226,4,1,0.75,209.01,0.00,N/A
104.21.104.184,4,1,0.75,209.12,0.00,N/A
172.67.212.137,4,1,0.75,209.67,0.00,N/A
172.67.123.136,4,1,0.75,209.89,0.00,N/A
104.21.0.77,4,1,0.75,210.27,0.00,N/A
172.65.170.217,4,1,0.75,210.29,0.00,N/A
104.27.35.43,4,1,0.75,210.34,0.00,N/A
104.27.79.195,4,1,0.75,210.51,0.00,N/A
104.27.63.69,4,1,0.75,210.53,0.00,N/A
104.25.26.185,4,1,0.75,210.61,0.00,N/A
104.25.42.149,4,1,0.75,210.70,0.00,N/A
198.41.196.53,4,1,0.75,210.92,0.00,N/A
172.65.202.128,4,1,0.75,211.10,0.00,N/A
104.17.17.118,4,1,0.75,211.16,0.00,N/A
104.25.126.214,4,1,0.75,211.18,0.00,N/A
172.65.123.189,4,1,0.75,211.24,0.00,N/A
104.27.60.48,4,1,0.75,211.46,0.00,N/A
172.65.186.1,4,1,0.75,211.52,0.00,N/A
172.65.86.79,4,1,0.75,211.57,0.00,N/A
104.21.10.53,4,1,0.75,211.76,0.00,N/A
172.65.184.152,4,1,0.75,211.78,0.00,N/A
104.27.70.234,4,1,0.75,211.98,0.00,N/A
172.67.168.42,4,1,0.75,212.00,0.00,N/A
104.21.99.173,4,1,0.75,212.05,0.00,N/A
172.65.181.172,4,1,0.75,212.07,0.00,N/A
172.67.203.54,4,1,0.75,212.08,0.00,N/A
104.27.50.143,4,1,0.75,212.10,0.00,N/A
104.21.53.192,4,1,0.75,212.10,0.00,N/A
172.67.206.8,4,1,0.75,212.26,0.00,N/A
104.27.77.89,4,1,0.75,212.28,0.00,N/A
104.25.151.104,4,1,0.75,212.32,0.00,N/A
104.21.12.16,4,1,0.75,212.68,0.00,N/A
104.21.17.104,4,1,0.75,212.70,0.00,N/A
172.65.187.74,4,1,0.75,212.74,0.00,N/A
104.25.33.231,4,1,0.75,212.80,0.00,N/A
104.25.141.117,4,1,0.75,213.00,0.00,N/A
104.27.67.150,4,1,0.75,213.13,0.00,N/A
172.65.144.178,4,1,0.75,213.20,0.00,N/A
104.21.52.226,4,1,0.75,213.68,0.00,N/A
172.65.177.1,4,1,0.75,213.78,0.00,N/A
104.27.58.213,4,1,0.75,214.07,0.00,N/A
172.67.178.33,4,1,0.75,214.39,0.00,N/A
172.67.127.189,4,1,0.75,214.40,0.00,N/A
172.67.167.87,4,1,0.75,214.48,0.00,N/A
104.21.96.216,4,1,0.75,214.66,0.00,N/A
104.21.103.195,4,1,0.75,214.83,0.00,N/A
104.23.122.11,4,1,0.75,214.85,0.00,N/A
104.21.87.122,4,1,0.75,214.92,0.00,N/A
172.67.185.245,4,1,0.75,214.92,0.00,N/A
172.65.91.153,4,1,0.75,215.02,0.00,N/A
104.27.97.215,4,1,0.75,215.05,0.00,N/A
104.25.142.8,4,1,0.75,215.25,0.00,N/A
104.23.132.217,4,1,0.75,215.33,0.00,N/A
172.67.145.128,4,1,0.75,215.37,0.00,N/A
172.67.210.240,4,1,0.75,215.43,0.00,N/A
172.67.182.141,4,1,0.75,215.57,0.00,N/A
172.67.170.100,4,1,0.75,215.85,0.00,N/A
172.67.215.56,4,1,0.75,216.24,0.00,N/A
172.65.176.224,4,1,0.75,216.76,0.00,N/A
172.67.151.13,4,1,0.75,217.05,0.00,N/A
104.21.198.22,4,1,0.75,217.27,0.00,N/A
104.21.50.44,4,1,0.75,217.36,0.00,N/A
104.25.32.177,4,1,0.75,217.38,0.00,N/A
104.19.35.200,4,1,0.75,217.44,0.00,N/A
104.21.115.123,4,1,0.75,217.72,0.00,N/A
104.21.20.237,4,1,0.75,218.20,0.00,N/A
172.67.173.238,4,1,0.75,218.40,0.00,N/A
104.25.231.57,4,1,0.75,218.45,0.00,N/A
104.21.106.152,4,1,0.75,218.58,0.00,N/A
104.27.126.205,4,1,0.75,218.88,0.00,N/A
172.67.124.177,4,1,0.75,219.90,0.00,N/A
172.66.164.0,4,1,0.75,220.22,0.00,N/A
104.21.121.201,4,1,0.75,221.67,0.00,N/A
104.19.45.187,4,1,0.75,221.99,0.00,N/A
104.21.6.226,4,1,0.75,222.78,0.00,N/A
172.67.188.195,4,1,0.75,224.44,0.00,N/A
104.21.59.151,4,1,0.75,224.92,0.00,N/A
104.21.111.210,4,1,0.75,225.97,0.00,N/A
172.65.221.120,4,1,0.75,226.62,0.00,N/A
172.65.93.123,4,1,0.75,226.94,0.00,N/A
104.21.82.137,4,1,0.75,227.02,0.00,N/A
104.21.75.155,4,1,0.75,227.19,0.00,N/A
104.21.4.103,4,1,0.75,227.65,0.00,N/A
172.65.218.115,4,1,0.75,228.33,0.00,N/A
172.67.183.190,4,1,0.75,228.38,0.00,N/A
172.65.225.135,4,1,0.75,228.57,0.00,N/A
104.21.18.142,4,1,0.75,228.62,0.00,N/A
172.67.216.164,4,1,0.75,229.22,0.00,N/A
104.21.41.229,4,1,0.75,229.28,0.00,N/A
104.21.86.97,4,1,0.75,229.30,0.00,N/A
104.21.54.37,4,1,0.75,229.37,0.00,N/A
172.67.190.171,4,1,0.75,229.48,0.00,N/A
172.67.194.139,4,1,0.75,229.59,0.00,N/A
104.21.40.93,4,1,0.75,229.68,0.00,N/A
172.67.180.110,4,1,0.75,229.94,0.00,N/A
172.67.166.204,4,1,0.75,230.28,0.00,N/A
104.21.74.128,4,1,0.75,230.60,0.00,N/A
172.67.214.107,4,1,0.75,230.77,0.00,N/A
172.67.175.89,4,1,0.75,231.01,0.00,N/A
104.16.175.228,4,1,0.75,231.75,0.00,N/A
104.21.48.8,4,1,0.75,232.07,0.00,N/A
104.21.37.104,4,1,0.75,232.16,0.00,N/A
104.21.8.192,4,1,0.75,233.78,0.00,N/A
172.67.202.238,4,1,0.75,235.09,0.00,N/A
172.67.205.247,4,1,0.75,235.42,0.00,N/A
172.65.223.201,4,1,0.75,236.56,0.00,N/A
104.21.193.204,4,1,0.75,236.93,0.00,N/A
104.21.102.58,4,1,0.75,236.95,0.00,N/A
104.18.231.140,4,1,0.75,238.04,0.00,N/A
172.67.154.197,4,1,0.75,240.34,0.00,N/A
104.16.180.108,4,1,0.75,242.44,0.00,N/A
104.21.116.58,4,1,0.75,243.03,0.00,N/A
172.67.136.221,4,1,0.75,243.68,0.00,N/A
104.21.13.66,4,1,0.75,244.41,0.00,N/A
104.21.92.240,4,1,0.75,244.88,0.00,N/A
172.67.157.171,4,1,0.75,245.27,0.00,N/A
104.21.101.247,4,1,0.75,246.05,0.00,N/A
172.67.181.79,4,1,0.75,246.05,0.00,N/A
104.21.118.139,4,1,0.75,246.36,0.00,N/A
172.67.177.56,4,1,0.75,246.65,0.00,N/A
104.21.127.178,4,1,0.75,246.81,0.00,N/A
104.21.98.191,4,1,0.75,247.32,0.00,N/A
104.21.113.193,4,1,0.75,247.69,0.00,N/A
172.67.189.86,4,1,0.75,248.51,0.00,N/A
104.21.2.249,4,1,0.75,248.68,0.00,N/A
104.21.222.98,4,1,0.75,248.68,0.00,N/A
104.18.242.144,4,1,0.75,250.21,0.00,N/A
104.21.126.207,4,1,0.75,251.52,0.00,N/A
104.16.242.230,4,1,0.75,253.13,0.00,N/A
172.67.211.106,4,1,0.75,253.30,0.00,N/A
104.21.114.153,4,1,0.75,256.73,0.00,N/A
104.21.97.15,4,1,0.75,258.68,0.00,N/A
172.67.174.227,4,1,0.75,259.79,0.00,N/A
172.67.198.73,4,1,0.75,259.83,0.00,N/A
104.19.3.200,4,1,0.75,260.44,0.00,N/A
104.19.63.44,4,1,0.75,262.87,0.00,N/A
172.65.32.207,4,1,0.75,263.57,0.00,N/A
104.18.216.81,4,1,0.75,264.24,0.00,N/A
104.21.1.68,4,1,0.75,265.42,0.00,N/A
104.21.81.31,4,1,0.75,265.49,0.00,N/A
104.18.251.232,4,1,0.75,266.91,0.00,N/A
172.67.197.65,4,1,0.75,267.28,0.00,N/A
104.18.156.241,4,1,0.75,269.81,0.00,N/A
172.65.24.244,4,1,0.75,271.18,0.00,N/A
104.16.253.250,4,1,0.75,271.66,0.00,N/A
104.17.4.252,4,1,0.75,273.42,0.00,N/A
104.18.215.115,4,1,0.75,278.16,0.00,N/A
104.16.212.149,4,1,0.75,287.06,0.00,N/A
104.17.13.0,4,1,0.75,290.13,0.00,N/A
141.101.114.149,4,1,0.75,303.88,0.00,N/A
104.18.230.149,4,1,0.75,333.73,0.00,N/A
104.18.248.2,4,1,0.75,346.67,0.00,N/A
104.18.71.193,4,1,0.75,358.68,0.00,N/A
104.17.2.17,4,1,0.75,386.55,0.00,N/A
104.17.216.244,4,1,0.75,398.34,0.00,N/A
1 IP 地址 已发送 已接收 丢包率 平均延迟 下载速度(MB/s) 地区码
2 141.101.121.206 4 4 0.00 170.61 0.00 N/A
3 141.101.120.141 4 4 0.00 172.19 0.00 N/A
4 172.64.34.217 4 4 0.00 172.73 0.00 N/A
5 104.18.232.108 4 4 0.00 175.13 0.00 N/A
6 104.19.49.47 4 4 0.00 175.76 0.00 N/A
7 104.18.210.110 4 4 0.00 176.37 0.00 N/A
8 104.18.133.38 4 4 0.00 176.87 0.00 N/A
9 172.65.200.235 4 4 0.00 180.18 0.00 N/A
10 172.65.206.215 4 4 0.00 181.09 0.00 N/A
11 172.65.88.110 4 4 0.00 181.92 0.00 N/A
12 104.18.87.58 4 4 0.00 182.15 0.00 N/A
13 104.27.195.221 4 4 0.00 182.32 0.00 N/A
14 172.64.35.209 4 4 0.00 182.80 0.00 N/A
15 172.65.83.97 4 4 0.00 183.80 0.00 N/A
16 172.67.115.45 4 4 0.00 184.35 0.00 N/A
17 172.67.109.61 4 4 0.00 184.42 0.00 N/A
18 104.27.199.104 4 4 0.00 184.61 0.00 N/A
19 104.27.55.140 4 4 0.00 184.95 0.00 N/A
20 104.23.126.50 4 4 0.00 186.35 0.00 N/A
21 104.23.97.94 4 4 0.00 186.58 0.00 N/A
22 172.65.80.97 4 4 0.00 187.11 0.00 N/A
23 172.65.141.175 4 4 0.00 187.34 0.00 N/A
24 104.27.49.243 4 4 0.00 188.22 0.00 N/A
25 108.162.195.23 4 4 0.00 189.54 0.00 N/A
26 172.65.146.228 4 4 0.00 189.77 0.00 N/A
27 172.65.89.157 4 4 0.00 189.96 0.00 N/A
28 104.25.4.81 4 4 0.00 190.07 0.00 N/A
29 104.25.11.89 4 4 0.00 190.35 0.00 N/A
30 172.65.90.242 4 4 0.00 190.36 0.00 N/A
31 104.25.138.144 4 4 0.00 190.39 0.00 N/A
32 104.27.200.172 4 4 0.00 190.83 0.00 N/A
33 172.67.88.239 4 4 0.00 191.16 0.00 N/A
34 104.18.250.196 4 4 0.00 192.35 0.00 N/A
35 172.65.240.126 4 4 0.00 192.80 0.00 N/A
36 104.27.118.3 4 4 0.00 192.95 0.00 N/A
37 104.25.63.97 4 4 0.00 192.96 0.00 N/A
38 104.27.204.236 4 4 0.00 193.05 0.00 N/A
39 104.25.47.196 4 4 0.00 193.90 0.00 N/A
40 172.66.135.31 4 4 0.00 194.02 0.00 N/A
41 104.16.201.188 4 4 0.00 194.24 0.00 N/A
42 104.27.201.28 4 4 0.00 195.17 0.00 N/A
43 104.27.66.223 4 4 0.00 195.48 0.00 N/A
44 172.65.81.92 4 4 0.00 196.15 0.00 N/A
45 172.65.138.75 4 4 0.00 196.45 0.00 N/A
46 162.159.9.55 4 4 0.00 196.60 0.00 N/A
47 104.19.77.26 4 4 0.00 197.43 0.00 N/A
48 172.65.125.141 4 4 0.00 197.70 0.00 N/A
49 104.25.101.238 4 4 0.00 199.28 0.00 N/A
50 141.101.113.135 4 4 0.00 199.94 0.00 N/A
51 104.24.253.165 4 4 0.00 201.58 0.00 N/A
52 104.25.43.12 4 4 0.00 201.72 0.00 N/A
53 104.27.64.178 4 4 0.00 202.07 0.00 N/A
54 104.25.40.112 4 4 0.00 202.49 0.00 N/A
55 172.65.85.42 4 4 0.00 203.00 0.00 N/A
56 104.27.206.177 4 4 0.00 203.81 0.00 N/A
57 104.27.194.185 4 4 0.00 204.52 0.00 N/A
58 104.27.57.176 4 4 0.00 204.72 0.00 N/A
59 162.159.6.99 4 4 0.00 205.14 0.00 N/A
60 104.16.208.207 4 4 0.00 205.96 0.00 N/A
61 172.65.106.7 4 4 0.00 206.00 0.00 N/A
62 104.25.100.152 4 4 0.00 206.14 0.00 N/A
63 104.25.9.10 4 4 0.00 206.22 0.00 N/A
64 104.25.2.103 4 4 0.00 206.67 0.00 N/A
65 104.25.6.214 4 4 0.00 207.38 0.00 N/A
66 104.27.125.248 4 4 0.00 207.56 0.00 N/A
67 104.23.99.171 4 4 0.00 208.38 0.00 N/A
68 162.159.20.67 4 4 0.00 208.69 0.00 N/A
69 172.65.137.158 4 4 0.00 208.89 0.00 N/A
70 141.101.90.252 4 4 0.00 208.91 0.00 N/A
71 104.17.27.5 4 4 0.00 209.43 0.00 N/A
72 104.23.96.103 4 4 0.00 209.71 0.00 N/A
73 198.41.200.113 4 4 0.00 210.96 0.00 N/A
74 104.25.3.135 4 4 0.00 211.53 0.00 N/A
75 172.67.192.153 4 4 0.00 213.75 0.00 N/A
76 172.67.128.121 4 4 0.00 216.03 0.00 N/A
77 104.16.192.233 4 4 0.00 216.10 0.00 N/A
78 104.29.158.150 4 4 0.00 216.16 0.00 N/A
79 104.19.27.221 4 4 0.00 217.16 0.00 N/A
80 104.16.238.110 4 4 0.00 219.89 0.00 N/A
81 104.21.110.121 4 4 0.00 220.02 0.00 N/A
82 172.67.142.189 4 4 0.00 221.18 0.00 N/A
83 162.159.10.98 4 4 0.00 221.72 0.00 N/A
84 172.67.141.146 4 4 0.00 222.05 0.00 N/A
85 104.21.21.9 4 4 0.00 222.20 0.00 N/A
86 104.29.157.254 4 4 0.00 223.36 0.00 N/A
87 104.21.44.171 4 4 0.00 223.49 0.00 N/A
88 104.21.33.67 4 4 0.00 224.51 0.00 N/A
89 162.159.14.250 4 4 0.00 225.19 0.00 N/A
90 172.67.149.124 4 4 0.00 225.46 0.00 N/A
91 104.21.60.76 4 4 0.00 226.39 0.00 N/A
92 104.21.34.167 4 4 0.00 228.03 0.00 N/A
93 104.21.66.44 4 4 0.00 228.14 0.00 N/A
94 198.41.193.202 4 4 0.00 228.58 0.00 N/A
95 172.67.231.90 4 4 0.00 229.40 0.00 N/A
96 172.65.60.144 4 4 0.00 229.46 0.00 N/A
97 162.159.11.102 4 4 0.00 229.99 0.00 N/A
98 188.114.97.221 4 4 0.00 230.32 0.00 N/A
99 104.21.31.187 4 4 0.00 230.39 0.00 N/A
100 198.41.206.251 4 4 0.00 230.57 0.00 N/A
101 172.67.169.129 4 4 0.00 231.16 0.00 N/A
102 104.18.221.160 4 4 0.00 231.31 0.00 N/A
103 172.67.251.106 4 4 0.00 231.38 0.00 N/A
104 104.21.19.88 4 4 0.00 232.40 0.00 N/A
105 104.21.76.1 4 4 0.00 233.29 0.00 N/A
106 104.21.85.131 4 4 0.00 233.30 0.00 N/A
107 104.21.25.159 4 4 0.00 233.59 0.00 N/A
108 172.67.208.10 4 4 0.00 234.32 0.00 N/A
109 172.64.92.242 4 4 0.00 234.75 0.00 N/A
110 172.65.5.190 4 4 0.00 235.78 0.00 N/A
111 173.245.59.112 4 4 0.00 237.44 0.00 N/A
112 104.21.108.113 4 4 0.00 237.57 0.00 N/A
113 104.18.245.3 4 4 0.00 238.08 0.00 N/A
114 162.159.40.247 4 4 0.00 239.48 0.00 N/A
115 104.19.0.13 4 4 0.00 240.12 0.00 N/A
116 104.21.42.59 4 4 0.00 240.75 0.00 N/A
117 104.19.15.109 4 4 0.00 248.82 0.00 N/A
118 104.21.49.195 4 4 0.00 249.41 0.00 N/A
119 104.19.14.123 4 4 0.00 250.93 0.00 N/A
120 198.41.199.9 4 4 0.00 253.58 0.00 N/A
121 104.29.159.206 4 4 0.00 255.12 0.00 N/A
122 108.162.198.59 4 4 0.00 260.18 0.00 N/A
123 104.29.144.155 4 4 0.00 261.55 0.00 N/A
124 198.41.216.166 4 4 0.00 263.88 0.00 N/A
125 162.159.25.79 4 4 0.00 265.49 0.00 N/A
126 104.16.173.251 4 4 0.00 265.54 0.00 N/A
127 104.16.219.16 4 4 0.00 267.19 0.00 N/A
128 103.21.244.66 4 4 0.00 267.32 0.00 N/A
129 198.41.192.140 4 4 0.00 270.43 0.00 N/A
130 162.159.16.8 4 4 0.00 281.10 0.00 N/A
131 104.29.145.249 4 4 0.00 284.12 0.00 N/A
132 104.17.14.107 4 4 0.00 284.74 0.00 N/A
133 162.159.33.9 4 3 0.25 163.50 0.00 N/A
134 104.19.1.203 4 3 0.25 165.66 0.00 N/A
135 104.17.10.246 4 3 0.25 168.12 0.00 N/A
136 172.65.204.110 4 3 0.25 168.44 0.00 N/A
137 104.16.251.149 4 3 0.25 169.01 0.00 N/A
138 104.19.24.233 4 3 0.25 169.23 0.00 N/A
139 141.101.122.200 4 3 0.25 169.24 0.00 N/A
140 104.16.222.196 4 3 0.25 169.37 0.00 N/A
141 104.17.18.41 4 3 0.25 169.59 0.00 N/A
142 104.19.59.153 4 3 0.25 170.84 0.00 N/A
143 104.17.3.191 4 3 0.25 171.07 0.00 N/A
144 172.65.212.42 4 3 0.25 171.19 0.00 N/A
145 172.65.194.74 4 3 0.25 171.33 0.00 N/A
146 104.16.194.52 4 3 0.25 171.42 0.00 N/A
147 198.41.203.196 4 3 0.25 173.66 0.00 N/A
148 104.19.40.143 4 3 0.25 173.79 0.00 N/A
149 104.16.224.56 4 3 0.25 174.55 0.00 N/A
150 104.16.171.114 4 3 0.25 174.63 0.00 N/A
151 104.17.16.183 4 3 0.25 174.76 0.00 N/A
152 141.101.115.123 4 3 0.25 175.35 0.00 N/A
153 198.41.217.76 4 3 0.25 175.82 0.00 N/A
154 141.101.123.213 4 3 0.25 176.23 0.00 N/A
155 198.41.201.13 4 3 0.25 176.44 0.00 N/A
156 104.19.136.126 4 3 0.25 176.96 0.00 N/A
157 104.16.193.195 4 3 0.25 177.00 0.00 N/A
158 104.21.224.85 4 3 0.25 177.85 0.00 N/A
159 172.65.238.127 4 3 0.25 177.96 0.00 N/A
160 104.18.206.194 4 3 0.25 178.00 0.00 N/A
161 172.65.233.45 4 3 0.25 178.90 0.00 N/A
162 104.19.50.237 4 3 0.25 179.04 0.00 N/A
163 104.19.61.11 4 3 0.25 179.38 0.00 N/A
164 172.65.216.254 4 3 0.25 179.63 0.00 N/A
165 173.245.58.254 4 3 0.25 179.74 0.00 N/A
166 198.41.222.188 4 3 0.25 179.95 0.00 N/A
167 173.245.49.150 4 3 0.25 180.27 0.00 N/A
168 104.16.195.175 4 3 0.25 180.62 0.00 N/A
169 104.19.57.218 4 3 0.25 182.05 0.00 N/A
170 190.93.247.8 4 3 0.25 182.22 0.00 N/A
171 104.16.191.64 4 3 0.25 183.41 0.00 N/A
172 172.64.42.188 4 3 0.25 183.54 0.00 N/A
173 104.25.158.236 4 3 0.25 183.65 0.00 N/A
174 162.159.23.138 4 3 0.25 183.81 0.00 N/A
175 172.64.159.95 4 3 0.25 184.51 0.00 N/A
176 104.27.115.83 4 3 0.25 185.01 0.00 N/A
177 104.27.93.122 4 3 0.25 185.08 0.00 N/A
178 162.159.27.175 4 3 0.25 185.26 0.00 N/A
179 104.27.90.5 4 3 0.25 185.65 0.00 N/A
180 104.25.134.158 4 3 0.25 185.71 0.00 N/A
181 172.65.197.229 4 3 0.25 185.75 0.00 N/A
182 104.25.104.84 4 3 0.25 185.96 0.00 N/A
183 104.27.192.21 4 3 0.25 186.04 0.00 N/A
184 104.16.216.68 4 3 0.25 186.26 0.00 N/A
185 198.41.207.148 4 3 0.25 186.59 0.00 N/A
186 104.27.198.29 4 3 0.25 186.64 0.00 N/A
187 104.27.117.34 4 3 0.25 186.68 0.00 N/A
188 172.65.148.94 4 3 0.25 186.96 0.00 N/A
189 104.25.121.4 4 3 0.25 187.30 0.00 N/A
190 104.25.49.216 4 3 0.25 187.42 0.00 N/A
191 104.25.84.187 4 3 0.25 187.48 0.00 N/A
192 104.27.116.178 4 3 0.25 188.87 0.00 N/A
193 104.16.215.44 4 3 0.25 188.89 0.00 N/A
194 104.18.234.235 4 3 0.25 188.90 0.00 N/A
195 104.25.108.235 4 3 0.25 188.91 0.00 N/A
196 104.27.197.249 4 3 0.25 189.45 0.00 N/A
197 104.27.122.29 4 3 0.25 189.92 0.00 N/A
198 104.16.189.44 4 3 0.25 190.13 0.00 N/A
199 104.19.58.126 4 3 0.25 190.13 0.00 N/A
200 104.18.238.3 4 3 0.25 190.26 0.00 N/A
201 172.65.128.134 4 3 0.25 190.66 0.00 N/A
202 104.27.51.248 4 3 0.25 190.79 0.00 N/A
203 104.16.178.48 4 3 0.25 191.00 0.00 N/A
204 172.65.203.83 4 3 0.25 191.08 0.00 N/A
205 104.25.112.128 4 3 0.25 191.28 0.00 N/A
206 104.25.83.3 4 3 0.25 191.51 0.00 N/A
207 172.64.82.38 4 3 0.25 191.55 0.00 N/A
208 104.27.193.89 4 3 0.25 191.69 0.00 N/A
209 104.25.5.141 4 3 0.25 192.19 0.00 N/A
210 162.159.4.237 4 3 0.25 192.21 0.00 N/A
211 104.25.45.112 4 3 0.25 192.49 0.00 N/A
212 104.16.184.4 4 3 0.25 192.59 0.00 N/A
213 172.65.103.133 4 3 0.25 193.07 0.00 N/A
214 104.25.136.115 4 3 0.25 193.45 0.00 N/A
215 104.25.135.203 4 3 0.25 193.77 0.00 N/A
216 104.27.62.142 4 3 0.25 194.12 0.00 N/A
217 104.27.120.219 4 3 0.25 194.18 0.00 N/A
218 104.19.55.100 4 3 0.25 194.19 0.00 N/A
219 104.20.62.129 4 3 0.25 194.26 0.00 N/A
220 104.24.249.90 4 3 0.25 194.30 0.00 N/A
221 104.25.24.7 4 3 0.25 194.43 0.00 N/A
222 104.25.198.124 4 3 0.25 194.78 0.00 N/A
223 104.16.170.74 4 3 0.25 195.30 0.00 N/A
224 104.25.139.149 4 3 0.25 195.68 0.00 N/A
225 104.27.47.127 4 3 0.25 196.12 0.00 N/A
226 104.25.80.93 4 3 0.25 196.20 0.00 N/A
227 104.25.25.78 4 3 0.25 196.23 0.00 N/A
228 104.20.24.111 4 3 0.25 196.53 0.00 N/A
229 104.27.96.63 4 3 0.25 196.87 0.00 N/A
230 104.25.64.161 4 3 0.25 197.45 0.00 N/A
231 172.65.108.167 4 3 0.25 198.03 0.00 N/A
232 172.65.198.92 4 3 0.25 198.90 0.00 N/A
233 104.27.203.151 4 3 0.25 199.26 0.00 N/A
234 172.65.207.20 4 3 0.25 199.30 0.00 N/A
235 172.65.220.142 4 3 0.25 199.39 0.00 N/A
236 104.25.131.218 4 3 0.25 199.47 0.00 N/A
237 172.65.182.19 4 3 0.25 200.05 0.00 N/A
238 104.19.42.3 4 3 0.25 200.06 0.00 N/A
239 104.25.22.250 4 3 0.25 200.09 0.00 N/A
240 104.25.155.139 4 3 0.25 200.19 0.00 N/A
241 104.25.7.77 4 3 0.25 200.55 0.00 N/A
242 104.18.240.134 4 3 0.25 200.85 0.00 N/A
243 104.23.138.193 4 3 0.25 201.23 0.00 N/A
244 104.16.255.166 4 3 0.25 201.57 0.00 N/A
245 172.65.171.208 4 3 0.25 201.79 0.00 N/A
246 104.19.51.187 4 3 0.25 201.87 0.00 N/A
247 104.25.67.102 4 3 0.25 201.96 0.00 N/A
248 172.64.37.142 4 3 0.25 202.65 0.00 N/A
249 104.25.15.172 4 3 0.25 202.72 0.00 N/A
250 104.16.183.157 4 3 0.25 202.80 0.00 N/A
251 104.25.16.44 4 3 0.25 202.92 0.00 N/A
252 104.27.119.20 4 3 0.25 203.03 0.00 N/A
253 104.27.202.72 4 3 0.25 203.32 0.00 N/A
254 104.27.205.247 4 3 0.25 204.24 0.00 N/A
255 104.27.44.147 4 3 0.25 204.95 0.00 N/A
256 172.65.162.180 4 3 0.25 205.18 0.00 N/A
257 104.25.27.102 4 3 0.25 205.50 0.00 N/A
258 104.25.20.71 4 3 0.25 205.55 0.00 N/A
259 104.25.118.22 4 3 0.25 205.63 0.00 N/A
260 104.27.196.211 4 3 0.25 205.69 0.00 N/A
261 162.159.2.235 4 3 0.25 205.82 0.00 N/A
262 104.24.242.122 4 3 0.25 205.83 0.00 N/A
263 172.65.169.247 4 3 0.25 205.93 0.00 N/A
264 104.25.137.129 4 3 0.25 206.03 0.00 N/A
265 108.162.192.52 4 3 0.25 206.74 0.00 N/A
266 172.65.87.122 4 3 0.25 206.76 0.00 N/A
267 172.65.102.200 4 3 0.25 206.82 0.00 N/A
268 104.25.77.56 4 3 0.25 206.91 0.00 N/A
269 104.25.160.115 4 3 0.25 207.14 0.00 N/A
270 104.16.226.129 4 3 0.25 207.18 0.00 N/A
271 104.25.115.147 4 3 0.25 207.34 0.00 N/A
272 172.65.84.239 4 3 0.25 207.39 0.00 N/A
273 104.25.154.250 4 3 0.25 207.40 0.00 N/A
274 104.16.190.135 4 3 0.25 207.60 0.00 N/A
275 172.65.101.45 4 3 0.25 207.72 0.00 N/A
276 104.27.48.85 4 3 0.25 207.91 0.00 N/A
277 104.25.152.164 4 3 0.25 208.62 0.00 N/A
278 172.65.166.48 4 3 0.25 208.71 0.00 N/A
279 104.25.146.130 4 3 0.25 208.94 0.00 N/A
280 104.25.28.221 4 3 0.25 209.34 0.00 N/A
281 172.65.237.6 4 3 0.25 209.59 0.00 N/A
282 104.24.252.194 4 3 0.25 209.71 0.00 N/A
283 104.25.46.121 4 3 0.25 210.02 0.00 N/A
284 104.25.169.246 4 3 0.25 210.61 0.00 N/A
285 104.25.161.66 4 3 0.25 210.67 0.00 N/A
286 104.27.207.21 4 3 0.25 210.90 0.00 N/A
287 104.16.198.181 4 3 0.25 210.97 0.00 N/A
288 104.23.100.117 4 3 0.25 211.25 0.00 N/A
289 162.159.1.252 4 3 0.25 211.26 0.00 N/A
290 172.67.23.7 4 3 0.25 211.28 0.00 N/A
291 162.159.8.88 4 3 0.25 211.35 0.00 N/A
292 172.65.135.249 4 3 0.25 211.63 0.00 N/A
293 172.65.165.6 4 3 0.25 211.69 0.00 N/A
294 104.19.64.35 4 3 0.25 212.13 0.00 N/A
295 104.19.46.41 4 3 0.25 212.23 0.00 N/A
296 104.25.120.222 4 3 0.25 212.38 0.00 N/A
297 104.21.69.157 4 3 0.25 212.45 0.00 N/A
298 190.93.244.83 4 3 0.25 212.70 0.00 N/A
299 104.19.178.229 4 3 0.25 212.79 0.00 N/A
300 104.25.122.149 4 3 0.25 213.01 0.00 N/A
301 104.25.17.46 4 3 0.25 213.05 0.00 N/A
302 104.17.8.199 4 3 0.25 213.24 0.00 N/A
303 104.25.109.0 4 3 0.25 213.25 0.00 N/A
304 104.16.207.56 4 3 0.25 213.36 0.00 N/A
305 172.67.130.27 4 3 0.25 214.47 0.00 N/A
306 172.64.38.0 4 3 0.25 215.75 0.00 N/A
307 198.41.198.1 4 3 0.25 216.23 0.00 N/A
308 104.18.205.192 4 3 0.25 216.30 0.00 N/A
309 104.18.239.237 4 3 0.25 216.80 0.00 N/A
310 172.65.163.87 4 3 0.25 216.98 0.00 N/A
311 104.19.39.46 4 3 0.25 217.28 0.00 N/A
312 104.21.46.108 4 3 0.25 217.74 0.00 N/A
313 104.19.8.219 4 3 0.25 217.91 0.00 N/A
314 162.159.13.95 4 3 0.25 218.47 0.00 N/A
315 172.65.232.188 4 3 0.25 218.78 0.00 N/A
316 104.17.22.192 4 3 0.25 219.23 0.00 N/A
317 172.67.252.113 4 3 0.25 219.49 0.00 N/A
318 104.19.56.193 4 3 0.25 220.02 0.00 N/A
319 104.21.26.80 4 3 0.25 220.23 0.00 N/A
320 104.21.24.120 4 3 0.25 221.44 0.00 N/A
321 104.19.33.84 4 3 0.25 221.66 0.00 N/A
322 172.67.143.82 4 3 0.25 222.08 0.00 N/A
323 172.67.254.234 4 3 0.25 222.35 0.00 N/A
324 104.21.112.16 4 3 0.25 223.11 0.00 N/A
325 104.19.21.218 4 3 0.25 223.37 0.00 N/A
326 104.19.73.167 4 3 0.25 223.90 0.00 N/A
327 104.21.23.135 4 3 0.25 224.35 0.00 N/A
328 104.16.234.1 4 3 0.25 224.35 0.00 N/A
329 104.19.48.44 4 3 0.25 224.48 0.00 N/A
330 172.67.207.103 4 3 0.25 224.66 0.00 N/A
331 172.67.221.88 4 3 0.25 224.72 0.00 N/A
332 104.21.88.25 4 3 0.25 225.10 0.00 N/A
333 104.16.218.194 4 3 0.25 225.40 0.00 N/A
334 104.17.134.211 4 3 0.25 225.62 0.00 N/A
335 172.67.159.242 4 3 0.25 225.83 0.00 N/A
336 104.17.21.1 4 3 0.25 227.12 0.00 N/A
337 104.18.209.181 4 3 0.25 227.46 0.00 N/A
338 172.67.244.246 4 3 0.25 227.66 0.00 N/A
339 172.67.253.56 4 3 0.25 228.01 0.00 N/A
340 172.67.247.4 4 3 0.25 228.89 0.00 N/A
341 104.16.169.161 4 3 0.25 228.97 0.00 N/A
342 172.67.218.10 4 3 0.25 229.20 0.00 N/A
343 104.21.70.108 4 3 0.25 229.34 0.00 N/A
344 172.67.223.187 4 3 0.25 229.65 0.00 N/A
345 172.65.213.174 4 3 0.25 230.05 0.00 N/A
346 104.21.43.192 4 3 0.25 230.10 0.00 N/A
347 104.21.14.60 4 3 0.25 230.30 0.00 N/A
348 172.67.241.43 4 3 0.25 230.34 0.00 N/A
349 104.17.33.86 4 3 0.25 231.27 0.00 N/A
350 172.67.140.236 4 3 0.25 231.73 0.00 N/A
351 104.19.36.71 4 3 0.25 232.24 0.00 N/A
352 104.18.204.121 4 3 0.25 232.55 0.00 N/A
353 172.67.199.61 4 3 0.25 232.65 0.00 N/A
354 104.21.9.13 4 3 0.25 233.36 0.00 N/A
355 104.19.160.32 4 3 0.25 234.72 0.00 N/A
356 104.21.58.175 4 3 0.25 234.77 0.00 N/A
357 172.67.220.236 4 3 0.25 234.79 0.00 N/A
358 162.159.7.161 4 3 0.25 235.58 0.00 N/A
359 104.19.84.157 4 3 0.25 235.61 0.00 N/A
360 104.16.211.208 4 3 0.25 235.86 0.00 N/A
361 104.18.243.77 4 3 0.25 237.12 0.00 N/A
362 172.67.217.240 4 3 0.25 238.03 0.00 N/A
363 172.67.219.51 4 3 0.25 238.64 0.00 N/A
364 104.19.75.223 4 3 0.25 239.77 0.00 N/A
365 104.21.39.161 4 3 0.25 239.78 0.00 N/A
366 172.67.250.234 4 3 0.25 239.85 0.00 N/A
367 104.21.125.50 4 3 0.25 240.77 0.00 N/A
368 104.17.40.37 4 3 0.25 241.11 0.00 N/A
369 104.21.61.36 4 3 0.25 241.76 0.00 N/A
370 172.67.246.106 4 3 0.25 242.09 0.00 N/A
371 172.67.255.212 4 3 0.25 243.02 0.00 N/A
372 104.16.228.223 4 3 0.25 243.10 0.00 N/A
373 104.21.63.40 4 3 0.25 243.37 0.00 N/A
374 104.19.79.26 4 3 0.25 243.66 0.00 N/A
375 104.21.90.89 4 3 0.25 245.12 0.00 N/A
376 104.21.91.252 4 3 0.25 248.01 0.00 N/A
377 104.19.99.114 4 3 0.25 248.71 0.00 N/A
378 104.18.220.135 4 3 0.25 248.95 0.00 N/A
379 104.17.156.56 4 3 0.25 249.01 0.00 N/A
380 104.16.156.57 4 3 0.25 249.55 0.00 N/A
381 104.19.13.228 4 3 0.25 250.32 0.00 N/A
382 104.16.223.248 4 3 0.25 250.33 0.00 N/A
383 104.21.84.7 4 3 0.25 253.21 0.00 N/A
384 104.21.32.10 4 3 0.25 254.79 0.00 N/A
385 104.19.52.148 4 3 0.25 255.58 0.00 N/A
386 188.114.99.114 4 3 0.25 256.25 0.00 N/A
387 162.159.26.85 4 3 0.25 257.47 0.00 N/A
388 104.17.24.239 4 3 0.25 259.24 0.00 N/A
389 104.17.46.40 4 3 0.25 259.94 0.00 N/A
390 104.16.237.232 4 3 0.25 263.49 0.00 N/A
391 104.16.172.243 4 3 0.25 266.25 0.00 N/A
392 104.16.244.83 4 3 0.25 269.50 0.00 N/A
393 104.16.248.22 4 3 0.25 270.58 0.00 N/A
394 104.19.9.231 4 3 0.25 274.11 0.00 N/A
395 104.17.26.25 4 3 0.25 277.95 0.00 N/A
396 104.18.143.96 4 3 0.25 280.35 0.00 N/A
397 104.16.254.154 4 3 0.25 290.76 0.00 N/A
398 104.16.128.24 4 3 0.25 295.17 0.00 N/A
399 104.16.217.215 4 3 0.25 296.90 0.00 N/A
400 104.16.243.152 4 3 0.25 298.05 0.00 N/A
401 104.19.74.232 4 3 0.25 301.24 0.00 N/A
402 104.16.186.132 4 3 0.25 301.86 0.00 N/A
403 104.19.22.68 4 3 0.25 305.49 0.00 N/A
404 104.17.25.230 4 3 0.25 305.60 0.00 N/A
405 162.159.12.250 4 3 0.25 333.08 0.00 N/A
406 104.16.232.127 4 2 0.50 165.77 0.00 N/A
407 172.65.211.195 4 2 0.50 166.03 0.00 N/A
408 104.16.210.63 4 2 0.50 166.28 0.00 N/A
409 104.18.202.245 4 2 0.50 167.10 0.00 N/A
410 104.19.32.244 4 2 0.50 168.25 0.00 N/A
411 104.19.47.130 4 2 0.50 168.65 0.00 N/A
412 104.19.28.39 4 2 0.50 168.73 0.00 N/A
413 104.19.62.128 4 2 0.50 169.06 0.00 N/A
414 104.18.236.37 4 2 0.50 169.51 0.00 N/A
415 104.16.213.127 4 2 0.50 169.63 0.00 N/A
416 104.19.5.114 4 2 0.50 169.63 0.00 N/A
417 172.65.214.236 4 2 0.50 169.80 0.00 N/A
418 104.19.34.192 4 2 0.50 170.53 0.00 N/A
419 172.65.217.13 4 2 0.50 170.61 0.00 N/A
420 104.18.255.70 4 2 0.50 171.30 0.00 N/A
421 104.18.228.127 4 2 0.50 171.34 0.00 N/A
422 104.16.196.113 4 2 0.50 171.73 0.00 N/A
423 104.18.200.77 4 2 0.50 171.82 0.00 N/A
424 104.19.29.149 4 2 0.50 172.26 0.00 N/A
425 104.18.246.66 4 2 0.50 172.34 0.00 N/A
426 104.19.53.109 4 2 0.50 172.43 0.00 N/A
427 104.17.9.181 4 2 0.50 172.74 0.00 N/A
428 172.65.208.209 4 2 0.50 173.03 0.00 N/A
429 104.18.219.64 4 2 0.50 173.10 0.00 N/A
430 104.19.6.132 4 2 0.50 173.37 0.00 N/A
431 104.19.66.0 4 2 0.50 173.63 0.00 N/A
432 172.65.193.184 4 2 0.50 173.81 0.00 N/A
433 104.19.25.178 4 2 0.50 174.11 0.00 N/A
434 104.16.230.87 4 2 0.50 174.42 0.00 N/A
435 104.16.225.15 4 2 0.50 174.97 0.00 N/A
436 104.17.6.82 4 2 0.50 175.80 0.00 N/A
437 104.16.168.243 4 2 0.50 176.35 0.00 N/A
438 162.159.46.227 4 2 0.50 176.41 0.00 N/A
439 104.18.247.48 4 2 0.50 176.50 0.00 N/A
440 104.19.19.210 4 2 0.50 177.17 0.00 N/A
441 172.65.215.191 4 2 0.50 177.38 0.00 N/A
442 172.65.228.103 4 2 0.50 177.92 0.00 N/A
443 104.19.54.104 4 2 0.50 178.14 0.00 N/A
444 172.65.209.80 4 2 0.50 178.23 0.00 N/A
445 172.64.149.174 4 2 0.50 178.96 0.00 N/A
446 108.162.194.75 4 2 0.50 179.04 0.00 N/A
447 104.27.113.13 4 2 0.50 179.52 0.00 N/A
448 104.18.198.55 4 2 0.50 180.07 0.00 N/A
449 104.19.60.74 4 2 0.50 180.28 0.00 N/A
450 104.17.67.158 4 2 0.50 180.45 0.00 N/A
451 104.19.241.60 4 2 0.50 180.59 0.00 N/A
452 172.65.131.127 4 2 0.50 180.84 0.00 N/A
453 104.16.181.177 4 2 0.50 181.33 0.00 N/A
454 172.67.111.160 4 2 0.50 181.90 0.00 N/A
455 104.27.89.177 4 2 0.50 182.25 0.00 N/A
456 104.25.1.205 4 2 0.50 182.37 0.00 N/A
457 104.27.121.46 4 2 0.50 182.55 0.00 N/A
458 104.25.156.2 4 2 0.50 182.89 0.00 N/A
459 172.65.242.204 4 2 0.50 183.62 0.00 N/A
460 172.65.161.140 4 2 0.50 183.64 0.00 N/A
461 104.27.94.162 4 2 0.50 184.37 0.00 N/A
462 104.25.145.241 4 2 0.50 184.61 0.00 N/A
463 104.25.167.30 4 2 0.50 185.29 0.00 N/A
464 104.25.61.52 4 2 0.50 185.51 0.00 N/A
465 104.16.236.16 4 2 0.50 186.96 0.00 N/A
466 172.65.210.45 4 2 0.50 186.99 0.00 N/A
467 104.16.204.13 4 2 0.50 187.11 0.00 N/A
468 172.64.146.66 4 2 0.50 187.42 0.00 N/A
469 172.65.134.158 4 2 0.50 187.58 0.00 N/A
470 172.65.226.202 4 2 0.50 187.86 0.00 N/A
471 104.25.103.134 4 2 0.50 189.11 0.00 N/A
472 172.65.190.155 4 2 0.50 189.11 0.00 N/A
473 104.27.73.8 4 2 0.50 189.20 0.00 N/A
474 172.65.92.58 4 2 0.50 189.23 0.00 N/A
475 104.25.125.82 4 2 0.50 189.24 0.00 N/A
476 104.27.124.170 4 2 0.50 189.25 0.00 N/A
477 104.27.114.18 4 2 0.50 189.58 0.00 N/A
478 104.25.204.1 4 2 0.50 189.60 0.00 N/A
479 104.25.147.35 4 2 0.50 189.62 0.00 N/A
480 104.25.30.42 4 2 0.50 189.70 0.00 N/A
481 104.27.53.231 4 2 0.50 189.94 0.00 N/A
482 104.23.112.114 4 2 0.50 190.05 0.00 N/A
483 104.25.150.43 4 2 0.50 190.41 0.00 N/A
484 104.17.28.28 4 2 0.50 190.48 0.00 N/A
485 104.25.163.122 4 2 0.50 190.63 0.00 N/A
486 104.25.92.129 4 2 0.50 190.84 0.00 N/A
487 104.16.174.195 4 2 0.50 191.23 0.00 N/A
488 172.65.180.91 4 2 0.50 191.24 0.00 N/A
489 172.65.191.61 4 2 0.50 191.38 0.00 N/A
490 104.25.129.229 4 2 0.50 191.48 0.00 N/A
491 172.65.140.27 4 2 0.50 191.66 0.00 N/A
492 104.27.59.198 4 2 0.50 191.79 0.00 N/A
493 104.27.86.147 4 2 0.50 191.95 0.00 N/A
494 104.27.123.120 4 2 0.50 192.21 0.00 N/A
495 162.159.51.244 4 2 0.50 192.76 0.00 N/A
496 104.16.197.186 4 2 0.50 193.16 0.00 N/A
497 104.25.79.107 4 2 0.50 193.32 0.00 N/A
498 172.65.130.170 4 2 0.50 194.25 0.00 N/A
499 104.27.72.254 4 2 0.50 194.69 0.00 N/A
500 172.65.189.137 4 2 0.50 195.25 0.00 N/A
501 104.25.114.125 4 2 0.50 195.28 0.00 N/A
502 104.25.153.60 4 2 0.50 195.49 0.00 N/A
503 104.27.95.5 4 2 0.50 196.06 0.00 N/A
504 172.65.82.218 4 2 0.50 196.10 0.00 N/A
505 104.16.202.120 4 2 0.50 196.33 0.00 N/A
506 104.27.54.21 4 2 0.50 196.33 0.00 N/A
507 172.65.127.66 4 2 0.50 196.40 0.00 N/A
508 172.66.145.171 4 2 0.50 196.63 0.00 N/A
509 104.27.112.128 4 2 0.50 196.68 0.00 N/A
510 104.25.107.63 4 2 0.50 197.10 0.00 N/A
511 104.23.115.58 4 2 0.50 197.14 0.00 N/A
512 172.67.147.131 4 2 0.50 197.27 0.00 N/A
513 104.27.65.58 4 2 0.50 197.46 0.00 N/A
514 104.25.38.160 4 2 0.50 197.66 0.00 N/A
515 104.16.235.17 4 2 0.50 197.72 0.00 N/A
516 104.25.166.221 4 2 0.50 197.78 0.00 N/A
517 104.25.39.109 4 2 0.50 197.83 0.00 N/A
518 104.25.21.254 4 2 0.50 198.45 0.00 N/A
519 172.65.172.134 4 2 0.50 198.48 0.00 N/A
520 104.25.102.240 4 2 0.50 198.58 0.00 N/A
521 104.25.202.146 4 2 0.50 198.61 0.00 N/A
522 104.25.128.235 4 2 0.50 198.76 0.00 N/A
523 104.25.19.188 4 2 0.50 199.01 0.00 N/A
524 104.25.91.90 4 2 0.50 199.07 0.00 N/A
525 104.16.206.246 4 2 0.50 199.23 0.00 N/A
526 172.65.168.169 4 2 0.50 199.43 0.00 N/A
527 172.65.139.219 4 2 0.50 200.38 0.00 N/A
528 104.27.127.17 4 2 0.50 200.40 0.00 N/A
529 172.67.114.185 4 2 0.50 201.63 0.00 N/A
530 104.24.245.37 4 2 0.50 201.73 0.00 N/A
531 104.18.233.82 4 2 0.50 201.99 0.00 N/A
532 104.23.134.70 4 2 0.50 202.04 0.00 N/A
533 104.27.45.21 4 2 0.50 202.21 0.00 N/A
534 104.25.76.224 4 2 0.50 202.43 0.00 N/A
535 104.25.69.222 4 2 0.50 202.52 0.00 N/A
536 172.65.244.105 4 2 0.50 202.69 0.00 N/A
537 172.66.196.154 4 2 0.50 202.74 0.00 N/A
538 172.65.167.167 4 2 0.50 202.94 0.00 N/A
539 104.25.117.55 4 2 0.50 203.07 0.00 N/A
540 104.25.132.250 4 2 0.50 203.24 0.00 N/A
541 104.19.23.120 4 2 0.50 203.25 0.00 N/A
542 172.67.193.52 4 2 0.50 203.42 0.00 N/A
543 104.16.231.173 4 2 0.50 203.75 0.00 N/A
544 104.25.111.179 4 2 0.50 203.81 0.00 N/A
545 172.65.145.26 4 2 0.50 203.95 0.00 N/A
546 172.67.122.116 4 2 0.50 204.07 0.00 N/A
547 104.27.87.198 4 2 0.50 204.25 0.00 N/A
548 104.16.250.187 4 2 0.50 204.32 0.00 N/A
549 172.65.192.175 4 2 0.50 204.45 0.00 N/A
550 172.65.178.57 4 2 0.50 204.62 0.00 N/A
551 104.27.43.169 4 2 0.50 204.75 0.00 N/A
552 172.67.112.161 4 2 0.50 204.87 0.00 N/A
553 162.159.42.97 4 2 0.50 205.12 0.00 N/A
554 172.67.238.4 4 2 0.50 205.90 0.00 N/A
555 172.67.243.192 4 2 0.50 205.92 0.00 N/A
556 104.23.98.14 4 2 0.50 205.99 0.00 N/A
557 104.27.71.131 4 2 0.50 206.35 0.00 N/A
558 104.25.182.99 4 2 0.50 206.55 0.00 N/A
559 104.17.15.149 4 2 0.50 206.97 0.00 N/A
560 104.25.89.4 4 2 0.50 207.06 0.00 N/A
561 104.27.88.10 4 2 0.50 207.06 0.00 N/A
562 104.25.78.37 4 2 0.50 208.03 0.00 N/A
563 104.23.101.18 4 2 0.50 208.19 0.00 N/A
564 104.25.162.191 4 2 0.50 208.30 0.00 N/A
565 104.16.209.136 4 2 0.50 208.41 0.00 N/A
566 104.27.46.19 4 2 0.50 209.08 0.00 N/A
567 104.25.148.28 4 2 0.50 209.50 0.00 N/A
568 104.25.72.41 4 2 0.50 209.60 0.00 N/A
569 104.27.69.216 4 2 0.50 209.81 0.00 N/A
570 172.65.124.65 4 2 0.50 209.92 0.00 N/A
571 104.25.123.30 4 2 0.50 210.09 0.00 N/A
572 104.25.143.37 4 2 0.50 210.44 0.00 N/A
573 104.27.74.218 4 2 0.50 210.49 0.00 N/A
574 104.25.71.180 4 2 0.50 211.95 0.00 N/A
575 104.25.110.235 4 2 0.50 212.26 0.00 N/A
576 172.67.230.47 4 2 0.50 212.26 0.00 N/A
577 104.25.106.176 4 2 0.50 212.27 0.00 N/A
578 104.25.119.239 4 2 0.50 212.62 0.00 N/A
579 104.25.18.74 4 2 0.50 213.15 0.00 N/A
580 104.24.254.26 4 2 0.50 213.23 0.00 N/A
581 172.67.226.119 4 2 0.50 213.24 0.00 N/A
582 172.67.179.228 4 2 0.50 213.75 0.00 N/A
583 104.25.159.108 4 2 0.50 214.22 0.00 N/A
584 172.67.117.58 4 2 0.50 214.58 0.00 N/A
585 104.27.56.251 4 2 0.50 214.91 0.00 N/A
586 172.67.224.75 4 2 0.50 215.10 0.00 N/A
587 172.65.129.88 4 2 0.50 215.35 0.00 N/A
588 172.67.249.143 4 2 0.50 215.54 0.00 N/A
589 172.67.43.168 4 2 0.50 216.97 0.00 N/A
590 172.67.227.148 4 2 0.50 217.04 0.00 N/A
591 172.67.36.28 4 2 0.50 218.39 0.00 N/A
592 104.18.254.179 4 2 0.50 218.56 0.00 N/A
593 172.67.144.231 4 2 0.50 219.65 0.00 N/A
594 104.21.100.208 4 2 0.50 219.97 0.00 N/A
595 104.25.70.253 4 2 0.50 220.19 0.00 N/A
596 104.21.94.224 4 2 0.50 220.34 0.00 N/A
597 104.21.89.3 4 2 0.50 220.83 0.00 N/A
598 172.67.229.25 4 2 0.50 222.55 0.00 N/A
599 104.21.68.236 4 2 0.50 222.66 0.00 N/A
600 172.67.233.144 4 2 0.50 222.73 0.00 N/A
601 104.21.62.90 4 2 0.50 223.24 0.00 N/A
602 172.67.245.123 4 2 0.50 223.47 0.00 N/A
603 104.21.36.9 4 2 0.50 223.48 0.00 N/A
604 198.41.214.181 4 2 0.50 223.61 0.00 N/A
605 172.67.196.9 4 2 0.50 223.67 0.00 N/A
606 172.67.171.87 4 2 0.50 224.33 0.00 N/A
607 104.21.73.62 4 2 0.50 224.38 0.00 N/A
608 104.18.214.143 4 2 0.50 225.16 0.00 N/A
609 104.19.16.151 4 2 0.50 225.38 0.00 N/A
610 172.67.133.180 4 2 0.50 225.94 0.00 N/A
611 104.21.45.36 4 2 0.50 226.22 0.00 N/A
612 104.17.11.9 4 2 0.50 227.71 0.00 N/A
613 172.67.150.56 4 2 0.50 227.74 0.00 N/A
614 104.21.22.214 4 2 0.50 227.81 0.00 N/A
615 104.21.79.9 4 2 0.50 228.13 0.00 N/A
616 104.17.12.216 4 2 0.50 228.19 0.00 N/A
617 104.21.51.135 4 2 0.50 228.24 0.00 N/A
618 104.21.35.76 4 2 0.50 228.26 0.00 N/A
619 104.21.47.241 4 2 0.50 228.45 0.00 N/A
620 172.67.236.31 4 2 0.50 228.80 0.00 N/A
621 172.67.148.83 4 2 0.50 229.02 0.00 N/A
622 104.16.227.253 4 2 0.50 229.10 0.00 N/A
623 172.67.239.243 4 2 0.50 229.11 0.00 N/A
624 172.67.184.224 4 2 0.50 229.43 0.00 N/A
625 172.67.200.95 4 2 0.50 230.74 0.00 N/A
626 104.21.117.130 4 2 0.50 231.99 0.00 N/A
627 104.21.38.25 4 2 0.50 232.07 0.00 N/A
628 172.67.235.165 4 2 0.50 232.51 0.00 N/A
629 172.67.191.89 4 2 0.50 232.92 0.00 N/A
630 104.21.78.31 4 2 0.50 233.48 0.00 N/A
631 172.64.235.13 4 2 0.50 233.52 0.00 N/A
632 104.17.5.111 4 2 0.50 234.38 0.00 N/A
633 172.67.163.174 4 2 0.50 234.60 0.00 N/A
634 172.65.23.28 4 2 0.50 235.39 0.00 N/A
635 104.21.77.247 4 2 0.50 235.64 0.00 N/A
636 104.21.95.112 4 2 0.50 235.92 0.00 N/A
637 172.67.234.190 4 2 0.50 236.33 0.00 N/A
638 104.21.80.214 4 2 0.50 236.43 0.00 N/A
639 172.67.187.174 4 2 0.50 236.78 0.00 N/A
640 104.21.72.115 4 2 0.50 236.98 0.00 N/A
641 104.16.249.247 4 2 0.50 237.12 0.00 N/A
642 172.67.222.130 4 2 0.50 237.44 0.00 N/A
643 172.67.232.134 4 2 0.50 237.45 0.00 N/A
644 172.67.186.23 4 2 0.50 237.52 0.00 N/A
645 172.67.146.102 4 2 0.50 237.54 0.00 N/A
646 104.21.71.65 4 2 0.50 238.21 0.00 N/A
647 172.67.240.219 4 2 0.50 238.31 0.00 N/A
648 104.19.31.159 4 2 0.50 238.99 0.00 N/A
649 172.67.209.179 4 2 0.50 239.14 0.00 N/A
650 104.18.203.126 4 2 0.50 240.80 0.00 N/A
651 172.67.225.16 4 2 0.50 241.65 0.00 N/A
652 104.19.20.215 4 2 0.50 241.67 0.00 N/A
653 104.18.199.178 4 2 0.50 241.70 0.00 N/A
654 104.21.93.155 4 2 0.50 242.14 0.00 N/A
655 104.21.105.247 4 2 0.50 242.39 0.00 N/A
656 172.67.237.203 4 2 0.50 243.20 0.00 N/A
657 104.21.67.19 4 2 0.50 244.70 0.00 N/A
658 172.67.129.156 4 2 0.50 245.99 0.00 N/A
659 172.67.242.188 4 2 0.50 246.07 0.00 N/A
660 172.67.201.160 4 2 0.50 246.09 0.00 N/A
661 172.67.228.250 4 2 0.50 246.47 0.00 N/A
662 172.67.195.214 4 2 0.50 247.36 0.00 N/A
663 104.17.23.35 4 2 0.50 250.13 0.00 N/A
664 104.16.252.241 4 2 0.50 251.28 0.00 N/A
665 104.18.249.186 4 2 0.50 252.02 0.00 N/A
666 172.67.248.187 4 2 0.50 252.07 0.00 N/A
667 172.67.131.46 4 2 0.50 252.28 0.00 N/A
668 104.17.0.9 4 2 0.50 252.95 0.00 N/A
669 172.67.204.103 4 2 0.50 254.53 0.00 N/A
670 104.16.179.117 4 2 0.50 254.91 0.00 N/A
671 172.65.12.220 4 2 0.50 256.55 0.00 N/A
672 172.67.176.156 4 2 0.50 256.81 0.00 N/A
673 172.65.231.8 4 2 0.50 258.67 0.00 N/A
674 104.16.203.254 4 2 0.50 262.04 0.00 N/A
675 104.19.41.46 4 2 0.50 265.39 0.00 N/A
676 104.16.221.213 4 2 0.50 268.02 0.00 N/A
677 104.19.97.228 4 2 0.50 269.77 0.00 N/A
678 104.19.38.43 4 2 0.50 275.62 0.00 N/A
679 104.16.205.95 4 2 0.50 282.59 0.00 N/A
680 162.159.0.137 4 2 0.50 283.38 0.00 N/A
681 162.159.32.3 4 2 0.50 291.78 0.00 N/A
682 162.159.36.48 4 2 0.50 296.85 0.00 N/A
683 104.16.182.109 4 2 0.50 308.08 0.00 N/A
684 104.17.1.92 4 2 0.50 315.93 0.00 N/A
685 104.19.72.245 4 2 0.50 319.16 0.00 N/A
686 104.16.229.45 4 2 0.50 321.22 0.00 N/A
687 190.93.245.152 4 2 0.50 336.90 0.00 N/A
688 104.17.7.145 4 2 0.50 349.57 0.00 N/A
689 104.18.213.14 4 1 0.75 163.67 0.00 N/A
690 104.19.30.199 4 1 0.75 164.04 0.00 N/A
691 104.18.252.219 4 1 0.75 164.51 0.00 N/A
692 104.18.237.110 4 1 0.75 165.68 0.00 N/A
693 104.16.188.172 4 1 0.75 166.77 0.00 N/A
694 104.18.217.241 4 1 0.75 167.06 0.00 N/A
695 172.65.246.132 4 1 0.75 168.13 0.00 N/A
696 104.19.65.194 4 1 0.75 168.13 0.00 N/A
697 104.16.214.194 4 1 0.75 168.14 0.00 N/A
698 104.16.199.146 4 1 0.75 169.13 0.00 N/A
699 104.16.187.85 4 1 0.75 169.21 0.00 N/A
700 104.18.201.59 4 1 0.75 169.94 0.00 N/A
701 104.16.200.21 4 1 0.75 170.40 0.00 N/A
702 104.19.7.172 4 1 0.75 171.16 0.00 N/A
703 104.16.233.87 4 1 0.75 171.19 0.00 N/A
704 104.18.241.152 4 1 0.75 171.42 0.00 N/A
705 104.18.212.123 4 1 0.75 171.42 0.00 N/A
706 104.16.239.52 4 1 0.75 171.47 0.00 N/A
707 104.18.244.113 4 1 0.75 171.79 0.00 N/A
708 172.65.227.240 4 1 0.75 171.86 0.00 N/A
709 172.65.251.201 4 1 0.75 172.89 0.00 N/A
710 104.17.75.226 4 1 0.75 173.25 0.00 N/A
711 104.18.223.101 4 1 0.75 173.25 0.00 N/A
712 104.18.229.11 4 1 0.75 173.40 0.00 N/A
713 104.19.37.75 4 1 0.75 173.70 0.00 N/A
714 104.16.177.246 4 1 0.75 174.08 0.00 N/A
715 104.18.218.96 4 1 0.75 174.15 0.00 N/A
716 104.16.240.184 4 1 0.75 174.20 0.00 N/A
717 104.16.241.21 4 1 0.75 174.25 0.00 N/A
718 104.21.237.45 4 1 0.75 176.08 0.00 N/A
719 104.17.30.19 4 1 0.75 176.25 0.00 N/A
720 104.18.225.76 4 1 0.75 176.46 0.00 N/A
721 172.65.229.96 4 1 0.75 176.57 0.00 N/A
722 104.19.26.118 4 1 0.75 176.57 0.00 N/A
723 104.17.45.234 4 1 0.75 176.78 0.00 N/A
724 172.65.219.91 4 1 0.75 176.96 0.00 N/A
725 198.41.223.198 4 1 0.75 177.06 0.00 N/A
726 172.65.222.206 4 1 0.75 178.64 0.00 N/A
727 190.93.246.193 4 1 0.75 179.66 0.00 N/A
728 104.25.75.19 4 1 0.75 179.71 0.00 N/A
729 104.18.226.224 4 1 0.75 180.63 0.00 N/A
730 108.162.193.248 4 1 0.75 181.13 0.00 N/A
731 104.25.31.119 4 1 0.75 181.94 0.00 N/A
732 172.67.110.213 4 1 0.75 182.15 0.00 N/A
733 162.159.41.63 4 1 0.75 182.80 0.00 N/A
734 104.25.8.235 4 1 0.75 182.84 0.00 N/A
735 104.25.105.177 4 1 0.75 182.97 0.00 N/A
736 104.25.29.162 4 1 0.75 183.02 0.00 N/A
737 172.67.118.15 4 1 0.75 183.07 0.00 N/A
738 172.65.174.192 4 1 0.75 183.14 0.00 N/A
739 104.25.50.20 4 1 0.75 183.59 0.00 N/A
740 104.21.239.56 4 1 0.75 183.60 0.00 N/A
741 172.65.159.113 4 1 0.75 183.75 0.00 N/A
742 104.25.41.41 4 1 0.75 183.86 0.00 N/A
743 104.25.168.83 4 1 0.75 184.24 0.00 N/A
744 104.23.123.173 4 1 0.75 184.85 0.00 N/A
745 104.23.116.70 4 1 0.75 184.85 0.00 N/A
746 104.25.170.252 4 1 0.75 184.94 0.00 N/A
747 104.27.42.251 4 1 0.75 185.19 0.00 N/A
748 172.65.234.41 4 1 0.75 185.35 0.00 N/A
749 104.16.7.148 4 1 0.75 185.90 0.00 N/A
750 162.159.128.78 4 1 0.75 185.91 0.00 N/A
751 104.25.85.217 4 1 0.75 186.13 0.00 N/A
752 104.25.23.29 4 1 0.75 186.50 0.00 N/A
753 104.23.141.211 4 1 0.75 186.78 0.00 N/A
754 104.27.76.127 4 1 0.75 187.87 0.00 N/A
755 104.16.157.22 4 1 0.75 188.21 0.00 N/A
756 104.25.178.192 4 1 0.75 189.04 0.00 N/A
757 104.17.241.221 4 1 0.75 190.77 0.00 N/A
758 104.19.96.75 4 1 0.75 190.80 0.00 N/A
759 162.159.245.6 4 1 0.75 191.48 0.00 N/A
760 104.23.129.59 4 1 0.75 192.33 0.00 N/A
761 162.159.243.54 4 1 0.75 192.36 0.00 N/A
762 104.27.92.55 4 1 0.75 192.62 0.00 N/A
763 104.25.65.123 4 1 0.75 192.95 0.00 N/A
764 104.25.157.69 4 1 0.75 193.41 0.00 N/A
765 172.65.158.237 4 1 0.75 194.02 0.00 N/A
766 104.19.2.132 4 1 0.75 194.04 0.00 N/A
767 104.23.114.211 4 1 0.75 194.68 0.00 N/A
768 104.27.52.65 4 1 0.75 194.82 0.00 N/A
769 104.25.116.110 4 1 0.75 194.86 0.00 N/A
770 172.65.179.128 4 1 0.75 194.91 0.00 N/A
771 104.25.124.114 4 1 0.75 195.15 0.00 N/A
772 104.25.0.192 4 1 0.75 195.22 0.00 N/A
773 104.25.86.81 4 1 0.75 195.43 0.00 N/A
774 104.25.144.144 4 1 0.75 195.68 0.00 N/A
775 104.25.10.30 4 1 0.75 195.76 0.00 N/A
776 172.65.173.33 4 1 0.75 195.82 0.00 N/A
777 104.25.173.172 4 1 0.75 196.16 0.00 N/A
778 104.25.73.32 4 1 0.75 196.44 0.00 N/A
779 104.27.75.93 4 1 0.75 196.77 0.00 N/A
780 104.27.91.117 4 1 0.75 197.12 0.00 N/A
781 104.25.140.229 4 1 0.75 197.32 0.00 N/A
782 172.65.185.64 4 1 0.75 197.33 0.00 N/A
783 104.25.66.201 4 1 0.75 197.39 0.00 N/A
784 172.65.142.109 4 1 0.75 197.51 0.00 N/A
785 172.65.175.0 4 1 0.75 197.54 0.00 N/A
786 172.65.164.223 4 1 0.75 197.54 0.00 N/A
787 104.16.102.58 4 1 0.75 197.81 0.00 N/A
788 172.65.133.234 4 1 0.75 197.83 0.00 N/A
789 104.25.133.24 4 1 0.75 197.83 0.00 N/A
790 104.25.81.166 4 1 0.75 197.91 0.00 N/A
791 172.67.134.70 4 1 0.75 198.12 0.00 N/A
792 104.25.127.129 4 1 0.75 198.41 0.00 N/A
793 104.27.68.180 4 1 0.75 198.52 0.00 N/A
794 104.25.113.193 4 1 0.75 198.67 0.00 N/A
795 104.27.61.7 4 1 0.75 198.77 0.00 N/A
796 172.65.156.55 4 1 0.75 198.83 0.00 N/A
797 172.65.132.228 4 1 0.75 199.10 0.00 N/A
798 104.23.106.123 4 1 0.75 199.24 0.00 N/A
799 172.67.172.253 4 1 0.75 199.41 0.00 N/A
800 172.67.113.106 4 1 0.75 199.41 0.00 N/A
801 104.18.151.71 4 1 0.75 199.45 0.00 N/A
802 104.25.44.230 4 1 0.75 199.53 0.00 N/A
803 172.65.183.239 4 1 0.75 199.54 0.00 N/A
804 104.21.16.108 4 1 0.75 200.13 0.00 N/A
805 172.65.188.8 4 1 0.75 200.15 0.00 N/A
806 172.65.160.83 4 1 0.75 200.30 0.00 N/A
807 104.27.78.3 4 1 0.75 200.46 0.00 N/A
808 104.27.80.42 4 1 0.75 200.69 0.00 N/A
809 104.27.82.19 4 1 0.75 200.93 0.00 N/A
810 104.18.150.215 4 1 0.75 201.21 0.00 N/A
811 104.25.82.130 4 1 0.75 201.26 0.00 N/A
812 172.67.132.213 4 1 0.75 201.42 0.00 N/A
813 104.25.165.110 4 1 0.75 203.09 0.00 N/A
814 172.67.116.7 4 1 0.75 204.09 0.00 N/A
815 172.67.79.153 4 1 0.75 205.89 0.00 N/A
816 104.25.177.29 4 1 0.75 206.47 0.00 N/A
817 104.23.127.84 4 1 0.75 206.79 0.00 N/A
818 172.65.157.26 4 1 0.75 206.81 0.00 N/A
819 172.65.143.100 4 1 0.75 207.04 0.00 N/A
820 172.67.213.100 4 1 0.75 207.71 0.00 N/A
821 172.67.120.9 4 1 0.75 207.72 0.00 N/A
822 172.67.153.129 4 1 0.75 208.63 0.00 N/A
823 172.67.125.200 4 1 0.75 208.65 0.00 N/A
824 104.25.149.106 4 1 0.75 208.83 0.00 N/A
825 162.159.34.226 4 1 0.75 209.01 0.00 N/A
826 104.21.104.184 4 1 0.75 209.12 0.00 N/A
827 172.67.212.137 4 1 0.75 209.67 0.00 N/A
828 172.67.123.136 4 1 0.75 209.89 0.00 N/A
829 104.21.0.77 4 1 0.75 210.27 0.00 N/A
830 172.65.170.217 4 1 0.75 210.29 0.00 N/A
831 104.27.35.43 4 1 0.75 210.34 0.00 N/A
832 104.27.79.195 4 1 0.75 210.51 0.00 N/A
833 104.27.63.69 4 1 0.75 210.53 0.00 N/A
834 104.25.26.185 4 1 0.75 210.61 0.00 N/A
835 104.25.42.149 4 1 0.75 210.70 0.00 N/A
836 198.41.196.53 4 1 0.75 210.92 0.00 N/A
837 172.65.202.128 4 1 0.75 211.10 0.00 N/A
838 104.17.17.118 4 1 0.75 211.16 0.00 N/A
839 104.25.126.214 4 1 0.75 211.18 0.00 N/A
840 172.65.123.189 4 1 0.75 211.24 0.00 N/A
841 104.27.60.48 4 1 0.75 211.46 0.00 N/A
842 172.65.186.1 4 1 0.75 211.52 0.00 N/A
843 172.65.86.79 4 1 0.75 211.57 0.00 N/A
844 104.21.10.53 4 1 0.75 211.76 0.00 N/A
845 172.65.184.152 4 1 0.75 211.78 0.00 N/A
846 104.27.70.234 4 1 0.75 211.98 0.00 N/A
847 172.67.168.42 4 1 0.75 212.00 0.00 N/A
848 104.21.99.173 4 1 0.75 212.05 0.00 N/A
849 172.65.181.172 4 1 0.75 212.07 0.00 N/A
850 172.67.203.54 4 1 0.75 212.08 0.00 N/A
851 104.27.50.143 4 1 0.75 212.10 0.00 N/A
852 104.21.53.192 4 1 0.75 212.10 0.00 N/A
853 172.67.206.8 4 1 0.75 212.26 0.00 N/A
854 104.27.77.89 4 1 0.75 212.28 0.00 N/A
855 104.25.151.104 4 1 0.75 212.32 0.00 N/A
856 104.21.12.16 4 1 0.75 212.68 0.00 N/A
857 104.21.17.104 4 1 0.75 212.70 0.00 N/A
858 172.65.187.74 4 1 0.75 212.74 0.00 N/A
859 104.25.33.231 4 1 0.75 212.80 0.00 N/A
860 104.25.141.117 4 1 0.75 213.00 0.00 N/A
861 104.27.67.150 4 1 0.75 213.13 0.00 N/A
862 172.65.144.178 4 1 0.75 213.20 0.00 N/A
863 104.21.52.226 4 1 0.75 213.68 0.00 N/A
864 172.65.177.1 4 1 0.75 213.78 0.00 N/A
865 104.27.58.213 4 1 0.75 214.07 0.00 N/A
866 172.67.178.33 4 1 0.75 214.39 0.00 N/A
867 172.67.127.189 4 1 0.75 214.40 0.00 N/A
868 172.67.167.87 4 1 0.75 214.48 0.00 N/A
869 104.21.96.216 4 1 0.75 214.66 0.00 N/A
870 104.21.103.195 4 1 0.75 214.83 0.00 N/A
871 104.23.122.11 4 1 0.75 214.85 0.00 N/A
872 104.21.87.122 4 1 0.75 214.92 0.00 N/A
873 172.67.185.245 4 1 0.75 214.92 0.00 N/A
874 172.65.91.153 4 1 0.75 215.02 0.00 N/A
875 104.27.97.215 4 1 0.75 215.05 0.00 N/A
876 104.25.142.8 4 1 0.75 215.25 0.00 N/A
877 104.23.132.217 4 1 0.75 215.33 0.00 N/A
878 172.67.145.128 4 1 0.75 215.37 0.00 N/A
879 172.67.210.240 4 1 0.75 215.43 0.00 N/A
880 172.67.182.141 4 1 0.75 215.57 0.00 N/A
881 172.67.170.100 4 1 0.75 215.85 0.00 N/A
882 172.67.215.56 4 1 0.75 216.24 0.00 N/A
883 172.65.176.224 4 1 0.75 216.76 0.00 N/A
884 172.67.151.13 4 1 0.75 217.05 0.00 N/A
885 104.21.198.22 4 1 0.75 217.27 0.00 N/A
886 104.21.50.44 4 1 0.75 217.36 0.00 N/A
887 104.25.32.177 4 1 0.75 217.38 0.00 N/A
888 104.19.35.200 4 1 0.75 217.44 0.00 N/A
889 104.21.115.123 4 1 0.75 217.72 0.00 N/A
890 104.21.20.237 4 1 0.75 218.20 0.00 N/A
891 172.67.173.238 4 1 0.75 218.40 0.00 N/A
892 104.25.231.57 4 1 0.75 218.45 0.00 N/A
893 104.21.106.152 4 1 0.75 218.58 0.00 N/A
894 104.27.126.205 4 1 0.75 218.88 0.00 N/A
895 172.67.124.177 4 1 0.75 219.90 0.00 N/A
896 172.66.164.0 4 1 0.75 220.22 0.00 N/A
897 104.21.121.201 4 1 0.75 221.67 0.00 N/A
898 104.19.45.187 4 1 0.75 221.99 0.00 N/A
899 104.21.6.226 4 1 0.75 222.78 0.00 N/A
900 172.67.188.195 4 1 0.75 224.44 0.00 N/A
901 104.21.59.151 4 1 0.75 224.92 0.00 N/A
902 104.21.111.210 4 1 0.75 225.97 0.00 N/A
903 172.65.221.120 4 1 0.75 226.62 0.00 N/A
904 172.65.93.123 4 1 0.75 226.94 0.00 N/A
905 104.21.82.137 4 1 0.75 227.02 0.00 N/A
906 104.21.75.155 4 1 0.75 227.19 0.00 N/A
907 104.21.4.103 4 1 0.75 227.65 0.00 N/A
908 172.65.218.115 4 1 0.75 228.33 0.00 N/A
909 172.67.183.190 4 1 0.75 228.38 0.00 N/A
910 172.65.225.135 4 1 0.75 228.57 0.00 N/A
911 104.21.18.142 4 1 0.75 228.62 0.00 N/A
912 172.67.216.164 4 1 0.75 229.22 0.00 N/A
913 104.21.41.229 4 1 0.75 229.28 0.00 N/A
914 104.21.86.97 4 1 0.75 229.30 0.00 N/A
915 104.21.54.37 4 1 0.75 229.37 0.00 N/A
916 172.67.190.171 4 1 0.75 229.48 0.00 N/A
917 172.67.194.139 4 1 0.75 229.59 0.00 N/A
918 104.21.40.93 4 1 0.75 229.68 0.00 N/A
919 172.67.180.110 4 1 0.75 229.94 0.00 N/A
920 172.67.166.204 4 1 0.75 230.28 0.00 N/A
921 104.21.74.128 4 1 0.75 230.60 0.00 N/A
922 172.67.214.107 4 1 0.75 230.77 0.00 N/A
923 172.67.175.89 4 1 0.75 231.01 0.00 N/A
924 104.16.175.228 4 1 0.75 231.75 0.00 N/A
925 104.21.48.8 4 1 0.75 232.07 0.00 N/A
926 104.21.37.104 4 1 0.75 232.16 0.00 N/A
927 104.21.8.192 4 1 0.75 233.78 0.00 N/A
928 172.67.202.238 4 1 0.75 235.09 0.00 N/A
929 172.67.205.247 4 1 0.75 235.42 0.00 N/A
930 172.65.223.201 4 1 0.75 236.56 0.00 N/A
931 104.21.193.204 4 1 0.75 236.93 0.00 N/A
932 104.21.102.58 4 1 0.75 236.95 0.00 N/A
933 104.18.231.140 4 1 0.75 238.04 0.00 N/A
934 172.67.154.197 4 1 0.75 240.34 0.00 N/A
935 104.16.180.108 4 1 0.75 242.44 0.00 N/A
936 104.21.116.58 4 1 0.75 243.03 0.00 N/A
937 172.67.136.221 4 1 0.75 243.68 0.00 N/A
938 104.21.13.66 4 1 0.75 244.41 0.00 N/A
939 104.21.92.240 4 1 0.75 244.88 0.00 N/A
940 172.67.157.171 4 1 0.75 245.27 0.00 N/A
941 104.21.101.247 4 1 0.75 246.05 0.00 N/A
942 172.67.181.79 4 1 0.75 246.05 0.00 N/A
943 104.21.118.139 4 1 0.75 246.36 0.00 N/A
944 172.67.177.56 4 1 0.75 246.65 0.00 N/A
945 104.21.127.178 4 1 0.75 246.81 0.00 N/A
946 104.21.98.191 4 1 0.75 247.32 0.00 N/A
947 104.21.113.193 4 1 0.75 247.69 0.00 N/A
948 172.67.189.86 4 1 0.75 248.51 0.00 N/A
949 104.21.2.249 4 1 0.75 248.68 0.00 N/A
950 104.21.222.98 4 1 0.75 248.68 0.00 N/A
951 104.18.242.144 4 1 0.75 250.21 0.00 N/A
952 104.21.126.207 4 1 0.75 251.52 0.00 N/A
953 104.16.242.230 4 1 0.75 253.13 0.00 N/A
954 172.67.211.106 4 1 0.75 253.30 0.00 N/A
955 104.21.114.153 4 1 0.75 256.73 0.00 N/A
956 104.21.97.15 4 1 0.75 258.68 0.00 N/A
957 172.67.174.227 4 1 0.75 259.79 0.00 N/A
958 172.67.198.73 4 1 0.75 259.83 0.00 N/A
959 104.19.3.200 4 1 0.75 260.44 0.00 N/A
960 104.19.63.44 4 1 0.75 262.87 0.00 N/A
961 172.65.32.207 4 1 0.75 263.57 0.00 N/A
962 104.18.216.81 4 1 0.75 264.24 0.00 N/A
963 104.21.1.68 4 1 0.75 265.42 0.00 N/A
964 104.21.81.31 4 1 0.75 265.49 0.00 N/A
965 104.18.251.232 4 1 0.75 266.91 0.00 N/A
966 172.67.197.65 4 1 0.75 267.28 0.00 N/A
967 104.18.156.241 4 1 0.75 269.81 0.00 N/A
968 172.65.24.244 4 1 0.75 271.18 0.00 N/A
969 104.16.253.250 4 1 0.75 271.66 0.00 N/A
970 104.17.4.252 4 1 0.75 273.42 0.00 N/A
971 104.18.215.115 4 1 0.75 278.16 0.00 N/A
972 104.16.212.149 4 1 0.75 287.06 0.00 N/A
973 104.17.13.0 4 1 0.75 290.13 0.00 N/A
974 141.101.114.149 4 1 0.75 303.88 0.00 N/A
975 104.18.230.149 4 1 0.75 333.73 0.00 N/A
976 104.18.248.2 4 1 0.75 346.67 0.00 N/A
977 104.18.71.193 4 1 0.75 358.68 0.00 N/A
978 104.17.2.17 4 1 0.75 386.55 0.00 N/A
979 104.17.216.244 4 1 0.75 398.34 0.00 N/A

11
source/bin/result.csv Normal file
View File

@@ -0,0 +1,11 @@
IP 地址,已发送,已接收,丢包率,平均延迟,下载速度(MB/s),地区码
104.21.237.213,4,4,0.00,173.18,86.14,LAX
141.101.120.105,4,4,0.00,173.02,40.72,SJC
162.159.251.244,4,4,0.00,171.02,8.59,LAX
104.18.19.226,4,4,0.00,172.26,5.27,LAX
104.19.32.102,4,4,0.00,173.01,4.52,SJC
141.101.121.113,4,4,0.00,169.51,3.69,SJC
162.159.207.149,4,4,0.00,169.94,3.24,SJC
104.19.21.114,4,4,0.00,171.00,0.38,SJC
104.19.127.210,4,4,0.00,170.53,0.22,SJC
104.19.60.94,4,4,0.00,168.33,0.00,N/A
1 IP 地址 已发送 已接收 丢包率 平均延迟 下载速度(MB/s) 地区码
2 104.21.237.213 4 4 0.00 173.18 86.14 LAX
3 141.101.120.105 4 4 0.00 173.02 40.72 SJC
4 162.159.251.244 4 4 0.00 171.02 8.59 LAX
5 104.18.19.226 4 4 0.00 172.26 5.27 LAX
6 104.19.32.102 4 4 0.00 173.01 4.52 SJC
7 141.101.121.113 4 4 0.00 169.51 3.69 SJC
8 162.159.207.149 4 4 0.00 169.94 3.24 SJC
9 104.19.21.114 4 4 0.00 171.00 0.38 SJC
10 104.19.127.210 4 4 0.00 170.53 0.22 SJC
11 104.19.60.94 4 4 0.00 168.33 0.00 N/A

11
source/core/__init__.py Normal file
View File

@@ -0,0 +1,11 @@
from .animations import MultiStageAnimations
from .ui_manager import UIManager
from .download_manager import DownloadManager
from .debug_manager import DebugManager
__all__ = [
'MultiStageAnimations',
'UIManager',
'DownloadManager',
'DebugManager'
]

View File

@@ -0,0 +1,57 @@
import os
import sys
from PySide6 import QtWidgets
from data.config import LOG_FILE
from utils import Logger
class DebugManager:
def __init__(self, main_window):
"""初始化调试管理器
Args:
main_window: 主窗口实例
"""
self.main_window = main_window
self.logger = None
self.original_stdout = None
self.original_stderr = None
def toggle_debug_mode(self, checked):
"""切换调试模式
Args:
checked: 是否启用调试模式
"""
self.main_window.config["debug_mode"] = checked
self.main_window.save_config(self.main_window.config)
if checked:
self.start_logging()
else:
self.stop_logging()
def start_logging(self):
"""启动日志记录"""
if self.logger is None:
try:
if os.path.exists(LOG_FILE):
os.remove(LOG_FILE)
# 保存原始的 stdout 和 stderr
self.original_stdout = sys.stdout
self.original_stderr = sys.stderr
# 创建 Logger 实例
self.logger = Logger(LOG_FILE, self.original_stdout)
sys.stdout = self.logger
sys.stderr = self.logger
print("--- Debug mode enabled ---")
except (IOError, OSError) as e:
QtWidgets.QMessageBox.critical(self.main_window, "错误", f"无法创建日志文件: {e}")
self.logger = None
def stop_logging(self):
"""停止日志记录"""
if self.logger:
print("--- Debug mode disabled ---")
sys.stdout = self.original_stdout
sys.stderr = self.original_stderr
self.logger.close()
self.logger = None

View File

@@ -0,0 +1,493 @@
import os
import requests
import json
from collections import deque
from urllib.parse import urlparse
from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from utils import msgbox_frame, HostsManager
from data.config import APP_NAME, PLUGIN, GAME_INFO, UA, CONFIG_URL
from workers import IpOptimizerThread
class DownloadManager:
def __init__(self, main_window):
"""初始化下载管理器
Args:
main_window: 主窗口实例用于访问UI和状态
"""
self.main_window = main_window
self.selected_folder = ""
self.download_queue = deque()
self.current_download_thread = None
self.hosts_manager = HostsManager()
self.optimized_ip = None
self.optimization_done = False # 标记是否已执行过优选
self.optimizing_msg_box = None
def file_dialog(self):
"""显示文件夹选择对话框,选择游戏安装目录"""
self.selected_folder = QtWidgets.QFileDialog.getExistingDirectory(
self.main_window, f"选择游戏所在【上级目录】 {APP_NAME}"
)
if not self.selected_folder:
QtWidgets.QMessageBox.warning(
self.main_window, f"通知 - {APP_NAME}", "\n未选择任何目录,请重新选择\n"
)
return
self.download_action()
def get_install_paths(self):
"""获取所有游戏版本的安装路径"""
return {
game: os.path.join(self.selected_folder, info["install_path"])
for game, info in GAME_INFO.items()
}
def is_debug_mode(self):
"""检查是否处于调试模式"""
if hasattr(self.main_window, 'ui_manager') and self.main_window.ui_manager:
if hasattr(self.main_window.ui_manager, 'debug_action') and self.main_window.ui_manager.debug_action:
return self.main_window.ui_manager.debug_action.isChecked()
return False
def get_download_url(self) -> dict:
"""获取所有游戏版本的下载链接
Returns:
dict: 包含游戏版本和下载URL的字典
"""
try:
if self.main_window.cloud_config:
if self.is_debug_mode():
print("--- Using pre-fetched cloud config ---")
config_data = self.main_window.cloud_config
else:
# 如果没有预加载的配置,则同步获取
headers = {"User-Agent": UA}
response = requests.get(CONFIG_URL, headers=headers, timeout=10)
response.raise_for_status()
config_data = response.json()
if not config_data:
raise ValueError("未能获取或解析配置数据")
if self.is_debug_mode():
print(f"DEBUG: Parsed JSON data: {json.dumps(config_data, indent=2)}")
# 统一处理URL提取确保返回扁平化的字典
urls = {}
for i in range(4):
key = f"vol.{i+1}.data"
if key in config_data and "url" in config_data[key]:
urls[f"vol{i+1}"] = config_data[key]["url"]
if "after.data" in config_data and "url" in config_data["after.data"]:
urls["after"] = config_data["after.data"]["url"]
# 检查是否成功提取了所有URL
if len(urls) != 5:
missing_keys_map = {
f"vol{i+1}": f"vol.{i+1}.data" for i in range(4)
}
missing_keys_map["after"] = "after.data"
extracted_keys = set(urls.keys())
all_keys = set(missing_keys_map.keys())
missing_simple_keys = all_keys - extracted_keys
missing_original_keys = [missing_keys_map[k] for k in missing_simple_keys]
raise ValueError(f"配置文件缺少必要的键: {', '.join(missing_original_keys)}")
if self.is_debug_mode():
print(f"DEBUG: Extracted URLs: {urls}")
print("--- Finished getting download URL successfully ---")
return urls
except requests.exceptions.RequestException as e:
status_code = e.response.status_code if e.response is not None else "未知"
try:
error_response = e.response.json() if e.response else {}
json_title = error_response.get("title", "无错误类型")
json_message = error_response.get("message", "无附加错误信息")
except (ValueError, AttributeError):
json_title = "配置文件异常,无法解析错误类型"
json_message = "配置文件异常,无法解析错误信息"
if self.is_debug_mode():
print(f"ERROR: Failed to get download config due to RequestException: {e}")
QtWidgets.QMessageBox.critical(
self.main_window,
f"错误 - {APP_NAME}",
f"\n下载配置获取失败\n\n【HTTP状态】{status_code}\n【错误类型】:{json_title}\n【错误信息】:{json_message}\n",
)
return {}
except ValueError as e:
if self.is_debug_mode():
print(f"ERROR: Failed to parse download config due to ValueError: {e}")
QtWidgets.QMessageBox.critical(
self.main_window,
f"错误 - {APP_NAME}",
f"\n配置文件格式异常\n\n【错误信息】:{e}\n",
)
return {}
def download_action(self):
"""开始下载流程"""
# 禁用开始安装按钮
self.main_window.ui.start_install_btn.setEnabled(False)
# 显示哈希检查窗口
self.main_window.hash_msg_box = self.main_window.hash_manager.hash_pop_window()
# 执行预检查
install_paths = self.get_install_paths()
self.main_window.hash_thread = self.main_window.create_hash_thread("pre", install_paths)
self.main_window.hash_thread.pre_finished.connect(self.on_pre_hash_finished)
self.main_window.hash_thread.start()
def on_pre_hash_finished(self, updated_status):
"""哈希预检查完成后的处理
Args:
updated_status: 更新后的安装状态
"""
self.main_window.installed_status = updated_status
if self.main_window.hash_msg_box and self.main_window.hash_msg_box.isVisible():
self.main_window.hash_msg_box.accept()
self.main_window.hash_msg_box = None
# 获取下载配置
config = self.get_download_url()
if not config:
QtWidgets.QMessageBox.critical(
self.main_window, f"错误 - {APP_NAME}", "\n网络状态异常或服务器故障,请重试\n"
)
# 重新启用开始安装按钮
self.main_window.ui.start_install_btn.setEnabled(True)
return
# 填充下载队列
self._fill_download_queue(config)
# 如果没有需要下载的内容,直接进行最终校验
if not self.download_queue:
self.main_window.after_hash_compare()
return
# 只有当有需要下载内容时才询问是否使用Cloudflare加速
# 询问用户是否使用Cloudflare加速
msg_box = QtWidgets.QMessageBox(self.main_window)
msg_box.setWindowTitle(f"下载优化 - {APP_NAME}")
msg_box.setText("是否愿意通过Cloudflare加速来优化下载速度\n\n这将临时修改系统的hosts文件并需要管理员权限。")
msg_box.setIcon(QtWidgets.QMessageBox.Icon.Question)
yes_button = msg_box.addButton("是,开启加速", QtWidgets.QMessageBox.ButtonRole.YesRole)
no_button = msg_box.addButton("否,直接下载", QtWidgets.QMessageBox.ButtonRole.NoRole)
msg_box.exec()
use_optimization = msg_box.clickedButton() == yes_button
if use_optimization and not self.optimization_done:
first_url = self.download_queue[0][0]
self._start_ip_optimization(first_url)
else:
# 如果用户选择不优化,或已经优化过,直接开始下载
self.next_download_task()
def _fill_download_queue(self, config):
"""填充下载队列
Args:
config: 包含下载URL的配置字典
"""
# 清空现有队列
self.download_queue.clear()
# 添加nekopara 1-4
for i in range(1, 5):
game_version = f"NEKOPARA Vol.{i}"
if not self.main_window.installed_status.get(game_version, False):
url = config.get(f"vol{i}")
if not url: continue
game_folder = os.path.join(self.selected_folder, f"NEKOPARA Vol. {i}")
_7z_path = os.path.join(PLUGIN, f"vol.{i}.7z")
plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))
# 添加nekopara after
game_version = "NEKOPARA After"
if not self.main_window.installed_status.get(game_version, False):
url = config.get("after")
if url:
game_folder = os.path.join(self.selected_folder, "NEKOPARA After")
_7z_path = os.path.join(PLUGIN, "after.7z")
plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))
def _start_ip_optimization(self, url):
"""开始IP优化过程
Args:
url: 用于优化的URL
"""
# 禁用退出按钮
self.main_window.ui.exit_btn.setEnabled(False)
self.optimizing_msg_box = msgbox_frame(
f"通知 - {APP_NAME}",
"\n正在优选Cloudflare IP请稍候...\n\n这可能需要5-10分钟请耐心等待喵~"
)
# 我们不再提供"跳过"按钮
self.optimizing_msg_box.setStandardButtons(QtWidgets.QMessageBox.StandardButton.NoButton)
self.optimizing_msg_box.setWindowModality(Qt.WindowModality.ApplicationModal)
self.optimizing_msg_box.open()
# 创建并启动优化线程
self.ip_optimizer_thread = IpOptimizerThread(url)
self.ip_optimizer_thread.finished.connect(self.on_optimization_finished)
self.ip_optimizer_thread.start()
def on_optimization_finished(self, ip):
"""IP优化完成后的处理
Args:
ip: 优选的IP地址如果失败则为空字符串
"""
self.optimized_ip = ip
self.optimization_done = True
# 关闭提示框
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
# 显示优选结果
if not ip:
QtWidgets.QMessageBox.warning(
self.main_window,
f"优选失败 - {APP_NAME}",
"\n未能找到合适的Cloudflare IP将使用默认网络进行下载。\n"
)
else:
# 应用优选IP到hosts文件
if self.download_queue:
first_url = self.download_queue[0][0]
hostname = urlparse(first_url).hostname
# 先清理可能存在的旧记录
self.hosts_manager.clean_hostname_entries(hostname)
if self.hosts_manager.apply_ip(hostname, ip):
QtWidgets.QMessageBox.information(
self.main_window,
f"成功 - {APP_NAME}",
f"\n已将优选IP ({ip}) 应用到hosts文件。\n"
)
else:
QtWidgets.QMessageBox.critical(
self.main_window,
f"错误 - {APP_NAME}",
"\n修改hosts文件失败请检查程序是否以管理员权限运行。\n"
)
# 开始下载
self.next_download_task()
def next_download_task(self):
"""处理下载队列中的下一个任务"""
if not self.download_queue:
self.main_window.after_hash_compare()
return
# 检查下载线程是否仍在运行,以避免在手动停止后立即开始下一个任务
if self.current_download_thread and self.current_download_thread.isRunning():
return
# 获取下一个下载任务并开始
url, game_folder, game_version, _7z_path, plugin_path = self.download_queue.popleft()
self.download_setting(url, game_folder, game_version, _7z_path, plugin_path)
def download_setting(self, url, game_folder, game_version, _7z_path, plugin_path):
"""准备下载特定游戏版本
Args:
url: 下载URL
game_folder: 游戏文件夹路径
game_version: 游戏版本名称
_7z_path: 7z文件保存路径
plugin_path: 插件路径
"""
game_exe = {
game: os.path.join(
self.selected_folder, info["install_path"].split("/")[0], info["exe"]
)
for game, info in GAME_INFO.items()
}
# 检查游戏是否已安装
if (
game_version not in game_exe
or not os.path.exists(game_exe[game_version])
or self.main_window.installed_status[game_version]
):
self.main_window.installed_status[game_version] = False
self.main_window.show_result()
return
# 创建进度窗口并开始下载
self.main_window.progress_window = self.main_window.create_progress_window()
self.start_download(url, _7z_path, game_version, game_folder, plugin_path)
def start_download(self, url, _7z_path, game_version, game_folder, plugin_path):
"""启动下载线程
Args:
url: 下载URL
_7z_path: 7z文件保存路径
game_version: 游戏版本名称
game_folder: 游戏文件夹路径
plugin_path: 插件路径
"""
# 禁用退出按钮
self.main_window.ui.exit_btn.setEnabled(False)
if self.optimized_ip:
print(f"已为 {game_version} 获取到优选IP: {self.optimized_ip}")
else:
print(f"未能为 {game_version} 获取优选IP将使用默认线路。")
# 创建并连接下载线程
self.current_download_thread = self.main_window.create_download_thread(url, _7z_path, game_version)
self.current_download_thread.progress.connect(self.main_window.progress_window.update_progress)
self.current_download_thread.finished.connect(
lambda success, error: self.on_download_finished(
success,
error,
url,
game_folder,
game_version,
_7z_path,
plugin_path,
)
)
# 连接停止按钮
self.main_window.progress_window.stop_button.clicked.connect(self.current_download_thread.stop)
# 启动线程和显示进度窗口
self.current_download_thread.start()
self.main_window.progress_window.exec()
def on_download_finished(self, success, error, url, game_folder, game_version, _7z_path, plugin_path):
"""下载完成后的处理
Args:
success: 是否下载成功
error: 错误信息
url: 下载URL
game_folder: 游戏文件夹路径
game_version: 游戏版本名称
_7z_path: 7z文件保存路径
plugin_path: 插件路径
"""
# 关闭进度窗口
if self.main_window.progress_window.isVisible():
self.main_window.progress_window.reject()
# 处理下载失败
if not success:
print(f"--- Download Failed: {game_version} ---")
print(error)
print("------------------------------------")
msg_box = QtWidgets.QMessageBox(self.main_window)
msg_box.setWindowTitle(f"下载失败 - {APP_NAME}")
msg_box.setText(f"\n文件获取失败: {game_version}\n错误: {error}\n\n是否重试?")
retry_button = msg_box.addButton("重试", QtWidgets.QMessageBox.ButtonRole.YesRole)
next_button = msg_box.addButton("下一个", QtWidgets.QMessageBox.ButtonRole.NoRole)
end_button = msg_box.addButton("结束", QtWidgets.QMessageBox.ButtonRole.RejectRole)
msg_box.exec()
clicked_button = msg_box.clickedButton()
# 处理用户选择
if clicked_button == retry_button:
self.download_setting(url, game_folder, game_version, _7z_path, plugin_path)
elif clicked_button == next_button:
self.next_download_task()
else:
self.on_download_stopped()
return
# 下载成功,开始解压缩
self.main_window.hash_msg_box = self.main_window.hash_manager.hash_pop_window()
# 创建并启动解压线程
self.main_window.extraction_thread = self.main_window.create_extraction_thread(
_7z_path, game_folder, plugin_path, game_version
)
self.main_window.extraction_thread.finished.connect(self.on_extraction_finished)
self.main_window.extraction_thread.start()
def on_extraction_finished(self, success, error_message, game_version):
"""解压完成后的处理
Args:
success: 是否解压成功
error_message: 错误信息
game_version: 游戏版本
"""
# 关闭哈希检查窗口
if self.main_window.hash_msg_box and self.main_window.hash_msg_box.isVisible():
self.main_window.hash_msg_box.close()
# 处理解压结果
if not success:
QtWidgets.QMessageBox.critical(self.main_window, f"错误 - {APP_NAME}", error_message)
self.main_window.installed_status[game_version] = False
else:
self.main_window.installed_status[game_version] = True
# 继续下一个下载任务
self.next_download_task()
def on_download_stopped(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, 'optimizing_msg_box') and self.optimizing_msg_box:
if self.optimizing_msg_box.isVisible():
self.optimizing_msg_box.accept()
self.optimizing_msg_box = None
# 停止当前可能仍在运行的下载线程
if self.current_download_thread and self.current_download_thread.isRunning():
self.current_download_thread.stop()
self.current_download_thread.wait() # 等待线程完全终止
# 清空下载队列,因为用户决定停止
self.download_queue.clear()
# 确保进度窗口已关闭
if hasattr(self.main_window, 'progress_window') and self.main_window.progress_window.isVisible():
self.main_window.progress_window.reject()
# 可以在这里决定是否立即进行哈希比较或显示结果
print("下载已全部停止。")
self.main_window.setEnabled(True) # 恢复主窗口交互
# 重新启用退出按钮和开始安装按钮
self.main_window.ui.exit_btn.setEnabled(True)
self.main_window.ui.start_install_btn.setEnabled(True)
self.main_window.show_result()

94
source/core/ui_manager.py Normal file
View File

@@ -0,0 +1,94 @@
from PySide6.QtGui import QIcon, QAction
from PySide6.QtWidgets import QMessageBox, QMainWindow
from PySide6.QtCore import Qt
import webbrowser
from utils import load_base64_image, msgbox_frame
from data.config import APP_NAME, APP_VERSION
from data.pic_data import img_data
class UIManager:
def __init__(self, main_window):
"""初始化UI管理器
Args:
main_window: 主窗口实例用于设置UI元素
"""
self.main_window = main_window
# 使用getattr获取ui属性如果不存在则为None
self.ui = getattr(main_window, 'ui', None)
self.debug_action = None
def setup_ui(self):
"""设置UI元素包括窗口图标、标题和菜单"""
# 设置窗口图标
icon_data = img_data.get("icon")
if icon_data:
pixmap = load_base64_image(icon_data)
self.main_window.setWindowIcon(QIcon(pixmap))
# 设置窗口标题
self.main_window.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
# 设置菜单
self._setup_help_menu()
self._setup_settings_menu()
def _setup_help_menu(self):
"""设置"帮助"菜单"""
if not self.ui or not hasattr(self.ui, 'menu_2'):
return
project_home_action = QAction("项目主页", self.main_window)
project_home_action.triggered.connect(self.open_project_home_page)
about_action = QAction("关于", self.main_window)
about_action.triggered.connect(self.show_about_dialog)
self.ui.menu_2.addAction(project_home_action)
self.ui.menu_2.addAction(about_action)
def _setup_settings_menu(self):
"""设置"设置"菜单"""
if not self.ui or not hasattr(self.ui, 'menu'):
return
self.debug_action = QAction("Debug模式", self.main_window, checkable=True)
# 安全地获取config属性
config = getattr(self.main_window, 'config', {})
debug_mode = False
if isinstance(config, dict):
debug_mode = config.get("debug_mode", False)
self.debug_action.setChecked(debug_mode)
# 安全地连接toggle_debug_mode方法
if hasattr(self.main_window, 'toggle_debug_mode'):
self.debug_action.triggered.connect(self.main_window.toggle_debug_mode)
self.ui.menu.addAction(self.debug_action)
# 为未来功能预留的"切换下载源"按钮
self.switch_source_action = QAction("切换下载源", self.main_window)
self.switch_source_action.setEnabled(False) # 暂时禁用
self.ui.menu.addAction(self.switch_source_action)
def open_project_home_page(self):
"""打开项目主页"""
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT")
def show_about_dialog(self):
"""显示关于对话框"""
about_text = f"""
<p><b>{APP_NAME} v{APP_VERSION}</b></p>
<p>原作: <a href="https://github.com/Yanam1Anna">Yanam1Anna</a></p>
<p>此应用根据 <a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/LICENSE">GPL-3.0 许可证</a> 授权。</p>
"""
msg_box = msgbox_frame(
f"关于 - {APP_NAME}",
about_text,
QMessageBox.StandardButton.Ok,
)
msg_box.setTextFormat(Qt.TextFormat.RichText) # 使用Qt.TextFormat
msg_box.exec()

View File

@@ -1,167 +1,27 @@
import os import os
import sys import sys
import shutil import shutil
import webbrowser
import requests
import py7zr
import json import json
from urllib.parse import urlparse
from collections import deque
from PySide6 import QtWidgets
from PySide6.QtCore import QTimer, Qt, QThread, Signal
from PySide6.QtGui import QIcon, QAction
from PySide6.QtWidgets import QMainWindow, QFileDialog, QApplication, QMessageBox, QPushButton
from Ui_install import Ui_MainWindows from PySide6 import QtWidgets
from animations import MultiStageAnimations from PySide6.QtCore import QTimer
from config import ( from PySide6.QtWidgets import QMainWindow, QMessageBox
APP_NAME, APP_VERSION, PLUGIN, GAME_INFO, BLOCK_SIZE,
from ui.Ui_install import Ui_MainWindows
from data.config import (
APP_NAME, PLUGIN, GAME_INFO, BLOCK_SIZE,
PLUGIN_HASH, UA, CONFIG_URL, LOG_FILE PLUGIN_HASH, UA, CONFIG_URL, LOG_FILE
) )
from utils import ( from utils import (
load_base64_image, HashManager, AdminPrivileges, msgbox_frame, load_config, save_config, HashManager, AdminPrivileges, msgbox_frame
load_config, save_config, HostsManager, censor_url )
from workers import (
DownloadThread, ProgressWindow, IpOptimizerThread,
HashThread, ExtractionThread, ConfigFetchThread
)
from core import (
MultiStageAnimations, UIManager, DownloadManager, DebugManager
) )
from download import DownloadThread, ProgressWindow
from ip_optimizer import IpOptimizer
from pic_data import img_data
class Logger:
def __init__(self, filename, stream):
self.terminal = stream
self.log = open(filename, "w", encoding="utf-8")
def write(self, message):
censored_message = censor_url(message)
self.terminal.write(censored_message)
self.log.write(censored_message)
self.flush()
def flush(self):
self.terminal.flush()
self.log.flush()
def close(self):
self.log.close()
class IpOptimizerThread(QThread):
finished = Signal(str)
def __init__(self, url, parent=None):
super().__init__(parent)
self.url = url
self.optimizer = IpOptimizer()
def run(self):
optimal_ip = self.optimizer.get_optimal_ip(self.url)
self.finished.emit(optimal_ip if optimal_ip else "")
def stop(self):
self.optimizer.stop()
class HashThread(QThread):
pre_finished = Signal(dict)
after_finished = Signal(dict)
def __init__(self, mode, install_paths, plugin_hash, installed_status, parent=None):
super().__init__(parent)
self.mode = mode
self.install_paths = install_paths
self.plugin_hash = plugin_hash
self.installed_status = installed_status
# 每个线程都应该有自己的HashManager实例
self.hash_manager = HashManager(BLOCK_SIZE)
def run(self):
if self.mode == "pre":
updated_status = self.hash_manager.cfg_pre_hash_compare(
self.install_paths, self.plugin_hash, self.installed_status
)
self.pre_finished.emit(updated_status)
elif self.mode == "after":
result = self.hash_manager.cfg_after_hash_compare(
self.install_paths, self.plugin_hash, self.installed_status
)
self.after_finished.emit(result)
class ExtractionThread(QThread):
finished = Signal(bool, str, str) # success, error_message, game_version
def __init__(self, _7z_path, game_folder, plugin_path, game_version, parent=None):
super().__init__(parent)
self._7z_path = _7z_path
self.game_folder = game_folder
self.plugin_path = plugin_path
self.game_version = game_version
def run(self):
try:
with py7zr.SevenZipFile(self._7z_path, mode="r") as archive:
archive.extractall(path=PLUGIN)
os.makedirs(self.game_folder, exist_ok=True)
shutil.copy(self.plugin_path, self.game_folder)
if self.game_version == "NEKOPARA After":
sig_path = os.path.join(PLUGIN, GAME_INFO[self.game_version]["sig_path"])
shutil.copy(sig_path, self.game_folder)
self.finished.emit(True, "", self.game_version)
except (py7zr.Bad7zFile, FileNotFoundError, Exception) as e:
self.finished.emit(False, f"\n文件操作失败,请重试\n\n【错误信息】:{e}\n", self.game_version)
class ConfigFetchThread(QThread):
finished = Signal(object, str) # data, error_message
def __init__(self, url, headers, debug_mode=False, parent=None):
super().__init__(parent)
self.url = url
self.headers = headers
self.debug_mode = debug_mode
def run(self):
try:
if self.debug_mode:
print("--- Starting to fetch cloud config ---")
print(f"DEBUG: Requesting URL: {self.url}")
print(f"DEBUG: Using Headers: {self.headers}")
response = requests.get(self.url, headers=self.headers, timeout=10)
if self.debug_mode:
print(f"DEBUG: Response Status Code: {response.status_code}")
print(f"DEBUG: Response Headers: {response.headers}")
print(f"DEBUG: Response Text: {response.text}")
response.raise_for_status()
# 首先总是尝试解析JSON
config_data = response.json()
# 检查是否是要求更新的错误信息
if config_data.get("message") == "请使用最新版本的FRAISEMOE Addons Installer NEXT进行下载安装":
self.finished.emit(None, "update_required")
return
# 检查是否是有效的配置文件
required_keys = [f"vol.{i+1}.data" for i in range(4)] + ["after.data"]
missing_keys = [key for key in required_keys if key not in config_data]
if missing_keys:
self.finished.emit(None, f"missing_keys:{','.join(missing_keys)}")
return
self.finished.emit(config_data, "")
except requests.exceptions.RequestException as e:
self.finished.emit(None, f"网络请求失败: {e}")
except (ValueError, json.JSONDecodeError) as e:
self.finished.emit(None, f"JSON解析失败: {e}")
finally:
if self.debug_mode:
print("--- Finished fetching cloud config ---")
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
def __init__(self): def __init__(self):
@@ -170,45 +30,42 @@ class MainWindow(QMainWindow):
# 初始化UI (从Ui_install.py导入) # 初始化UI (从Ui_install.py导入)
self.ui = Ui_MainWindows() self.ui = Ui_MainWindows()
self.ui.setupUi(self) self.ui.setupUi(self)
icon_data = img_data.get("icon")
if icon_data:
pixmap = load_base64_image(icon_data)
self.setWindowIcon(QIcon(pixmap))
# 设置窗口标题为APP_NAME加版本号
self.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
# 初始化动画系统 (从animations.py导入) # 初始化配置
self.animator = MultiStageAnimations(self.ui, self)
# 初始化功能变量
self.selected_folder = ""
self.installed_status = {f"NEKOPARA Vol.{i}": False for i in range(1, 5)}
self.installed_status["NEKOPARA After"] = False # 添加After的状态
self.download_queue = deque()
self.current_download_thread = None
self.hash_manager = HashManager(BLOCK_SIZE)
self.hash_thread = None
self.extraction_thread = None
self.hash_msg_box = None
self.optimized_ip = None
self.optimization_done = False # 标记是否已执行过优选
self.logger = None
self.hosts_manager = HostsManager() # 实例化HostsManager
self.cloud_config = None
self.config_fetch_thread = None
# 加载配置
self.config = load_config() self.config = load_config()
# 初始化状态变量
self.cloud_config = None
self.installed_status = {f"NEKOPARA Vol.{i}": False for i in range(1, 5)}
self.installed_status["NEKOPARA After"] = False # 添加After的状态
self.hash_msg_box = None
self.progress_window = None
# 初始化工具类
self.hash_manager = HashManager(BLOCK_SIZE)
self.admin_privileges = AdminPrivileges()
# 初始化管理器
self.animator = MultiStageAnimations(self.ui, self)
self.ui_manager = UIManager(self)
# 首先设置UI - 确保debug_action已初始化
self.ui_manager.setup_ui()
self.debug_manager = DebugManager(self)
self.download_manager = DownloadManager(self)
# 设置退出按钮和开始安装按钮的样式表,使其在禁用状态下不会变灰
button_style = "QPushButton:disabled { opacity: 1.0; }"
self.ui.exit_btn.setStyleSheet(button_style)
self.ui.start_install_btn.setStyleSheet(button_style)
# 检查管理员权限和进程 # 检查管理员权限和进程
admin_privileges = AdminPrivileges() self.admin_privileges.request_admin_privileges()
admin_privileges.request_admin_privileges() self.admin_privileges.check_and_terminate_processes()
admin_privileges.check_and_terminate_processes()
# 备份hosts文件 # 备份hosts文件
self.hosts_manager.backup() self.download_manager.hosts_manager.backup()
# 创建缓存目录 # 创建缓存目录
if not os.path.exists(PLUGIN): if not os.path.exists(PLUGIN):
@@ -222,32 +79,14 @@ class MainWindow(QMainWindow):
) )
sys.exit(1) sys.exit(1)
# 连接信号 (使用Ui_install.py中的组件名称) # 连接信号
self.ui.start_install_btn.clicked.connect(self.file_dialog) self.ui.start_install_btn.clicked.connect(self.download_manager.file_dialog)
self.ui.exit_btn.clicked.connect(self.shutdown_app) self.ui.exit_btn.clicked.connect(self.shutdown_app)
# “帮助”菜单
project_home_action = QAction("项目主页", self)
project_home_action.triggered.connect(self.open_project_home_page)
about_action = QAction("关于", self)
about_action.triggered.connect(self.show_about_dialog)
self.ui.menu_2.addAction(project_home_action)
self.ui.menu_2.addAction(about_action)
# “设置”菜单
self.debug_action = QAction("Debug模式", self, checkable=True)
self.debug_action.setChecked(self.config.get("debug_mode", False))
self.debug_action.triggered.connect(self.toggle_debug_mode)
self.ui.menu.addAction(self.debug_action)
# 为未来功能预留的“切换下载源”按钮
self.switch_source_action = QAction("切换下载源", self)
self.switch_source_action.setEnabled(False) # 暂时禁用
self.ui.menu.addAction(self.switch_source_action)
# 根据初始配置决定是否开启Debug模式 # 根据初始配置决定是否开启Debug模式
if self.debug_action.isChecked(): if hasattr(self.ui_manager, 'debug_action') and self.ui_manager.debug_action:
self.start_logging() if self.ui_manager.debug_action.isChecked():
self.debug_manager.start_logging()
# 在窗口显示前设置初始状态 # 在窗口显示前设置初始状态
self.animator.initialize() self.animator.initialize()
@@ -256,437 +95,159 @@ class MainWindow(QMainWindow):
QTimer.singleShot(100, self.start_animations) QTimer.singleShot(100, self.start_animations)
def start_animations(self): def start_animations(self):
self.ui.exit_btn.setEnabled(False) """开始启动动画"""
# 不再禁用退出按钮的交互性,只通过样式表控制外观
# 但仍然需要跟踪动画状态,防止用户在动画播放过程中退出
self.animation_in_progress = True
# 禁用开始安装按钮,防止在动画播放期间点击
self.ui.start_install_btn.setEnabled(False)
self.animator.animation_finished.connect(self.on_animations_finished) self.animator.animation_finished.connect(self.on_animations_finished)
self.animator.start_animations() self.animator.start_animations()
self.fetch_cloud_config() self.fetch_cloud_config()
def on_animations_finished(self): def on_animations_finished(self):
self.ui.exit_btn.setEnabled(True) """动画完成后启用按钮"""
self.animation_in_progress = False
# 启用开始安装按钮
self.ui.start_install_btn.setEnabled(True)
def fetch_cloud_config(self): def fetch_cloud_config(self):
"""获取云端配置"""
headers = {"User-Agent": UA} headers = {"User-Agent": UA}
debug_mode = self.debug_action.isChecked() debug_mode = self.ui_manager.debug_action.isChecked() if self.ui_manager.debug_action else False
self.config_fetch_thread = ConfigFetchThread(CONFIG_URL, headers, debug_mode, self) self.config_fetch_thread = ConfigFetchThread(CONFIG_URL, headers, debug_mode, self)
self.config_fetch_thread.finished.connect(self.on_config_fetched) self.config_fetch_thread.finished.connect(self.on_config_fetched)
self.config_fetch_thread.start() self.config_fetch_thread.start()
def on_config_fetched(self, data, error_message): def on_config_fetched(self, data, error_message):
"""云端配置获取完成的回调处理
Args:
data: 获取到的配置数据
error_message: 错误信息,如果有
"""
if error_message: if error_message:
if error_message == "update_required": if error_message == "update_required":
msg_box = msgbox_frame( msg_box = msgbox_frame(
f"更新提示 - {APP_NAME}", f"更新提示 - {APP_NAME}",
"\n当前版本过低,请及时更新。\n", "\n当前版本过低,请及时更新。\n",
QtWidgets.QMessageBox.StandardButton.Ok, QMessageBox.StandardButton.Ok,
) )
msg_box.exec() msg_box.exec()
self.open_project_home_page() self.ui_manager.open_project_home_page()
self.shutdown_app(force_exit=True) self.shutdown_app(force_exit=True)
elif "missing_keys" in error_message: elif "missing_keys" in error_message:
missing_versions = error_message.split(":")[1] missing_versions = error_message.split(":")[1]
msg_box = msgbox_frame( msg_box = msgbox_frame(
f"配置缺失 - {APP_NAME}", f"配置缺失 - {APP_NAME}",
f'\n云端缺失下载链接,可能云服务器正在维护,不影响其他版本下载。\n当前缺失版本:"{missing_versions}"\n', f'\n云端缺失下载链接,可能云服务器正在维护,不影响其他版本下载。\n当前缺失版本:"{missing_versions}"\n',
QtWidgets.QMessageBox.StandardButton.Ok, QMessageBox.StandardButton.Ok,
) )
msg_box.exec() msg_box.exec()
else: else:
# 其他错误暂时只在debug模式下打印 # 其他错误暂时只在debug模式下打印
if self.debug_action.isChecked(): debug_mode = self.ui_manager.debug_action.isChecked() if self.ui_manager.debug_action else False
if debug_mode:
print(f"获取云端配置失败: {error_message}") print(f"获取云端配置失败: {error_message}")
else: else:
self.cloud_config = data self.cloud_config = data
if self.debug_action.isChecked(): debug_mode = self.ui_manager.debug_action.isChecked() if self.ui_manager.debug_action else False
if debug_mode:
print("--- Cloud config fetched successfully ---") print("--- Cloud config fetched successfully ---")
print(json.dumps(data, indent=2)) print(json.dumps(data, indent=2))
def toggle_debug_mode(self, checked): def toggle_debug_mode(self, checked):
self.config["debug_mode"] = checked """切换调试模式
save_config(self.config)
if checked:
self.start_logging()
else:
self.stop_logging()
def start_logging(self):
if self.logger is None:
try:
if os.path.exists(LOG_FILE):
os.remove(LOG_FILE)
# 保存原始的 stdout 和 stderr
self.original_stdout = sys.stdout
self.original_stderr = sys.stderr
# 创建 Logger 实例
self.logger = Logger(LOG_FILE, self.original_stdout)
sys.stdout = self.logger
sys.stderr = self.logger
print("--- Debug mode enabled ---")
except (IOError, OSError) as e:
QtWidgets.QMessageBox.critical(self, "错误", f"无法创建日志文件: {e}")
self.logger = None
def stop_logging(self):
if self.logger:
print("--- Debug mode disabled ---")
sys.stdout = self.original_stdout
sys.stderr = self.original_stderr
self.logger.close()
self.logger = None
def get_install_paths(self): Args:
return { checked: 是否启用调试模式
game: os.path.join(self.selected_folder, info["install_path"]) """
for game, info in GAME_INFO.items() self.debug_manager.toggle_debug_mode(checked)
}
def save_config(self, config):
def file_dialog(self): """保存配置的便捷方法"""
self.selected_folder = QFileDialog.getExistingDirectory( save_config(config)
self, f"选择游戏所在【上级目录】 {APP_NAME}"
) def create_download_thread(self, url, _7z_path, game_version):
if not self.selected_folder: """创建下载线程
QtWidgets.QMessageBox.warning(
self, f"通知 - {APP_NAME}", "\n未选择任何目录,请重新选择\n" Args:
) url: 下载URL
return _7z_path: 7z文件保存路径
self.download_action() game_version: 游戏版本
def get_download_url(self) -> dict:
try:
if self.cloud_config:
if self.debug_action.isChecked():
print("--- Using pre-fetched cloud config ---")
config_data = self.cloud_config
else:
# 如果没有预加载的配置,则同步获取
headers = {"User-Agent": UA}
response = requests.get(CONFIG_URL, headers=headers, timeout=10)
response.raise_for_status()
config_data = response.json()
if not config_data:
raise ValueError("未能获取或解析配置数据")
if self.debug_action.isChecked():
print(f"DEBUG: Parsed JSON data: {json.dumps(config_data, indent=2)}")
# 统一处理URL提取确保返回扁平化的字典
urls = {}
for i in range(4):
key = f"vol.{i+1}.data"
if key in config_data and "url" in config_data[key]:
urls[f"vol{i+1}"] = config_data[key]["url"]
if "after.data" in config_data and "url" in config_data["after.data"]: Returns:
urls["after"] = config_data["after.data"]["url"] DownloadThread: 下载线程实例
"""
# 检查是否成功提取了所有URL return DownloadThread(url, _7z_path, game_version, self)
if len(urls) != 5:
missing_keys_map = { def create_progress_window(self):
f"vol{i+1}": f"vol.{i+1}.data" for i in range(4) """创建下载进度窗口
}
missing_keys_map["after"] = "after.data" Returns:
ProgressWindow: 进度窗口实例
extracted_keys = set(urls.keys()) """
all_keys = set(missing_keys_map.keys()) return ProgressWindow(self)
missing_simple_keys = all_keys - extracted_keys
def create_hash_thread(self, mode, install_paths):
missing_original_keys = [missing_keys_map[k] for k in missing_simple_keys] """创建哈希检查线程
raise ValueError(f"配置文件缺少必要的键: {', '.join(missing_original_keys)}")
Args:
if self.debug_action.isChecked(): mode: 检查模式,"pre""after"
print(f"DEBUG: Extracted URLs: {urls}") install_paths: 安装路径字典
print("--- Finished getting download URL successfully ---")
return urls
if self.debug_action.isChecked():
print(f"DEBUG: Extracted URLs: {urls}")
print("--- Finished getting download URL successfully ---")
return urls
except requests.exceptions.RequestException as e:
status_code = e.response.status_code if e.response is not None else "未知"
try:
error_response = e.response.json() if e.response else {}
json_title = error_response.get("title", "无错误类型")
json_message = error_response.get("message", "无附加错误信息")
except (ValueError, AttributeError):
json_title = "配置文件异常,无法解析错误类型"
json_message = "配置文件异常,无法解析错误信息"
if self.debug_action.isChecked():
print(f"ERROR: Failed to get download config due to RequestException: {e}")
QtWidgets.QMessageBox.critical( Returns:
self, HashThread: 哈希检查线程实例
f"错误 - {APP_NAME}", """
f"\n下载配置获取失败\n\n【HTTP状态】{status_code}\n【错误类型】:{json_title}\n【错误信息】:{json_message}\n", return HashThread(mode, install_paths, PLUGIN_HASH, self.installed_status, self)
)
return {}
except ValueError as e:
if self.debug_action.isChecked():
print(f"ERROR: Failed to parse download config due to ValueError: {e}")
QtWidgets.QMessageBox.critical(
self,
f"错误 - {APP_NAME}",
f"\n配置文件格式异常\n\n【错误信息】:{e}\n",
)
return {}
def download_setting(self, url, game_folder, game_version, _7z_path, plugin_path):
game_exe = {
game: os.path.join(
self.selected_folder, info["install_path"].split("/")[0], info["exe"]
)
for game, info in GAME_INFO.items()
}
if ( def create_extraction_thread(self, _7z_path, game_folder, plugin_path, game_version):
game_version not in game_exe """创建解压线程
or not os.path.exists(game_exe[game_version])
or self.installed_status[game_version] Args:
): _7z_path: 7z文件路径
self.installed_status[game_version] = False game_folder: 游戏文件夹路径
self.show_result() plugin_path: 插件路径
return game_version: 游戏版本
self.progress_window = ProgressWindow(self) Returns:
self.start_download_with_ip(self.optimized_ip, url, _7z_path, game_version, game_folder, plugin_path) ExtractionThread: 解压线程实例
"""
return ExtractionThread(_7z_path, game_folder, plugin_path, game_version, self)
def on_optimization_and_hosts_finished(self, ip):
self.optimized_ip = ip
self.optimization_done = True
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
if not ip:
QtWidgets.QMessageBox.warning(self, f"优选失败 - {APP_NAME}", "\n未能找到合适的Cloudflare IP将使用默认网络进行下载。\n")
else:
if self.download_queue:
first_url = self.download_queue[0][0]
hostname = urlparse(first_url).hostname
if self.hosts_manager.apply_ip(hostname, ip):
QtWidgets.QMessageBox.information(self, f"成功 - {APP_NAME}", f"\n已将优选IP ({ip}) 应用到hosts文件。\n")
else:
QtWidgets.QMessageBox.critical(self, f"错误 - {APP_NAME}", "\n修改hosts文件失败请检查程序是否以管理员权限运行。\n")
self.next_download_task() def after_hash_compare(self):
"""进行安装后哈希比较"""
def start_download_with_ip(self, preferred_ip, url, _7z_path, game_version, game_folder, plugin_path): # 禁用退出按钮
if preferred_ip: self.ui.exit_btn.setEnabled(False)
print(f"已为 {game_version} 获取到优选IP: {preferred_ip}")
else:
print(f"未能为 {game_version} 获取优选IP将使用默认线路。")
self.current_download_thread = DownloadThread(url, _7z_path, game_version, self)
self.current_download_thread.progress.connect(self.progress_window.update_progress)
self.current_download_thread.finished.connect(
lambda success, error: self.install_setting(
success,
error,
self.progress_window,
url,
game_folder,
game_version,
_7z_path,
plugin_path,
)
)
self.progress_window.stop_button.clicked.connect(self.current_download_thread.stop)
self.current_download_thread.start()
self.progress_window.exec()
def install_setting(
self,
success,
error,
progress_window,
url,
game_folder,
game_version,
_7z_path,
plugin_path,
):
if progress_window.isVisible():
progress_window.reject()
if not success:
print(f"--- Download Failed: {game_version} ---")
print(error)
print("------------------------------------")
msg_box = QtWidgets.QMessageBox(self)
msg_box.setWindowTitle(f"下载失败 - {APP_NAME}")
msg_box.setText(f"\n文件获取失败: {game_version}\n错误: {error}\n\n是否重试?")
retry_button = msg_box.addButton("重试", QtWidgets.QMessageBox.ButtonRole.YesRole)
next_button = msg_box.addButton("下一个", QtWidgets.QMessageBox.ButtonRole.NoRole)
end_button = msg_box.addButton("结束", QtWidgets.QMessageBox.ButtonRole.RejectRole)
msg_box.exec()
clicked_button = msg_box.clickedButton()
if clicked_button == retry_button:
self.download_setting(url, game_folder, game_version, _7z_path, plugin_path)
elif clicked_button == next_button:
self.next_download_task()
else:
self.on_download_stopped()
return
# --- Start Extraction in a new thread ---
self.hash_msg_box = self.hash_manager.hash_pop_window()
self.extraction_thread = ExtractionThread(_7z_path, game_folder, plugin_path, game_version, self)
self.extraction_thread.finished.connect(self.on_extraction_finished)
self.extraction_thread.start()
def on_extraction_finished(self, success, error_message, game_version):
if self.hash_msg_box and self.hash_msg_box.isVisible():
self.hash_msg_box.close()
if not success:
QtWidgets.QMessageBox.critical(self, f"错误 - {APP_NAME}", error_message)
self.installed_status[game_version] = False
else:
self.installed_status[game_version] = True
self.next_download_task()
def download_action(self):
# 询问用户是否使用Cloudflare加速
msg_box = QMessageBox(self)
msg_box.setWindowTitle(f"下载优化 - {APP_NAME}")
msg_box.setText("是否愿意通过Cloudflare加速来优化下载速度\n\n这将临时修改系统的hosts文件并需要管理员权限。")
msg_box.setIcon(QMessageBox.Icon.Question)
yes_button = msg_box.addButton("是,开启加速", QMessageBox.ButtonRole.YesRole)
no_button = msg_box.addButton("否,直接下载", QMessageBox.ButtonRole.NoRole)
msg_box.exec()
use_optimization = msg_box.clickedButton() == yes_button
self.hash_msg_box = self.hash_manager.hash_pop_window() self.hash_msg_box = self.hash_manager.hash_pop_window()
install_paths = self.get_install_paths() install_paths = self.download_manager.get_install_paths()
self.hash_thread = HashThread("pre", install_paths, PLUGIN_HASH, self.installed_status, self) self.hash_thread = self.create_hash_thread("after", install_paths)
# 将用户选择传递给哈希完成后的处理函数
self.hash_thread.pre_finished.connect(lambda status: self.on_pre_hash_finished(status, use_optimization))
self.hash_thread.start()
def on_pre_hash_finished(self, updated_status, use_optimization):
self.installed_status = updated_status
if self.hash_msg_box and self.hash_msg_box.isVisible():
self.hash_msg_box.accept()
self.hash_msg_box = None
config = self.get_download_url()
if not config:
QtWidgets.QMessageBox.critical(
self, f"错误 - {APP_NAME}", "\n网络状态异常或服务器故障,请重试\n"
)
return
# --- 填充下载队列 ---
for i in range(1, 5):
game_version = f"NEKOPARA Vol.{i}"
if not self.installed_status.get(game_version, False):
url = config.get(f"vol{i}")
if not url: continue
game_folder = os.path.join(self.selected_folder, f"NEKOPARA Vol. {i}")
_7z_path = os.path.join(PLUGIN, f"vol.{i}.7z")
plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))
game_version = "NEKOPARA After"
if not self.installed_status.get(game_version, False):
url = config.get("after")
if url:
game_folder = os.path.join(self.selected_folder, "NEKOPARA After")
_7z_path = os.path.join(PLUGIN, "after.7z")
plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))
if not self.download_queue:
self.after_hash_compare(PLUGIN_HASH)
return
if use_optimization and not self.optimization_done:
first_url = self.download_queue[0][0]
self.optimizing_msg_box = msgbox_frame(
f"通知 - {APP_NAME}",
"\n正在优选Cloudflare IP请稍候...\n\n这可能需要5-10分钟请耐心等待喵~"
)
# 我们不再提供“跳过”按钮,因为用户已经做出了选择
self.optimizing_msg_box.setStandardButtons(QMessageBox.StandardButton.NoButton)
self.optimizing_msg_box.setWindowModality(Qt.WindowModality.ApplicationModal)
self.optimizing_msg_box.open()
self.ip_optimizer_thread = IpOptimizerThread(first_url)
# 优选完成后需要修改hosts并开始下载
self.ip_optimizer_thread.finished.connect(self.on_optimization_and_hosts_finished)
self.ip_optimizer_thread.start()
else:
# 如果用户选择不优化,或已经优化过,直接开始下载
self.next_download_task()
def next_download_task(self):
if not self.download_queue:
self.after_hash_compare(PLUGIN_HASH)
return
# 检查下载线程是否仍在运行,以避免在手动停止后立即开始下一个任务
if self.current_download_thread and self.current_download_thread.isRunning():
return
# 在开始下载前确保hosts文件已修改如果需要
# 这里的逻辑保持不变因为hosts文件应该在队列开始前就被修改了
url, game_folder, game_version, _7z_path, plugin_path = self.download_queue.popleft()
self.download_setting(url, game_folder, game_version, _7z_path, plugin_path)
def on_download_stopped(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, 'optimizing_msg_box') and self.optimizing_msg_box:
if self.optimizing_msg_box.isVisible():
self.optimizing_msg_box.accept()
self.optimizing_msg_box = None
# 停止当前可能仍在运行的下载线程
if self.current_download_thread and self.current_download_thread.isRunning():
self.current_download_thread.stop()
self.current_download_thread.wait() # 等待线程完全终止
# 清空下载队列,因为用户决定停止
self.download_queue.clear()
# 确保进度窗口已关闭
if hasattr(self, 'progress_window') and self.progress_window.isVisible():
self.progress_window.reject()
# 可以在这里决定是否立即进行哈希比较或显示结果
print("下载已全部停止。")
self.setEnabled(True) # 恢复主窗口交互
self.show_result()
def after_hash_compare(self, plugin_hash):
self.hash_msg_box = self.hash_manager.hash_pop_window()
install_paths = self.get_install_paths()
self.hash_thread = HashThread("after", install_paths, plugin_hash, self.installed_status, self)
self.hash_thread.after_finished.connect(self.on_after_hash_finished) self.hash_thread.after_finished.connect(self.on_after_hash_finished)
self.hash_thread.start() self.hash_thread.start()
def on_after_hash_finished(self, result): def on_after_hash_finished(self, result):
if self.hash_msg_box and self.hash_msg_box.isVisible(): """哈希比较完成后的处理
self.hash_msg_box.close()
Args:
result: 哈希比较结果
"""
# 确保哈希检查窗口关闭,无论是否还在显示
if self.hash_msg_box:
try:
if self.hash_msg_box.isVisible():
self.hash_msg_box.close()
else:
# 如果窗口已经不可见但没有关闭,也要尝试关闭
self.hash_msg_box.close()
except:
pass # 忽略任何关闭窗口时的错误
self.hash_msg_box = None
if not result["passed"]: if not result["passed"]:
game = result.get("game", "未知游戏") game = result.get("game", "未知游戏")
@@ -694,94 +255,95 @@ class MainWindow(QMainWindow):
msg_box = msgbox_frame( msg_box = msgbox_frame(
f"文件校验失败 - {APP_NAME}", f"文件校验失败 - {APP_NAME}",
message, message,
QtWidgets.QMessageBox.StandardButton.Ok, QMessageBox.StandardButton.Ok,
) )
msg_box.exec() msg_box.exec()
self.show_result() # 重新启用退出按钮和开始安装按钮
self.ui.exit_btn.setEnabled(True)
self.ui.start_install_btn.setEnabled(True)
# 添加短暂延迟确保UI更新
QTimer.singleShot(100, self.show_result)
def show_result(self): def show_result(self):
"""显示安装结果"""
installed_version = "\n".join( installed_version = "\n".join(
[i for i in self.installed_status if self.installed_status[i]] [i for i in self.installed_status if self.installed_status[i]]
) )
failed_ver = "\n".join( failed_ver = "\n".join(
[i for i in self.installed_status if not self.installed_status[i]] [i for i in self.installed_status if not self.installed_status[i]]
) )
QtWidgets.QMessageBox.information( QMessageBox.information(
self, self,
f"完成 - {APP_NAME}", f"完成 - {APP_NAME}",
f"\n安装结果:\n安装成功数:{len(installed_version.splitlines())} 安装失败数:{len(failed_ver.splitlines())}\n" f"\n安装结果:\n安装成功数:{len(installed_version.splitlines())} 安装失败数:{len(failed_ver.splitlines())}\n"
f"安装成功的版本:\n{installed_version}\n尚未持有或未使用本工具安装补丁的版本:\n{failed_ver}\n", f"安装成功的版本:\n{installed_version}\n尚未持有或未使用本工具安装补丁的版本:\n{failed_ver}\n",
) )
def show_about_dialog(self):
"""显示关于对话框"""
about_text = f"""
<p><b>{APP_NAME} v{APP_VERSION}</b></p>
<p>原作: <a href="https://github.com/Yanam1Anna">Yanam1Anna</a></p>
<p>此应用根据 <a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/LICENSE">GPL-3.0 许可证</a> 授权。</p>
"""
msg_box = msgbox_frame(
f"关于 - {APP_NAME}",
about_text,
QtWidgets.QMessageBox.StandardButton.Ok,
)
msg_box.setTextFormat(Qt.TextFormat.RichText) # 启用富文本
msg_box.exec()
def open_project_home_page(self):
"""打开项目主页"""
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT")
def closeEvent(self, event): def closeEvent(self, event):
"""窗口关闭事件处理
Args:
event: 关闭事件
"""
self.shutdown_app(event) self.shutdown_app(event)
def shutdown_app(self, event=None, force_exit=False): def shutdown_app(self, event=None, force_exit=False):
self.hosts_manager.restore() # 恢复hosts文件 """关闭应用程序
self.stop_logging() # 确保在退出时停止日志记录
Args:
event: 关闭事件如果是从closeEvent调用的
force_exit: 是否强制退出
"""
# 检查是否有动画或任务正在进行
if hasattr(self, 'animation_in_progress') and self.animation_in_progress and not force_exit:
# 如果动画正在进行,阻止退出
if event:
event.ignore()
return
# 检查是否有下载任务正在进行
if hasattr(self.download_manager, 'current_download_thread') and \
self.download_manager.current_download_thread and \
self.download_manager.current_download_thread.isRunning() and not force_exit:
# 询问用户是否确认退出
reply = QMessageBox.question(
self,
f"确认退出 - {APP_NAME}",
"\n下载任务正在进行中,确定要退出吗?\n",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.No:
if event:
event.ignore()
return
# 恢复hosts文件
self.download_manager.hosts_manager.restore()
# 额外检查并清理hosts文件中的残留记录
self.download_manager.hosts_manager.check_and_clean_all_entries()
# 停止日志记录
self.debug_manager.stop_logging()
if not force_exit: if not force_exit:
reply = QtWidgets.QMessageBox.question( reply = QMessageBox.question(
self, self,
"退出程序", f"确认退出 - {APP_NAME}",
"\n是否确定退出?\n", "\n确定退出吗?\n",
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QtWidgets.QMessageBox.StandardButton.No, QMessageBox.StandardButton.No
) )
if reply != QtWidgets.QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.No:
if event: if event:
event.ignore() event.ignore()
return return
if ( # 退出应用程序
self.current_download_thread
and self.current_download_thread.isRunning()
):
QtWidgets.QMessageBox.critical(
self,
f"错误 - {APP_NAME}",
"\n当前有下载任务正在进行,完成后再试\n",
)
if event:
event.ignore()
return
if os.path.exists(PLUGIN):
for attempt in range(3):
try:
shutil.rmtree(PLUGIN)
break
except Exception as e:
if attempt == 2:
QtWidgets.QMessageBox.critical(
self,
f"错误 - {APP_NAME}",
f"\n清理缓存失败\n\n【错误信息】:{e}\n",
)
if event:
event.accept()
sys.exit(1)
if event: if event:
event.accept() event.accept()
else: else:
sys.exit(0) sys.exit(0)

656
source/main_window.py.bak Normal file
View File

@@ -0,0 +1,656 @@
import os
import sys
import shutil
import webbrowser
import requests
import json
from urllib.parse import urlparse
from collections import deque
from PySide6 import QtWidgets
from PySide6.QtCore import QTimer, Qt
from PySide6.QtGui import QIcon, QAction
from PySide6.QtWidgets import QMainWindow, QFileDialog, QApplication, QMessageBox
from ui.Ui_install import Ui_MainWindows
from core.animations import MultiStageAnimations
from data.config import (
APP_NAME, APP_VERSION, PLUGIN, GAME_INFO, BLOCK_SIZE,
PLUGIN_HASH, UA, CONFIG_URL, LOG_FILE
)
from utils import (
load_base64_image, HashManager, AdminPrivileges, msgbox_frame,
load_config, save_config, HostsManager, Logger
)
from workers.download import DownloadThread, ProgressWindow
from data.pic_data import img_data
from workers import (
IpOptimizerThread, HashThread, ExtractionThread, ConfigFetchThread
)
from core import (
MultiStageAnimations, UIManager, DownloadManager, DebugManager
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 初始化UI (从Ui_install.py导入)
self.ui = Ui_MainWindows()
self.ui.setupUi(self)
icon_data = img_data.get("icon")
if icon_data:
pixmap = load_base64_image(icon_data)
self.setWindowIcon(QIcon(pixmap))
# 设置窗口标题为APP_NAME加版本号
self.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
# 初始化动画系统 (从animations.py导入)
self.animator = MultiStageAnimations(self.ui, self)
# 初始化功能变量
self.selected_folder = ""
self.installed_status = {f"NEKOPARA Vol.{i}": False for i in range(1, 5)}
self.installed_status["NEKOPARA After"] = False # 添加After的状态
self.download_queue = deque()
self.current_download_thread = None
self.hash_manager = HashManager(BLOCK_SIZE)
self.hash_thread = None
self.extraction_thread = None
self.hash_msg_box = None
self.optimized_ip = None
self.optimization_done = False # 标记是否已执行过优选
self.logger = None
self.hosts_manager = HostsManager() # 实例化HostsManager
self.cloud_config = None
self.config_fetch_thread = None
# 加载配置
self.config = load_config()
# 检查管理员权限和进程
self.admin_privileges = AdminPrivileges()
self.admin_privileges.request_admin_privileges()
self.admin_privileges.check_and_terminate_processes()
# 备份hosts文件
self.hosts_manager.backup()
# 创建缓存目录
if not os.path.exists(PLUGIN):
try:
os.makedirs(PLUGIN)
except OSError as e:
QtWidgets.QMessageBox.critical(
self,
f"错误 - {APP_NAME}",
f"\n无法创建缓存位置\n\n使用管理员身份运行或检查文件读写权限\n\n【错误信息】:{e}\n",
)
sys.exit(1)
# 连接信号 (使用Ui_install.py中的组件名称)
self.ui.start_install_btn.clicked.connect(self.download_manager.file_dialog)
self.ui.exit_btn.clicked.connect(self.shutdown_app)
# “帮助”菜单
project_home_action = QAction("项目主页", self)
project_home_action.triggered.connect(self.open_project_home_page)
about_action = QAction("关于", self)
about_action.triggered.connect(self.show_about_dialog)
self.ui.menu_2.addAction(project_home_action)
self.ui.menu_2.addAction(about_action)
# “设置”菜单
self.debug_action = QAction("Debug模式", self, checkable=True)
self.debug_action.setChecked(self.config.get("debug_mode", False))
self.debug_action.triggered.connect(self.toggle_debug_mode)
self.ui.menu.addAction(self.debug_action)
# 为未来功能预留的“切换下载源”按钮
self.switch_source_action = QAction("切换下载源", self)
self.switch_source_action.setEnabled(False) # 暂时禁用
self.ui.menu.addAction(self.switch_source_action)
# 根据初始配置决定是否开启Debug模式
if self.debug_action.isChecked():
self.start_logging()
# 在窗口显示前设置初始状态
self.animator.initialize()
# 窗口显示后延迟100ms启动动画
QTimer.singleShot(100, self.start_animations)
def start_animations(self):
self.ui.exit_btn.setEnabled(False)
self.animator.animation_finished.connect(self.on_animations_finished)
self.animator.start_animations()
self.fetch_cloud_config()
def on_animations_finished(self):
self.ui.exit_btn.setEnabled(True)
def fetch_cloud_config(self):
headers = {"User-Agent": UA}
debug_mode = self.debug_action.isChecked()
self.config_fetch_thread = ConfigFetchThread(CONFIG_URL, headers, debug_mode, self)
self.config_fetch_thread.finished.connect(self.on_config_fetched)
self.config_fetch_thread.start()
def on_config_fetched(self, data, error_message):
if error_message:
if error_message == "update_required":
msg_box = msgbox_frame(
f"更新提示 - {APP_NAME}",
"\n当前版本过低,请及时更新。\n",
QtWidgets.QMessageBox.StandardButton.Ok,
)
msg_box.exec()
self.open_project_home_page()
self.shutdown_app(force_exit=True)
elif "missing_keys" in error_message:
missing_versions = error_message.split(":")[1]
msg_box = msgbox_frame(
f"配置缺失 - {APP_NAME}",
f'\n云端缺失下载链接,可能云服务器正在维护,不影响其他版本下载。\n当前缺失版本:"{missing_versions}"\n',
QtWidgets.QMessageBox.StandardButton.Ok,
)
msg_box.exec()
else:
# 其他错误暂时只在debug模式下打印
if self.debug_action.isChecked():
print(f"获取云端配置失败: {error_message}")
else:
self.cloud_config = data
if self.debug_action.isChecked():
print("--- Cloud config fetched successfully ---")
print(json.dumps(data, indent=2))
def toggle_debug_mode(self, checked):
self.config["debug_mode"] = checked
save_config(self.config)
if checked:
self.start_logging()
else:
self.stop_logging()
def start_logging(self):
if self.logger is None:
try:
if os.path.exists(LOG_FILE):
os.remove(LOG_FILE)
# 保存原始的 stdout 和 stderr
self.original_stdout = sys.stdout
self.original_stderr = sys.stderr
# 创建 Logger 实例
self.logger = Logger(LOG_FILE, self.original_stdout)
sys.stdout = self.logger
sys.stderr = self.logger
print("--- Debug mode enabled ---")
except (IOError, OSError) as e:
QtWidgets.QMessageBox.critical(self, "错误", f"无法创建日志文件: {e}")
self.logger = None
def stop_logging(self):
if self.logger:
print("--- Debug mode disabled ---")
sys.stdout = self.original_stdout
sys.stderr = self.original_stderr
self.logger.close()
self.logger = None
def get_install_paths(self):
return {
game: os.path.join(self.selected_folder, info["install_path"])
for game, info in GAME_INFO.items()
}
def file_dialog(self):
self.selected_folder = QFileDialog.getExistingDirectory(
self, f"选择游戏所在【上级目录】 {APP_NAME}"
)
if not self.selected_folder:
QtWidgets.QMessageBox.warning(
self, f"通知 - {APP_NAME}", "\n未选择任何目录,请重新选择\n"
)
return
self.download_action()
def get_download_url(self) -> dict:
try:
if self.cloud_config:
if self.debug_action.isChecked():
print("--- Using pre-fetched cloud config ---")
config_data = self.cloud_config
else:
# 如果没有预加载的配置,则同步获取
headers = {"User-Agent": UA}
response = requests.get(CONFIG_URL, headers=headers, timeout=10)
response.raise_for_status()
config_data = response.json()
if not config_data:
raise ValueError("未能获取或解析配置数据")
if self.debug_action.isChecked():
print(f"DEBUG: Parsed JSON data: {json.dumps(config_data, indent=2)}")
# 统一处理URL提取确保返回扁平化的字典
urls = {}
for i in range(4):
key = f"vol.{i+1}.data"
if key in config_data and "url" in config_data[key]:
urls[f"vol{i+1}"] = config_data[key]["url"]
if "after.data" in config_data and "url" in config_data["after.data"]:
urls["after"] = config_data["after.data"]["url"]
# 检查是否成功提取了所有URL
if len(urls) != 5:
missing_keys_map = {
f"vol{i+1}": f"vol.{i+1}.data" for i in range(4)
}
missing_keys_map["after"] = "after.data"
extracted_keys = set(urls.keys())
all_keys = set(missing_keys_map.keys())
missing_simple_keys = all_keys - extracted_keys
missing_original_keys = [missing_keys_map[k] for k in missing_simple_keys]
raise ValueError(f"配置文件缺少必要的键: {', '.join(missing_original_keys)}")
if self.debug_action.isChecked():
print(f"DEBUG: Extracted URLs: {urls}")
print("--- Finished getting download URL successfully ---")
return urls
if self.debug_action.isChecked():
print(f"DEBUG: Extracted URLs: {urls}")
print("--- Finished getting download URL successfully ---")
return urls
except requests.exceptions.RequestException as e:
status_code = e.response.status_code if e.response is not None else "未知"
try:
error_response = e.response.json() if e.response else {}
json_title = error_response.get("title", "无错误类型")
json_message = error_response.get("message", "无附加错误信息")
except (ValueError, AttributeError):
json_title = "配置文件异常,无法解析错误类型"
json_message = "配置文件异常,无法解析错误信息"
if self.debug_action.isChecked():
print(f"ERROR: Failed to get download config due to RequestException: {e}")
QtWidgets.QMessageBox.critical(
self,
f"错误 - {APP_NAME}",
f"\n下载配置获取失败\n\n【HTTP状态】{status_code}\n【错误类型】:{json_title}\n【错误信息】:{json_message}\n",
)
return {}
except ValueError as e:
if self.debug_action.isChecked():
print(f"ERROR: Failed to parse download config due to ValueError: {e}")
QtWidgets.QMessageBox.critical(
self,
f"错误 - {APP_NAME}",
f"\n配置文件格式异常\n\n【错误信息】:{e}\n",
)
return {}
def download_setting(self, url, game_folder, game_version, _7z_path, plugin_path):
game_exe = {
game: os.path.join(
self.selected_folder, info["install_path"].split("/")[0], info["exe"]
)
for game, info in GAME_INFO.items()
}
if (
game_version not in game_exe
or not os.path.exists(game_exe[game_version])
or self.installed_status[game_version]
):
self.installed_status[game_version] = False
self.show_result()
return
self.progress_window = ProgressWindow(self)
self.start_download_with_ip(self.optimized_ip, url, _7z_path, game_version, game_folder, plugin_path)
def on_optimization_and_hosts_finished(self, ip):
self.optimized_ip = ip
self.optimization_done = True
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
if not ip:
QtWidgets.QMessageBox.warning(self, f"优选失败 - {APP_NAME}", "\n未能找到合适的Cloudflare IP将使用默认网络进行下载。\n")
else:
if self.download_queue:
first_url = self.download_queue[0][0]
hostname = urlparse(first_url).hostname
if self.hosts_manager.apply_ip(hostname, ip):
QtWidgets.QMessageBox.information(self, f"成功 - {APP_NAME}", f"\n已将优选IP ({ip}) 应用到hosts文件。\n")
else:
QtWidgets.QMessageBox.critical(self, f"错误 - {APP_NAME}", "\n修改hosts文件失败请检查程序是否以管理员权限运行。\n")
self.next_download_task()
def start_download_with_ip(self, preferred_ip, url, _7z_path, game_version, game_folder, plugin_path):
if preferred_ip:
print(f"已为 {game_version} 获取到优选IP: {preferred_ip}")
else:
print(f"未能为 {game_version} 获取优选IP将使用默认线路。")
self.current_download_thread = DownloadThread(url, _7z_path, game_version, self)
self.current_download_thread.progress.connect(self.progress_window.update_progress)
self.current_download_thread.finished.connect(
lambda success, error: self.install_setting(
success,
error,
self.progress_window,
url,
game_folder,
game_version,
_7z_path,
plugin_path,
)
)
self.progress_window.stop_button.clicked.connect(self.current_download_thread.stop)
self.current_download_thread.start()
self.progress_window.exec()
def install_setting(
self,
success,
error,
progress_window,
url,
game_folder,
game_version,
_7z_path,
plugin_path,
):
if progress_window.isVisible():
progress_window.reject()
if not success:
print(f"--- Download Failed: {game_version} ---")
print(error)
print("------------------------------------")
msg_box = QtWidgets.QMessageBox(self)
msg_box.setWindowTitle(f"下载失败 - {APP_NAME}")
msg_box.setText(f"\n文件获取失败: {game_version}\n错误: {error}\n\n是否重试?")
retry_button = msg_box.addButton("重试", QtWidgets.QMessageBox.ButtonRole.YesRole)
next_button = msg_box.addButton("下一个", QtWidgets.QMessageBox.ButtonRole.NoRole)
end_button = msg_box.addButton("结束", QtWidgets.QMessageBox.ButtonRole.RejectRole)
msg_box.exec()
clicked_button = msg_box.clickedButton()
if clicked_button == retry_button:
self.download_setting(url, game_folder, game_version, _7z_path, plugin_path)
elif clicked_button == next_button:
self.next_download_task()
else:
self.on_download_stopped()
return
# --- Start Extraction in a new thread ---
self.hash_msg_box = self.hash_manager.hash_pop_window()
self.extraction_thread = ExtractionThread(_7z_path, game_folder, plugin_path, game_version, self)
self.extraction_thread.finished.connect(self.on_extraction_finished)
self.extraction_thread.start()
def on_extraction_finished(self, success, error_message, game_version):
if self.hash_msg_box and self.hash_msg_box.isVisible():
self.hash_msg_box.close()
if not success:
QtWidgets.QMessageBox.critical(self, f"错误 - {APP_NAME}", error_message)
self.installed_status[game_version] = False
else:
self.installed_status[game_version] = True
self.next_download_task()
def download_action(self):
# 询问用户是否使用Cloudflare加速
msg_box = QMessageBox(self)
msg_box.setWindowTitle(f"下载优化 - {APP_NAME}")
msg_box.setText("是否愿意通过Cloudflare加速来优化下载速度\n\n这将临时修改系统的hosts文件并需要管理员权限。")
msg_box.setIcon(QMessageBox.Icon.Question)
yes_button = msg_box.addButton("是,开启加速", QMessageBox.ButtonRole.YesRole)
no_button = msg_box.addButton("否,直接下载", QMessageBox.ButtonRole.NoRole)
msg_box.exec()
use_optimization = msg_box.clickedButton() == yes_button
self.hash_msg_box = self.hash_manager.hash_pop_window()
install_paths = self.get_install_paths()
self.hash_thread = HashThread("pre", install_paths, PLUGIN_HASH, self.installed_status, self)
# 将用户选择传递给哈希完成后的处理函数
self.hash_thread.pre_finished.connect(lambda status: self.on_pre_hash_finished(status, use_optimization))
self.hash_thread.start()
def on_pre_hash_finished(self, updated_status, use_optimization):
self.installed_status = updated_status
if self.hash_msg_box and self.hash_msg_box.isVisible():
self.hash_msg_box.accept()
self.hash_msg_box = None
config = self.get_download_url()
if not config:
QtWidgets.QMessageBox.critical(
self, f"错误 - {APP_NAME}", "\n网络状态异常或服务器故障,请重试\n"
)
return
# --- 填充下载队列 ---
for i in range(1, 5):
game_version = f"NEKOPARA Vol.{i}"
if not self.installed_status.get(game_version, False):
url = config.get(f"vol{i}")
if not url: continue
game_folder = os.path.join(self.selected_folder, f"NEKOPARA Vol. {i}")
_7z_path = os.path.join(PLUGIN, f"vol.{i}.7z")
plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))
game_version = "NEKOPARA After"
if not self.installed_status.get(game_version, False):
url = config.get("after")
if url:
game_folder = os.path.join(self.selected_folder, "NEKOPARA After")
_7z_path = os.path.join(PLUGIN, "after.7z")
plugin_path = os.path.join(PLUGIN, GAME_INFO[game_version]["plugin_path"])
self.download_queue.append((url, game_folder, game_version, _7z_path, plugin_path))
if not self.download_queue:
self.after_hash_compare(PLUGIN_HASH)
return
if use_optimization and not self.optimization_done:
first_url = self.download_queue[0][0]
self.optimizing_msg_box = msgbox_frame(
f"通知 - {APP_NAME}",
"\n正在优选Cloudflare IP请稍候...\n\n这可能需要5-10分钟请耐心等待喵~"
)
# 我们不再提供“跳过”按钮,因为用户已经做出了选择
self.optimizing_msg_box.setStandardButtons(QMessageBox.StandardButton.NoButton)
self.optimizing_msg_box.setWindowModality(Qt.WindowModality.ApplicationModal)
self.optimizing_msg_box.open()
self.ip_optimizer_thread = IpOptimizerThread(first_url)
# 优选完成后需要修改hosts并开始下载
self.ip_optimizer_thread.finished.connect(self.on_optimization_and_hosts_finished)
self.ip_optimizer_thread.start()
else:
# 如果用户选择不优化,或已经优化过,直接开始下载
self.next_download_task()
def next_download_task(self):
if not self.download_queue:
self.after_hash_compare(PLUGIN_HASH)
return
# 检查下载线程是否仍在运行,以避免在手动停止后立即开始下一个任务
if self.current_download_thread and self.current_download_thread.isRunning():
return
# 在开始下载前确保hosts文件已修改如果需要
# 这里的逻辑保持不变因为hosts文件应该在队列开始前就被修改了
url, game_folder, game_version, _7z_path, plugin_path = self.download_queue.popleft()
self.download_setting(url, game_folder, game_version, _7z_path, plugin_path)
def on_download_stopped(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, 'optimizing_msg_box') and self.optimizing_msg_box:
if self.optimizing_msg_box.isVisible():
self.optimizing_msg_box.accept()
self.optimizing_msg_box = None
# 停止当前可能仍在运行的下载线程
if self.current_download_thread and self.current_download_thread.isRunning():
self.current_download_thread.stop()
self.current_download_thread.wait() # 等待线程完全终止
# 清空下载队列,因为用户决定停止
self.download_queue.clear()
# 确保进度窗口已关闭
if hasattr(self, 'progress_window') and self.progress_window.isVisible():
self.progress_window.reject()
# 可以在这里决定是否立即进行哈希比较或显示结果
print("下载已全部停止。")
self.setEnabled(True) # 恢复主窗口交互
self.show_result()
def after_hash_compare(self, plugin_hash):
self.hash_msg_box = self.hash_manager.hash_pop_window()
install_paths = self.get_install_paths()
self.hash_thread = HashThread("after", install_paths, plugin_hash, self.installed_status, self)
self.hash_thread.after_finished.connect(self.on_after_hash_finished)
self.hash_thread.start()
def on_after_hash_finished(self, result):
if self.hash_msg_box and self.hash_msg_box.isVisible():
self.hash_msg_box.close()
if not result["passed"]:
game = result.get("game", "未知游戏")
message = result.get("message", "发生未知错误。")
msg_box = msgbox_frame(
f"文件校验失败 - {APP_NAME}",
message,
QtWidgets.QMessageBox.StandardButton.Ok,
)
msg_box.exec()
self.show_result()
def show_result(self):
installed_version = "\n".join(
[i for i in self.installed_status if self.installed_status[i]]
)
failed_ver = "\n".join(
[i for i in self.installed_status if not self.installed_status[i]]
)
QtWidgets.QMessageBox.information(
self,
f"完成 - {APP_NAME}",
f"\n安装结果:\n安装成功数:{len(installed_version.splitlines())} 安装失败数:{len(failed_ver.splitlines())}\n"
f"安装成功的版本:\n{installed_version}\n尚未持有或未使用本工具安装补丁的版本:\n{failed_ver}\n",
)
def show_about_dialog(self):
"""显示关于对话框"""
about_text = f"""
<p><b>{APP_NAME} v{APP_VERSION}</b></p>
<p>原作: <a href="https://github.com/Yanam1Anna">Yanam1Anna</a></p>
<p>此应用根据 <a href="https://github.com/hyb-oyqq/FRAISEMOE2-Installer/blob/master/LICENSE">GPL-3.0 许可证</a> 授权。</p>
"""
msg_box = msgbox_frame(
f"关于 - {APP_NAME}",
about_text,
QtWidgets.QMessageBox.StandardButton.Ok,
)
msg_box.setTextFormat(Qt.TextFormat.RichText) # 启用富文本
msg_box.exec()
def open_project_home_page(self):
"""打开项目主页"""
webbrowser.open("https://github.com/hyb-oyqq/FRAISEMOE-Addons-Installer-NEXT")
def closeEvent(self, event):
self.shutdown_app(event)
def shutdown_app(self, event=None, force_exit=False):
self.hosts_manager.restore() # 恢复hosts文件
self.stop_logging() # 确保在退出时停止日志记录
if not force_exit:
reply = QtWidgets.QMessageBox.question(
self,
"退出程序",
"\n是否确定退出?\n",
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No,
QtWidgets.QMessageBox.StandardButton.No,
)
if reply != QtWidgets.QMessageBox.StandardButton.Yes:
if event:
event.ignore()
return
if (
self.current_download_thread
and self.current_download_thread.isRunning()
):
QtWidgets.QMessageBox.critical(
self,
f"错误 - {APP_NAME}",
"\n当前有下载任务正在进行,完成后再试\n",
)
if event:
event.ignore()
return
if os.path.exists(PLUGIN):
for attempt in range(3):
try:
shutil.rmtree(PLUGIN)
break
except Exception as e:
if attempt == 2:
QtWidgets.QMessageBox.critical(
self,
f"错误 - {APP_NAME}",
f"\n清理缓存失败\n\n【错误信息】:{e}\n",
)
if event:
event.accept()
sys.exit(1)
if event:
event.accept()
else:
sys.exit(0)

View File

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -1,4 +1,4 @@
from pic_data import img_data from data.pic_data import img_data
from PySide6.QtGui import QPixmap from PySide6.QtGui import QPixmap
import base64 import base64
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,

18
source/utils/__init__.py Normal file
View File

@@ -0,0 +1,18 @@
from .logger import Logger
from .helpers import (
load_base64_image, HashManager, AdminPrivileges, msgbox_frame,
load_config, save_config, HostsManager, censor_url, resource_path
)
__all__ = [
'Logger',
'load_base64_image',
'HashManager',
'AdminPrivileges',
'msgbox_frame',
'load_config',
'save_config',
'HostsManager',
'censor_url',
'resource_path'
]

View File

@@ -9,8 +9,8 @@ import psutil
from PySide6 import QtCore, QtWidgets from PySide6 import QtCore, QtWidgets
import re import re
from PySide6.QtGui import QIcon, QPixmap from PySide6.QtGui import QIcon, QPixmap
from pic_data import img_data from data.pic_data import img_data
from config import APP_NAME, CONFIG_FILE from data.config import APP_NAME, CONFIG_FILE
def resource_path(relative_path): def resource_path(relative_path):
"""获取资源的绝对路径适用于开发环境和PyInstaller打包环境""" """获取资源的绝对路径适用于开发环境和PyInstaller打包环境"""
@@ -19,7 +19,18 @@ def resource_path(relative_path):
base_path = getattr(sys, '_MEIPASS', os.path.dirname(sys.executable)) base_path = getattr(sys, '_MEIPASS', os.path.dirname(sys.executable))
else: else:
# 在开发环境中运行 # 在开发环境中运行
base_path = os.path.dirname(os.path.abspath(__file__)) base_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# 现在base_path是项目根目录我们需要进入source目录
base_path = os.path.join(base_path, 'source')
# 处理特殊情况
if relative_path == "aria2c.exe":
return os.path.join(base_path, 'bin', relative_path)
elif relative_path == "cfst.exe":
return os.path.join(base_path, 'bin', relative_path)
elif relative_path == "ip.txt" or relative_path == "ipv6.txt":
return os.path.join(base_path, 'resources', 'data', relative_path)
return os.path.join(base_path, relative_path) return os.path.join(base_path, relative_path)
def load_base64_image(base64_str): def load_base64_image(base64_str):
@@ -224,6 +235,7 @@ class HostsManager:
self.backup_path = os.path.join(os.path.dirname(self.hosts_path), f'hosts.bak.{APP_NAME}') self.backup_path = os.path.join(os.path.dirname(self.hosts_path), f'hosts.bak.{APP_NAME}')
self.original_content = None self.original_content = None
self.modified = False self.modified = False
self.modified_hostnames = set() # 跟踪被修改的主机名
def backup(self): def backup(self):
if not AdminPrivileges().is_admin(): if not AdminPrivileges().is_admin():
@@ -241,6 +253,48 @@ class HostsManager:
msg_box = msgbox_frame(f"错误 - {APP_NAME}", f"\n无法备份hosts文件请检查权限。\n\n【错误信息】:{e}\n", QtWidgets.QMessageBox.StandardButton.Ok) msg_box = msgbox_frame(f"错误 - {APP_NAME}", f"\n无法备份hosts文件请检查权限。\n\n【错误信息】:{e}\n", QtWidgets.QMessageBox.StandardButton.Ok)
msg_box.exec() msg_box.exec()
return False return False
def clean_hostname_entries(self, hostname):
"""清理hosts文件中指定域名的所有记录
Args:
hostname: 要清理的域名
Returns:
bool: 清理是否成功
"""
if not self.original_content:
if not self.backup():
return False
# 确保original_content不为None
if not self.original_content:
print("无法读取hosts文件内容操作中止。")
return False
if not AdminPrivileges().is_admin():
print("需要管理员权限来修改hosts文件。")
return False
try:
lines = self.original_content.splitlines()
new_lines = [line for line in lines if hostname not in line]
# 如果没有变化,不需要写入
if len(new_lines) == len(lines):
print(f"Hosts文件中没有找到 {hostname} 的记录")
return True
with open(self.hosts_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines))
# 更新原始内容
self.original_content = '\n'.join(new_lines)
print(f"已从hosts文件中清理 {hostname} 的记录")
return True
except IOError as e:
print(f"清理hosts文件失败: {e}")
return False
def apply_ip(self, hostname, ip_address): def apply_ip(self, hostname, ip_address):
if not self.original_content: if not self.original_content:
@@ -256,17 +310,23 @@ class HostsManager:
return False return False
try: try:
lines = self.original_content.splitlines() # 首先清理已有的同域名记录
new_lines = [line for line in lines if not (hostname in line and line.strip().startswith(ip_address))] self.clean_hostname_entries(hostname)
# 然后添加新记录
lines = self.original_content.splitlines()
new_entry = f"{ip_address}\t{hostname}" new_entry = f"{ip_address}\t{hostname}"
new_lines.append(f"\n# Added by {APP_NAME}") lines.append(f"\n# Added by {APP_NAME}")
new_lines.append(new_entry) lines.append(new_entry)
with open(self.hosts_path, 'w', encoding='utf-8') as f: with open(self.hosts_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines)) f.write('\n'.join(lines))
# 更新原始内容
self.original_content = '\n'.join(lines)
self.modified = True self.modified = True
# 记录被修改的主机名,用于最终清理
self.modified_hostnames.add(hostname)
print(f"Hosts文件已更新: {new_entry}") print(f"Hosts文件已更新: {new_entry}")
return True return True
except IOError as e: except IOError as e:
@@ -275,6 +335,55 @@ class HostsManager:
msg_box.exec() msg_box.exec()
return False return False
def check_and_clean_all_entries(self):
"""检查并清理所有由本应用程序添加的hosts记录
Returns:
bool: 清理是否成功
"""
if not AdminPrivileges().is_admin():
print("需要管理员权限来检查和清理hosts文件。")
return False
try:
# 读取当前hosts文件内容
with open(self.hosts_path, 'r', encoding='utf-8') as f:
current_content = f.read()
lines = current_content.splitlines()
new_lines = []
skip_next = False
for line in lines:
# 如果上一行是我们的注释标记,跳过当前行
if skip_next:
skip_next = False
continue
# 检查是否是我们添加的注释行
if f"# Added by {APP_NAME}" in line:
skip_next = True # 跳过下一行实际的hosts记录
continue
# 保留其他所有行
new_lines.append(line)
# 检查是否有变化
if len(new_lines) == len(lines):
print("Hosts文件中没有找到由本应用添加的记录")
return True
# 写回清理后的内容
with open(self.hosts_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines))
print(f"已清理所有由 {APP_NAME} 添加的hosts记录")
return True
except IOError as e:
print(f"检查和清理hosts文件失败: {e}")
return False
def restore(self): def restore(self):
if not self.modified: if not self.modified:
if os.path.exists(self.backup_path): if os.path.exists(self.backup_path):
@@ -282,6 +391,8 @@ class HostsManager:
os.remove(self.backup_path) os.remove(self.backup_path)
except OSError: except OSError:
pass pass
# 即使没有修改过,也检查一次是否有残留
self.check_and_clean_all_entries()
return True return True
if not AdminPrivileges().is_admin(): if not AdminPrivileges().is_admin():
@@ -299,6 +410,8 @@ class HostsManager:
os.remove(self.backup_path) os.remove(self.backup_path)
except OSError: except OSError:
pass pass
# 恢复后再检查一次是否有残留
self.check_and_clean_all_entries()
return True return True
except IOError as e: except IOError as e:
print(f"从内存恢复hosts文件失败: {e}") print(f"从内存恢复hosts文件失败: {e}")
@@ -309,6 +422,8 @@ class HostsManager:
def restore_from_backup_file(self): def restore_from_backup_file(self):
if not os.path.exists(self.backup_path): if not os.path.exists(self.backup_path):
print("未找到hosts备份文件无法恢复。") print("未找到hosts备份文件无法恢复。")
# 即使没有备份文件,也尝试清理可能的残留
self.check_and_clean_all_entries()
return False return False
try: try:
with open(self.backup_path, 'r', encoding='utf-8') as bf: with open(self.backup_path, 'r', encoding='utf-8') as bf:
@@ -318,11 +433,15 @@ class HostsManager:
os.remove(self.backup_path) os.remove(self.backup_path)
self.modified = False self.modified = False
print("Hosts文件已从备份文件恢复。") print("Hosts文件已从备份文件恢复。")
# 恢复后再检查一次是否有残留
self.check_and_clean_all_entries()
return True return True
except (IOError, OSError) as e: except (IOError, OSError) as e:
print(f"从备份文件恢复hosts失败: {e}") print(f"从备份文件恢复hosts失败: {e}")
msg_box = msgbox_frame(f"警告 - {APP_NAME}", f"\n自动恢复hosts文件失败请手动从 {self.backup_path} 恢复。\n\n【错误信息】:{e}\n", QtWidgets.QMessageBox.StandardButton.Ok) msg_box = msgbox_frame(f"警告 - {APP_NAME}", f"\n自动恢复hosts文件失败请手动从 {self.backup_path} 恢复。\n\n【错误信息】:{e}\n", QtWidgets.QMessageBox.StandardButton.Ok)
msg_box.exec() msg_box.exec()
# 尽管恢复失败,仍然尝试清理可能的残留
self.check_and_clean_all_entries()
return False return False
def censor_url(text): def censor_url(text):

19
source/utils/logger.py Normal file
View File

@@ -0,0 +1,19 @@
from .helpers import censor_url
class Logger:
def __init__(self, filename, stream):
self.terminal = stream
self.log = open(filename, "w", encoding="utf-8")
def write(self, message):
censored_message = censor_url(message)
self.terminal.write(censored_message)
self.log.write(censored_message)
self.flush()
def flush(self):
self.terminal.flush()
self.log.flush()
def close(self):
self.log.close()

View File

@@ -0,0 +1,14 @@
from .hash_thread import HashThread
from .extraction_thread import ExtractionThread
from .config_fetch_thread import ConfigFetchThread
from .ip_optimizer import IpOptimizerThread
from .download import DownloadThread, ProgressWindow
__all__ = [
'IpOptimizerThread',
'HashThread',
'ExtractionThread',
'ConfigFetchThread',
'DownloadThread',
'ProgressWindow'
]

View File

@@ -0,0 +1,52 @@
import json
import requests
from PySide6.QtCore import QThread, Signal
class ConfigFetchThread(QThread):
finished = Signal(object, str) # data, error_message
def __init__(self, url, headers, debug_mode=False, parent=None):
super().__init__(parent)
self.url = url
self.headers = headers
self.debug_mode = debug_mode
def run(self):
try:
if self.debug_mode:
print("--- Starting to fetch cloud config ---")
print(f"DEBUG: Requesting URL: {self.url}")
print(f"DEBUG: Using Headers: {self.headers}")
response = requests.get(self.url, headers=self.headers, timeout=10)
if self.debug_mode:
print(f"DEBUG: Response Status Code: {response.status_code}")
print(f"DEBUG: Response Headers: {response.headers}")
print(f"DEBUG: Response Text: {response.text}")
response.raise_for_status()
# 首先总是尝试解析JSON
config_data = response.json()
# 检查是否是要求更新的错误信息
if config_data.get("message") == "请使用最新版本的FRAISEMOE Addons Installer NEXT进行下载安装":
self.finished.emit(None, "update_required")
return
# 检查是否是有效的配置文件
required_keys = [f"vol.{i+1}.data" for i in range(4)] + ["after.data"]
missing_keys = [key for key in required_keys if key not in config_data]
if missing_keys:
self.finished.emit(None, f"missing_keys:{','.join(missing_keys)}")
return
self.finished.emit(config_data, "")
except requests.exceptions.RequestException as e:
self.finished.emit(None, f"网络请求失败: {e}")
except (ValueError, json.JSONDecodeError) as e:
self.finished.emit(None, f"JSON解析失败: {e}")
finally:
if self.debug_mode:
print("--- Finished fetching cloud config ---")

View File

@@ -7,7 +7,7 @@ from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import (Qt, Signal, QThread, QTimer) from PySide6.QtCore import (Qt, Signal, QThread, QTimer)
from PySide6.QtWidgets import (QLabel, QProgressBar, QVBoxLayout, QDialog) from PySide6.QtWidgets import (QLabel, QProgressBar, QVBoxLayout, QDialog)
from utils import resource_path from utils import resource_path
from config import APP_NAME, UA from data.config import APP_NAME, UA
# 下载线程类 # 下载线程类
class DownloadThread(QThread): class DownloadThread(QThread):

View File

@@ -0,0 +1,31 @@
import os
import shutil
import py7zr
from PySide6.QtCore import QThread, Signal
from data.config import PLUGIN, GAME_INFO
class ExtractionThread(QThread):
finished = Signal(bool, str, str) # success, error_message, game_version
def __init__(self, _7z_path, game_folder, plugin_path, game_version, parent=None):
super().__init__(parent)
self._7z_path = _7z_path
self.game_folder = game_folder
self.plugin_path = plugin_path
self.game_version = game_version
def run(self):
try:
with py7zr.SevenZipFile(self._7z_path, mode="r") as archive:
archive.extractall(path=PLUGIN)
os.makedirs(self.game_folder, exist_ok=True)
shutil.copy(self.plugin_path, self.game_folder)
if self.game_version == "NEKOPARA After":
sig_path = os.path.join(PLUGIN, GAME_INFO[self.game_version]["sig_path"])
shutil.copy(sig_path, self.game_folder)
self.finished.emit(True, "", self.game_version)
except (py7zr.Bad7zFile, FileNotFoundError, Exception) as e:
self.finished.emit(False, f"\n文件操作失败,请重试\n\n【错误信息】:{e}\n", self.game_version)

View File

@@ -0,0 +1,28 @@
from PySide6.QtCore import QThread, Signal
from utils import HashManager
from data.config import BLOCK_SIZE
class HashThread(QThread):
pre_finished = Signal(dict)
after_finished = Signal(dict)
def __init__(self, mode, install_paths, plugin_hash, installed_status, parent=None):
super().__init__(parent)
self.mode = mode
self.install_paths = install_paths
self.plugin_hash = plugin_hash
self.installed_status = installed_status
# 每个线程都应该有自己的HashManager实例
self.hash_manager = HashManager(BLOCK_SIZE)
def run(self):
if self.mode == "pre":
updated_status = self.hash_manager.cfg_pre_hash_compare(
self.install_paths, self.plugin_hash, self.installed_status
)
self.pre_finished.emit(updated_status)
elif self.mode == "after":
result = self.hash_manager.cfg_after_hash_compare(
self.install_paths, self.plugin_hash, self.installed_status
)
self.after_finished.emit(result)

View File

@@ -5,6 +5,7 @@ import sys
import time import time
from urllib.parse import urlparse from urllib.parse import urlparse
from PySide6.QtCore import QThread, Signal
from utils import resource_path from utils import resource_path
class IpOptimizer: class IpOptimizer:
@@ -28,13 +29,15 @@ class IpOptimizer:
return None return None
ip_txt_path = resource_path("ip.txt") ip_txt_path = resource_path("ip.txt")
# 正确的参数设置根据cfst帮助文档
command = [ command = [
cst_path, cst_path,
"-p", "1", "-n", "500", # 延迟测速线程数 (默认200)
"-o", "", "-p", "1", # 显示结果数量 (默认10个)
"-url", url, "-url", url, # 指定测速地址
"-f", ip_txt_path, "-f", ip_txt_path, # IP文件
"-dd", "-dd", # 禁用下载测速,按延迟排序
] ]
creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0 creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
@@ -53,17 +56,13 @@ class IpOptimizer:
bufsize=0 bufsize=0
) )
# 立即向 stdin 发送换行符,以便程序在 Windows 下正常退出 # 更新正则表达式以匹配cfst输出中的IP格式
if self.process.stdin: # 匹配格式: IP地址在行首后面跟着一些数字和文本
try: ip_pattern = re.compile(r'^(\d+\.\d+\.\d+\.\d+)\s+.*')
self.process.stdin.write('\n')
self.process.stdin.flush() # 标记是否已经找到结果表头和完成标记
except: found_header = False
pass found_completion = False
finally:
self.process.stdin.close()
ip_pattern = re.compile(r'^\s*([\d\.]+)\s+\d+\s+\d+\s+[\d\.]+%?\s+[\d\.]+\s+[\d\.]+\s+.*$')
stdout = self.process.stdout stdout = self.process.stdout
if not stdout: if not stdout:
@@ -72,7 +71,7 @@ class IpOptimizer:
optimal_ip = None optimal_ip = None
timeout_counter = 0 timeout_counter = 0
max_timeout = 60 max_timeout = 300 # 增加超时时间到5分钟
while True: while True:
if self.process.poll() is not None: if self.process.poll() is not None:
@@ -97,16 +96,47 @@ class IpOptimizer:
cleaned_line = line.strip() cleaned_line = line.strip()
if cleaned_line: if cleaned_line:
print(cleaned_line) print(cleaned_line)
match = ip_pattern.match(cleaned_line)
if match: # 检测结果表头
optimal_ip = match.group(1) if "IP 地址" in cleaned_line and "平均延迟" in cleaned_line:
print(f"找到最优 IP: {optimal_ip}, 正在终止测速进程...") print("检测到IP结果表头准备获取IP地址...")
break found_header = True
continue
# 检测完成标记
if "完整测速结果已写入" in cleaned_line or "按下 回车键 或 Ctrl+C 退出" in cleaned_line:
print("检测到测速完成信息")
found_completion = True
# 如果已经找到了IP可以退出了
if optimal_ip:
break
# 已找到表头后尝试匹配IP地址行
if found_header:
match = ip_pattern.search(cleaned_line)
if match and not optimal_ip: # 只保存第一个匹配的IP最优IP
optimal_ip = match.group(1)
print(f"找到最优 IP: {optimal_ip}")
# 如果已经看到完成标记,可以退出了
if found_completion:
break
except Exception as e: except Exception as e:
print(f"读取输出时发生错误: {e}") print(f"读取输出时发生错误: {e}")
break break
# 确保完全读取输出后再发送退出信号
if self.process and self.process.poll() is None:
try:
if self.process.stdin and not self.process.stdin.closed:
print("发送退出信号...")
self.process.stdin.write('\n')
self.process.stdin.flush()
except:
pass
self.stop() self.stop()
print("--- CloudflareSpeedTest 执行结束 ---") print("--- CloudflareSpeedTest 执行结束 ---")
@@ -135,6 +165,24 @@ class IpOptimizer:
self.process.wait() self.process.wait()
print("CloudflareSpeedTest 进程已终止。") print("CloudflareSpeedTest 进程已终止。")
class IpOptimizerThread(QThread):
"""用于在后台线程中运行IP优化的类"""
finished = Signal(str)
def __init__(self, url, parent=None):
super().__init__(parent)
self.url = url
self.optimizer = IpOptimizer()
def run(self):
optimal_ip = self.optimizer.get_optimal_ip(self.url)
self.finished.emit(optimal_ip if optimal_ip else "")
def stop(self):
self.optimizer.stop()
if __name__ == '__main__': if __name__ == '__main__':
# 用于直接测试此模块 # 用于直接测试此模块
test_url = "https://speed.cloudflare.com/__down?during=download&bytes=104857600" test_url = "https://speed.cloudflare.com/__down?during=download&bytes=104857600"
@@ -143,4 +191,4 @@ if __name__ == '__main__':
if ip: if ip:
print(f"{test_url} 找到的最优 IP 是: {ip}") print(f"{test_url} 找到的最优 IP 是: {ip}")
else: else:
print(f"未能为 {test_url} 找到最优 IP。") print(f"未能为 {test_url} 找到最优 IP。")