mirror of
https://github.com/yJason/ClashX-Dashboard.git
synced 2026-03-01 00:35:19 +08:00
fix: spm
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// ClashProxy.swift
|
||||
// ClashX
|
||||
//
|
||||
// Created by CYC on 2019/3/17.
|
||||
// Copyright © 2019 west2online. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import SwiftyJSON
|
||||
|
||||
enum ClashProxyType: String, Codable {
|
||||
case urltest = "URLTest"
|
||||
case fallback = "Fallback"
|
||||
case loadBalance = "LoadBalance"
|
||||
case select = "Selector"
|
||||
case direct = "Direct"
|
||||
case reject = "Reject"
|
||||
case shadowsocks = "Shadowsocks"
|
||||
case shadowsocksR = "ShadowsocksR"
|
||||
case socks5 = "Socks5"
|
||||
case http = "Http"
|
||||
case vmess = "Vmess"
|
||||
case snell = "Snell"
|
||||
case trojan = "Trojan"
|
||||
case relay = "Relay"
|
||||
|
||||
case vless = "Vless"
|
||||
case hysteria = "Hysteria"
|
||||
case wireguardMeta = "WireGuard"
|
||||
case wireguard = "Wireguard"
|
||||
case tuic = "Tuic"
|
||||
|
||||
case pass = "Pass"
|
||||
|
||||
case unknown = "Unknown"
|
||||
|
||||
static let proxyGroups: [ClashProxyType] = [.select, .urltest, .fallback, .loadBalance]
|
||||
|
||||
var isAutoGroup: Bool {
|
||||
switch self {
|
||||
case .urltest, .fallback, .loadBalance:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func isProxyGroup(_ proxy: ClashProxy) -> Bool {
|
||||
switch proxy.type {
|
||||
case .select, .urltest, .fallback, .loadBalance, .relay: return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
static func isBuiltInProxy(_ proxy: ClashProxy) -> Bool {
|
||||
switch proxy.name {
|
||||
case "DIRECT", "REJECT", "PASS": return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typealias ClashProxyName = String
|
||||
typealias ClashProviderName = String
|
||||
|
||||
class ClashProxySpeedHistory: Codable {
|
||||
let time: Date
|
||||
let delay: Int
|
||||
let meanDelay: Int?
|
||||
|
||||
class HisDateFormaterInstance {
|
||||
static let shared = HisDateFormaterInstance()
|
||||
lazy var formater: DateFormatter = {
|
||||
var f = DateFormatter()
|
||||
f.dateFormat = "HH:mm"
|
||||
return f
|
||||
}()
|
||||
}
|
||||
|
||||
lazy var delayInt: Int = {
|
||||
meanDelay ?? delay
|
||||
}()
|
||||
|
||||
lazy var delayDisplay: String = {
|
||||
switch delayInt {
|
||||
case 0: return NSLocalizedString("fail", comment: "")
|
||||
default: return "\(delayInt) ms"
|
||||
}
|
||||
}()
|
||||
|
||||
lazy var dateDisplay: String = {
|
||||
return HisDateFormaterInstance.shared.formater.string(from: time)
|
||||
}()
|
||||
|
||||
lazy var displayString: String = "\(dateDisplay) \(delayDisplay)"
|
||||
}
|
||||
|
||||
class ClashProxy: Codable {
|
||||
let id: String?
|
||||
let name: ClashProxyName
|
||||
let type: ClashProxyType
|
||||
let all: [ClashProxyName]?
|
||||
let history: [ClashProxySpeedHistory]
|
||||
let now: ClashProxyName?
|
||||
weak var enclosingResp: ClashProxyResp?
|
||||
weak var enclosingProvider: ClashProvider?
|
||||
|
||||
let udp: Bool
|
||||
let xudp: Bool
|
||||
let tfo: Bool
|
||||
|
||||
enum SpeedtestAbleItem {
|
||||
case proxy(name: ClashProxyName)
|
||||
case provider(name: ClashProxyName, provider: ClashProviderName)
|
||||
case group(name: ClashProxyName)
|
||||
}
|
||||
|
||||
private static var nameLengthCachedMap = [ClashProxyName: CGFloat]()
|
||||
static func cleanCache() {
|
||||
nameLengthCachedMap.removeAll()
|
||||
}
|
||||
|
||||
lazy var speedtestAble: [SpeedtestAbleItem] = {
|
||||
guard let resp = enclosingResp, let allProxys = all else { return [] }
|
||||
var proxys = [SpeedtestAbleItem]()
|
||||
for proxy in allProxys {
|
||||
if let p = resp.proxiesMap[proxy] {
|
||||
if !ClashProxyType.isProxyGroup(p) {
|
||||
if let provider = p.enclosingProvider {
|
||||
proxys.append(.provider(name: p.name, provider: provider.name))
|
||||
} else {
|
||||
proxys.append(.proxy(name: p.name))
|
||||
}
|
||||
} else {
|
||||
proxys.append(.group(name: p.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return proxys
|
||||
}()
|
||||
|
||||
lazy var isSpeedTestable: Bool = {
|
||||
return speedtestAble.count > 0
|
||||
}()
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type, all, history, now, name, udp, xudp, tfo, id
|
||||
}
|
||||
|
||||
lazy var maxProxyNameLength: CGFloat = {
|
||||
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20)
|
||||
|
||||
let lengths = all?.compactMap({ name -> CGFloat in
|
||||
if let length = ClashProxy.nameLengthCachedMap[name] {
|
||||
return length
|
||||
}
|
||||
|
||||
let rects = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20)
|
||||
let attr = [NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 14)]
|
||||
let length = (name as NSString)
|
||||
.boundingRect(with: rect,
|
||||
options: .usesLineFragmentOrigin,
|
||||
attributes: attr).width
|
||||
ClashProxy.nameLengthCachedMap[name] = length
|
||||
return length
|
||||
})
|
||||
return lengths?.max() ?? 0
|
||||
}()
|
||||
}
|
||||
|
||||
class ClashProxyResp {
|
||||
var proxies: [ClashProxy]
|
||||
|
||||
var proxiesMap: [ClashProxyName: ClashProxy]
|
||||
|
||||
var enclosingProviderResp: ClashProviderResp?
|
||||
|
||||
init(_ data: Data?) {
|
||||
guard let data
|
||||
else {
|
||||
self.proxiesMap = [:]
|
||||
self.proxies = []
|
||||
return
|
||||
}
|
||||
let proxies = JSON(data)["proxies"]
|
||||
var proxiesModel = [ClashProxy]()
|
||||
|
||||
var proxiesMap = [ClashProxyName: ClashProxy]()
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
decoder.dateDecodingStrategy = .formatted(DateFormatter.js)
|
||||
for value in proxies.dictionaryValue.values {
|
||||
guard let data = try? value.rawData() else {
|
||||
continue
|
||||
}
|
||||
guard let proxy = try? decoder.decode(ClashProxy.self, from: data) else {
|
||||
continue
|
||||
}
|
||||
proxiesModel.append(proxy)
|
||||
proxiesMap[proxy.name] = proxy
|
||||
}
|
||||
self.proxiesMap = proxiesMap
|
||||
self.proxies = proxiesModel
|
||||
|
||||
for proxy in self.proxies {
|
||||
proxy.enclosingResp = self
|
||||
}
|
||||
}
|
||||
|
||||
func updateProvider(_ providerResp: ClashProviderResp) {
|
||||
enclosingProviderResp = providerResp
|
||||
for provider in providerResp.providers.values {
|
||||
for proxy in provider.proxies {
|
||||
proxy.enclosingProvider = provider
|
||||
proxiesMap[proxy.name] = proxy
|
||||
proxies.append(proxy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy var proxiesSortMap: [ClashProxyName: Int] = {
|
||||
var map = [ClashProxyName: Int]()
|
||||
for (idx, proxy) in (self.proxiesMap["GLOBAL"]?.all ?? []).enumerated() {
|
||||
map[proxy] = idx
|
||||
}
|
||||
return map
|
||||
}()
|
||||
|
||||
lazy var proxyGroups: [ClashProxy] = {
|
||||
return proxies.filter {
|
||||
ClashProxyType.isProxyGroup($0)
|
||||
}.sorted(by: { proxiesSortMap[$0.name] ?? -1 < proxiesSortMap[$1.name] ?? -1 })
|
||||
}()
|
||||
|
||||
lazy var longestProxyGroupName = {
|
||||
return proxyGroups.max { $1.name.count > $0.name.count }?.name ?? ""
|
||||
}()
|
||||
|
||||
lazy var maxProxyNameLength: CGFloat = {
|
||||
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20)
|
||||
let attr = [NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 0)]
|
||||
return (self.longestProxyGroupName as NSString)
|
||||
.boundingRect(with: rect,
|
||||
options: .usesLineFragmentOrigin,
|
||||
attributes: attr).width
|
||||
}()
|
||||
}
|
||||
Reference in New Issue
Block a user