158 lines
3.9 KiB
Swift
158 lines
3.9 KiB
Swift
//
|
|
// AlphaMetaDownloader.swift
|
|
// ClashX Meta
|
|
//
|
|
// Copyright © 2023 west2online. All rights reserved.
|
|
//
|
|
|
|
import Cocoa
|
|
import Alamofire
|
|
import PromiseKit
|
|
|
|
class AlphaMetaDownloader: NSObject {
|
|
|
|
enum errors: Error {
|
|
case decodeReleaseInfoFailed
|
|
case notFoundUpdate
|
|
case downloadFailed
|
|
case unknownError
|
|
case testFailed
|
|
|
|
func des() -> String {
|
|
switch self {
|
|
case .decodeReleaseInfoFailed:
|
|
return "Decode alpha release info failed"
|
|
case .notFoundUpdate:
|
|
return "Not found update"
|
|
case .downloadFailed:
|
|
return "Download failed"
|
|
case .testFailed:
|
|
return "Test downloaded file failed"
|
|
case .unknownError:
|
|
return "Unknown error"
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ReleasesResp: Decodable {
|
|
let assets: [Asset]
|
|
struct Asset: Decodable {
|
|
let name: String
|
|
let downloadUrl: String
|
|
let contentType: String
|
|
let state: String
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case name,
|
|
state,
|
|
downloadUrl = "browser_download_url",
|
|
contentType = "content_type"
|
|
}
|
|
}
|
|
}
|
|
|
|
static func assetName() -> String? {
|
|
switch GetMachineHardwareName() {
|
|
case "x86_64":
|
|
return "darwin-amd64"
|
|
case "arm64":
|
|
return "darwin-arm64"
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
static func GetMachineHardwareName() -> String? {
|
|
var sysInfo = utsname()
|
|
let retVal = uname(&sysInfo)
|
|
|
|
guard retVal == EXIT_SUCCESS else { return nil }
|
|
|
|
let machineMirror = Mirror(reflecting: sysInfo.machine)
|
|
let identifier = machineMirror.children.reduce("") { identifier, element in
|
|
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
|
return identifier + String(UnicodeScalar(UInt8(value)))
|
|
}
|
|
return identifier
|
|
}
|
|
|
|
static func alphaAsset() -> Promise<ReleasesResp.Asset> {
|
|
Promise { resolver in
|
|
let assetName = assetName()
|
|
AF.request("https://api.github.com/repos/MetaCubeX/Clash.Meta/releases/tags/Prerelease-Alpha").responseDecodable(of: ReleasesResp.self) {
|
|
guard let assets = $0.value?.assets,
|
|
let assetName,
|
|
let asset = assets.first(where: {
|
|
$0.name.contains(assetName) &&
|
|
!$0.name.contains("cgo") &&
|
|
$0.state == "uploaded" &&
|
|
$0.contentType == "application/gzip"
|
|
}) else {
|
|
resolver.reject(errors.decodeReleaseInfoFailed)
|
|
return
|
|
}
|
|
resolver.fulfill(asset)
|
|
}
|
|
}
|
|
}
|
|
|
|
static func checkVersion(_ asset: ReleasesResp.Asset) -> Promise<ReleasesResp.Asset> {
|
|
Promise { resolver in
|
|
guard let path = Paths.alphaCorePath()?.path,
|
|
let ad = NSApplication.shared.delegate as? AppDelegate else {
|
|
resolver.reject(errors.unknownError)
|
|
return
|
|
}
|
|
if let v = ad.testMetaCore(path),
|
|
asset.name.contains(v.version) {
|
|
resolver.reject(errors.notFoundUpdate)
|
|
}
|
|
resolver.fulfill(asset)
|
|
}
|
|
}
|
|
|
|
static func downloadCore(_ asset: ReleasesResp.Asset) -> Promise<Data> {
|
|
Promise { resolver in
|
|
let fm = FileManager.default
|
|
AF.download(asset.downloadUrl).response {
|
|
guard let gzPath = $0.fileURL?.path,
|
|
let contentData = fm.contents(atPath: gzPath)
|
|
else {
|
|
resolver.reject(errors.downloadFailed)
|
|
return
|
|
}
|
|
resolver.fulfill(contentData)
|
|
}
|
|
}
|
|
}
|
|
|
|
static func replaceCore(_ gzData: Data) -> Promise<String> {
|
|
Promise { resolver in
|
|
let fm = FileManager.default
|
|
|
|
guard let helperURL = Paths.alphaCorePath(),
|
|
let ad = NSApplication.shared.delegate as? AppDelegate else {
|
|
resolver.reject(errors.unknownError)
|
|
return
|
|
}
|
|
|
|
try fm.createDirectory(at: helperURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil)
|
|
|
|
let cachePath = Paths.tempPath().appending("/\(UUID().uuidString).newcore")
|
|
try gzData.gunzipped().write(to: .init(fileURLWithPath: cachePath))
|
|
|
|
Logger.log("save alpha core in \(cachePath)")
|
|
|
|
guard let version = ad.testMetaCore(cachePath)?.version else {
|
|
resolver.reject(errors.testFailed)
|
|
return
|
|
}
|
|
|
|
try? fm.removeItem(at: helperURL)
|
|
try fm.moveItem(atPath: cachePath, toPath: helperURL.path)
|
|
|
|
resolver.fulfill(version)
|
|
}
|
|
}
|
|
}
|