mirror of
https://github.com/X1a0He/Adobe-Downloader.git
synced 2025-11-25 03:14:57 +08:00
fix: Adjust the way to obtain the version number of the Setup component to fix the crash issue under macOS 13.
This commit is contained in:
@@ -30,9 +30,9 @@
|
||||
filePath = "Adobe Downloader/Utils/InstallManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "132"
|
||||
endingLineNumber = "132"
|
||||
landmarkName = "retry(at:progressHandler:logHandler:)"
|
||||
startingLineNumber = "128"
|
||||
endingLineNumber = "128"
|
||||
landmarkName = "retry(at:progressHandler:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
|
||||
@@ -71,6 +71,7 @@ enum NetworkError: Error, LocalizedError {
|
||||
case unsupportedPlatform(String)
|
||||
case incompatibleVersion(String, String)
|
||||
case cancelled
|
||||
case installError(String)
|
||||
|
||||
var errorCode: Int {
|
||||
switch self {
|
||||
@@ -97,6 +98,7 @@ enum NetworkError: Error, LocalizedError {
|
||||
case .unsupportedPlatform: return 7002
|
||||
case .incompatibleVersion: return 7003
|
||||
case .cancelled: return 5004
|
||||
case .installError: return 8001
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,6 +159,8 @@ enum NetworkError: Error, LocalizedError {
|
||||
return NSLocalizedString("版本不兼容: 当前版本 \(current), 需要版本 \(required)", comment: "Incompatible version")
|
||||
case .cancelled:
|
||||
return NSLocalizedString("下载已取消", comment: "Download cancelled")
|
||||
case .installError(let message):
|
||||
return NSLocalizedString("安装错误: \(message)", comment: "Install error")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,6 @@ class NetworkManager: ObservableObject {
|
||||
@Published var loadingState: LoadingState = .idle
|
||||
@Published var downloadTasks: [NewDownloadTask] = []
|
||||
@Published var installationState: InstallationState = .idle
|
||||
@Published var installationLogs: [String] = [] {
|
||||
didSet {
|
||||
if installationLogs.count > 1000 {
|
||||
installationLogs = Array(installationLogs.suffix(1000))
|
||||
}
|
||||
}
|
||||
}
|
||||
@Published var installCommand: String = ""
|
||||
private let cancelTracker = CancelTracker()
|
||||
internal var downloadUtils: DownloadUtils!
|
||||
@@ -205,7 +198,6 @@ class NetworkManager: ObservableObject {
|
||||
func installProduct(at path: URL) async {
|
||||
await MainActor.run {
|
||||
installationState = .installing(progress: 0, status: "准备安装...")
|
||||
installationLogs.removeAll()
|
||||
}
|
||||
|
||||
do {
|
||||
@@ -219,11 +211,6 @@ class NetworkManager: ObservableObject {
|
||||
self.installationState = .installing(progress: progress, status: status)
|
||||
}
|
||||
}
|
||||
},
|
||||
logHandler: { log in
|
||||
Task { @MainActor in
|
||||
self.installationLogs.append(log)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -284,11 +271,6 @@ class NetworkManager: ObservableObject {
|
||||
self.installationState = .installing(progress: progress, status: status)
|
||||
}
|
||||
}
|
||||
},
|
||||
logHandler: { log in
|
||||
Task { @MainActor in
|
||||
self.installationLogs.append(log)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -380,45 +362,4 @@ class NetworkManager: ObservableObject {
|
||||
downloadTasks.append(contentsOf: savedTasks)
|
||||
updateDockBadge()
|
||||
}
|
||||
|
||||
private func fetchProductsWithVersion(_ version: String) async throws -> [String: Sap] {
|
||||
var components = URLComponents(string: NetworkConstants.productsXmlURL)
|
||||
components?.queryItems = [
|
||||
URLQueryItem(name: "_type", value: "xml"),
|
||||
URLQueryItem(name: "channel", value: "ccm"),
|
||||
URLQueryItem(name: "channel", value: "sti"),
|
||||
URLQueryItem(name: "platform", value: "osx10-64,osx10,macarm64,macuniversal"),
|
||||
URLQueryItem(name: "productType", value: "Desktop"),
|
||||
URLQueryItem(name: "version", value: version)
|
||||
]
|
||||
|
||||
guard let url = components?.url else {
|
||||
throw NetworkError.invalidURL(NetworkConstants.productsXmlURL)
|
||||
}
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
NetworkConstants.adobeRequestHeaders.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse else {
|
||||
throw NetworkError.invalidResponse
|
||||
}
|
||||
|
||||
guard (200...299).contains(httpResponse.statusCode) else {
|
||||
throw NetworkError.httpError(httpResponse.statusCode, nil)
|
||||
}
|
||||
|
||||
guard let xmlString = String(data: data, encoding: .utf8) else {
|
||||
throw NetworkError.invalidData("无法解码XML数据")
|
||||
}
|
||||
|
||||
let result = try await Task.detached(priority: .userInitiated) {
|
||||
let parseResult = try XHXMLParser.parse(xmlString: xmlString)
|
||||
return parseResult.products
|
||||
}.value
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,10 +505,15 @@ class DownloadUtils {
|
||||
|
||||
let (manifestData, _) = try await URLSession.shared.data(for: request)
|
||||
|
||||
let manifestXML = try XMLDocument(data: manifestData)
|
||||
#if DEBUG
|
||||
if let manifestString = String(data: manifestData, encoding: .utf8) {
|
||||
print("Manifest内容: \(manifestString)")
|
||||
}
|
||||
#endif
|
||||
let manifestDoc = try XMLDocument(data: manifestData)
|
||||
|
||||
guard let downloadPath = try manifestXML.nodes(forXPath: "//asset_list/asset/asset_path").first?.stringValue,
|
||||
let assetSizeStr = try manifestXML.nodes(forXPath: "//asset_list/asset/asset_size").first?.stringValue,
|
||||
guard let downloadPath = try manifestDoc.nodes(forXPath: "//asset_list/asset/asset_path").first?.stringValue,
|
||||
let assetSizeStr = try manifestDoc.nodes(forXPath: "//asset_list/asset/asset_size").first?.stringValue,
|
||||
let assetSize = Int64(assetSizeStr) else {
|
||||
throw NetworkError.invalidData("无法从manifest中获取下载信息")
|
||||
}
|
||||
@@ -950,140 +955,181 @@ class DownloadUtils {
|
||||
}
|
||||
}
|
||||
|
||||
func downloadSetupComponents(
|
||||
func downloadX1a0HeCCPackages(
|
||||
progressHandler: @escaping (Double, String) -> Void,
|
||||
cancellationHandler: @escaping () -> Bool
|
||||
) async throws {
|
||||
let architecture = AppStatics.isAppleSilicon ? "arm" : "intel"
|
||||
let baseURLs = [
|
||||
"https://github.com/X1a0He/Adobe-Downloader/raw/refs/heads/main/X1a0HeCC/\(architecture)/HDBox/",
|
||||
"https://github.com/X1a0He/Adobe-Downloader/raw/refs/heads/develop/X1a0HeCC/\(architecture)/HDBox/"
|
||||
]
|
||||
|
||||
let components = [
|
||||
"HDHelper",
|
||||
"HDIM.dylib",
|
||||
"HDPIM.dylib",
|
||||
"HUM.dylib",
|
||||
"Setup"
|
||||
]
|
||||
|
||||
let targetDirectory = "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox"
|
||||
let baseUrl = "https://cdn-ffc.oobesaas.adobe.com/core/v1/applications?name=CreativeCloud&platform=\(AppStatics.isAppleSilicon ? "macarm64" : "osx10")"
|
||||
|
||||
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
try FileManager.default.createDirectory(at: tempDirectory, withIntermediateDirectories: true)
|
||||
|
||||
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: tempDirectory.path)
|
||||
|
||||
let configuration = URLSessionConfiguration.default
|
||||
configuration.timeoutIntervalForRequest = 30
|
||||
configuration.timeoutIntervalForResource = 300
|
||||
configuration.httpAdditionalHeaders = NetworkConstants.downloadHeaders
|
||||
let session = URLSession(configuration: configuration)
|
||||
|
||||
do {
|
||||
var request = URLRequest(url: URL(string: baseUrl)!)
|
||||
NetworkConstants.downloadHeaders.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
|
||||
let (data, response) = try await session.data(for: request)
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse,
|
||||
(200...299).contains(httpResponse.statusCode) else {
|
||||
throw NetworkError.invalidResponse
|
||||
}
|
||||
|
||||
let xmlDoc = try XMLDocument(data: data)
|
||||
|
||||
for (index, component) in components.enumerated() {
|
||||
if cancellationHandler() {
|
||||
try? FileManager.default.removeItem(at: tempDirectory)
|
||||
throw NetworkError.cancelled
|
||||
let packageSets = try xmlDoc.nodes(forXPath: "//packageSet[name='ADC']")
|
||||
guard let adcPackageSet = packageSets.first else {
|
||||
throw NetworkError.invalidData("找不到ADC包集")
|
||||
}
|
||||
|
||||
await MainActor.run {
|
||||
progressHandler(Double(index) / Double(components.count), "正在下载 \(component)...")
|
||||
let targetPackages = ["HDBox", "IPCBox"]
|
||||
var packagesToDownload: [(name: String, url: URL, size: Int64)] = []
|
||||
|
||||
for packageName in targetPackages {
|
||||
let packageNodes = try adcPackageSet.nodes(forXPath: ".//package[name='\(packageName)']")
|
||||
guard let package = packageNodes.first else {
|
||||
print("未找到包: \(packageName)")
|
||||
continue
|
||||
}
|
||||
|
||||
guard let manifestUrl = try package.nodes(forXPath: ".//manifestUrl").first?.stringValue,
|
||||
let cdnBase = try xmlDoc.nodes(forXPath: "//cdn/secure").first?.stringValue else {
|
||||
print("无法获取manifest URL或CDN基础URL")
|
||||
continue
|
||||
}
|
||||
|
||||
let manifestFullUrl = cdnBase + manifestUrl
|
||||
|
||||
var manifestRequest = URLRequest(url: URL(string: manifestFullUrl)!)
|
||||
NetworkConstants.downloadHeaders.forEach { manifestRequest.setValue($0.value, forHTTPHeaderField: $0.key) }
|
||||
let (manifestData, manifestResponse) = try await session.data(for: manifestRequest)
|
||||
|
||||
guard let manifestHttpResponse = manifestResponse as? HTTPURLResponse,
|
||||
(200...299).contains(manifestHttpResponse.statusCode) else {
|
||||
print("获取manifest失败: HTTP \(String(describing: (manifestResponse as? HTTPURLResponse)?.statusCode))")
|
||||
continue
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if let manifestString = String(data: manifestData, encoding: .utf8) {
|
||||
print("Manifest内容: \(manifestString)")
|
||||
}
|
||||
#endif
|
||||
let manifestDoc = try XMLDocument(data: manifestData)
|
||||
let assetPathNodes = try manifestDoc.nodes(forXPath: "//asset_path")
|
||||
let sizeNodes = try manifestDoc.nodes(forXPath: "//asset_size")
|
||||
guard let assetPath = assetPathNodes.first?.stringValue,
|
||||
let sizeStr = sizeNodes.first?.stringValue,
|
||||
let size = Int64(sizeStr),
|
||||
let downloadUrl = URL(string: assetPath) else {
|
||||
continue
|
||||
}
|
||||
packagesToDownload.append((packageName, downloadUrl, size))
|
||||
}
|
||||
|
||||
guard !packagesToDownload.isEmpty else {
|
||||
throw NetworkError.invalidData("没有找到可下载的包")
|
||||
}
|
||||
|
||||
var lastError: Error? = nil
|
||||
var downloaded = false
|
||||
|
||||
for baseURL in baseURLs {
|
||||
guard !downloaded else { break }
|
||||
let totalCount = packagesToDownload.count
|
||||
for (index, package) in packagesToDownload.enumerated() {
|
||||
if cancellationHandler() {
|
||||
try? FileManager.default.removeItem(at: tempDirectory)
|
||||
throw NetworkError.cancelled
|
||||
}
|
||||
|
||||
await MainActor.run {
|
||||
progressHandler(Double(index) / Double(totalCount), "正在下载 \(package.name)...")
|
||||
}
|
||||
|
||||
let url = URL(string: baseURL + component)!
|
||||
let destinationURL = tempDirectory.appendingPathComponent(component)
|
||||
print(url)
|
||||
do {
|
||||
let (downloadURL, response) = try await session.download(from: url)
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse else {
|
||||
throw NetworkError.invalidResponse
|
||||
}
|
||||
|
||||
if httpResponse.statusCode == 404 {
|
||||
lastError = NetworkError.fileNotFound(component)
|
||||
continue
|
||||
}
|
||||
|
||||
if !(200...299).contains(httpResponse.statusCode) {
|
||||
lastError = NetworkError.httpError(httpResponse.statusCode, "下载 \(component) 失败")
|
||||
continue
|
||||
}
|
||||
|
||||
try FileManager.default.moveItem(at: downloadURL, to: destinationURL)
|
||||
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: destinationURL.path)
|
||||
downloaded = true
|
||||
|
||||
} catch URLError.timedOut {
|
||||
lastError = NetworkError.timeout
|
||||
continue
|
||||
} catch {
|
||||
lastError = error is NetworkError ? error : NetworkError.downloadError("下载 \(component) 失败: \(error.localizedDescription)", error)
|
||||
let destinationURL = tempDirectory.appendingPathComponent("\(package.name).zip")
|
||||
var downloadRequest = URLRequest(url: package.url)
|
||||
NetworkConstants.downloadHeaders.forEach { downloadRequest.setValue($0.value, forHTTPHeaderField: $0.key) }
|
||||
let (downloadURL, downloadResponse) = try await session.download(for: downloadRequest)
|
||||
|
||||
guard let httpResponse = downloadResponse as? HTTPURLResponse,
|
||||
(200...299).contains(httpResponse.statusCode) else {
|
||||
print("下载失败: HTTP \(String(describing: (downloadResponse as? HTTPURLResponse)?.statusCode))")
|
||||
continue
|
||||
}
|
||||
|
||||
try FileManager.default.moveItem(at: downloadURL, to: destinationURL)
|
||||
}
|
||||
|
||||
if !downloaded {
|
||||
try? FileManager.default.removeItem(at: tempDirectory)
|
||||
throw lastError ?? NetworkError.downloadError("无法下载 \(component)", nil)
|
||||
await MainActor.run {
|
||||
progressHandler(0.9, "正在安装组件...")
|
||||
}
|
||||
}
|
||||
|
||||
let targetDirectory = "/Library/Application Support/Adobe/Adobe Desktop Common"
|
||||
|
||||
await MainActor.run {
|
||||
progressHandler(1.0, "下载完成,正在安装...")
|
||||
}
|
||||
if !FileManager.default.fileExists(atPath: targetDirectory) {
|
||||
let baseCommands = [
|
||||
"mkdir -p '\(targetDirectory)'",
|
||||
"chmod 755 '\(targetDirectory)'"
|
||||
]
|
||||
|
||||
let commands = [
|
||||
"mkdir -p '\(targetDirectory)'",
|
||||
"chmod 755 '\(targetDirectory)'",
|
||||
"cp -f '\(tempDirectory.path)/'* '\(targetDirectory)/'",
|
||||
"chmod 755 '\(targetDirectory)/'*",
|
||||
"chown -R root:wheel '\(targetDirectory)'"
|
||||
]
|
||||
|
||||
for command in commands {
|
||||
let result = await withCheckedContinuation { continuation in
|
||||
PrivilegedHelperManager.shared.executeCommand(command) { result in
|
||||
continuation.resume(returning: result)
|
||||
for command in baseCommands {
|
||||
let result = await withCheckedContinuation { continuation in
|
||||
PrivilegedHelperManager.shared.executeCommand(command) { result in
|
||||
continuation.resume(returning: result)
|
||||
}
|
||||
}
|
||||
|
||||
if result.starts(with: "Error:") {
|
||||
try? FileManager.default.removeItem(at: tempDirectory)
|
||||
throw NetworkError.installError("创建目录失败: \(result)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.starts(with: "Error:") {
|
||||
try? FileManager.default.removeItem(at: tempDirectory)
|
||||
throw NSError(domain: "SetupComponentsError",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: "安装组件失败: \(result)"])
|
||||
}
|
||||
}
|
||||
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
ModifySetup.backupAndModifySetupFile { success, message in
|
||||
if success {
|
||||
continuation.resume()
|
||||
} else {
|
||||
continuation.resume(throwing: NSError(domain: "SetupComponentsError",
|
||||
code: -1,
|
||||
userInfo: [NSLocalizedDescriptionKey: message]))
|
||||
for package in packagesToDownload {
|
||||
let packageDir = "\(targetDirectory)/\(package.name)"
|
||||
let packageCommands = [
|
||||
"mkdir -p '\(packageDir)'",
|
||||
"unzip -o '\(tempDirectory.path)/\(package.name).zip' -d '\(packageDir)/'",
|
||||
"chmod -R 755 '\(packageDir)'",
|
||||
"chown -R root:wheel '\(packageDir)'"
|
||||
]
|
||||
|
||||
for command in packageCommands {
|
||||
let result = await withCheckedContinuation { continuation in
|
||||
PrivilegedHelperManager.shared.executeCommand(command) { result in
|
||||
continuation.resume(returning: result)
|
||||
}
|
||||
}
|
||||
|
||||
if result.starts(with: "Error:") {
|
||||
try? FileManager.default.removeItem(at: tempDirectory)
|
||||
throw NetworkError.installError("安装 \(package.name) 失败: \(result)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try? FileManager.default.removeItem(at: tempDirectory)
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
ModifySetup.backupAndModifySetupFile { success, message in
|
||||
if success {
|
||||
continuation.resume()
|
||||
} else {
|
||||
continuation.resume(throwing: NetworkError.installError(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModifySetup.clearVersionCache()
|
||||
ModifySetup.clearVersionCache()
|
||||
|
||||
await MainActor.run {
|
||||
progressHandler(1.0, "安装完成")
|
||||
try? FileManager.default.removeItem(at: tempDirectory)
|
||||
|
||||
await MainActor.run {
|
||||
progressHandler(1.0, "安装完成")
|
||||
}
|
||||
} catch {
|
||||
print("发生错误: \(error.localizedDescription)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,7 @@ actor InstallManager {
|
||||
|
||||
private func executeInstallation(
|
||||
at appPath: URL,
|
||||
progressHandler: @escaping (Double, String) -> Void,
|
||||
logHandler: @escaping (String) -> Void
|
||||
progressHandler: @escaping (Double, String) -> Void
|
||||
) async throws {
|
||||
guard FileManager.default.fileExists(atPath: setupPath) else {
|
||||
throw InstallError.setupNotFound
|
||||
@@ -53,13 +52,13 @@ actor InstallManager {
|
||||
do {
|
||||
try await PrivilegedHelperManager.shared.executeInstallation(installCommand) { output in
|
||||
Task { @MainActor in
|
||||
logHandler(output)
|
||||
if let range = output.range(of: "Exit Code: (-?[0-9]+)", options: .regularExpression),
|
||||
let codeStr = output[range].split(separator: ":").last?.trimmingCharacters(in: .whitespaces),
|
||||
let exitCode = Int(codeStr) {
|
||||
|
||||
if exitCode == 0 {
|
||||
progressHandler(1.0, String(localized: "安装完成"))
|
||||
PrivilegedHelperManager.shared.executeCommand("pkill -f Setup") { _ in }
|
||||
continuation.resume()
|
||||
} else {
|
||||
let errorMessage: String
|
||||
@@ -104,13 +103,11 @@ actor InstallManager {
|
||||
|
||||
func install(
|
||||
at appPath: URL,
|
||||
progressHandler: @escaping (Double, String) -> Void,
|
||||
logHandler: @escaping (String) -> Void
|
||||
progressHandler: @escaping (Double, String) -> Void
|
||||
) async throws {
|
||||
try await executeInstallation(
|
||||
at: appPath,
|
||||
progressHandler: progressHandler,
|
||||
logHandler: logHandler
|
||||
progressHandler: progressHandler
|
||||
)
|
||||
}
|
||||
|
||||
@@ -124,13 +121,11 @@ actor InstallManager {
|
||||
|
||||
func retry(
|
||||
at appPath: URL,
|
||||
progressHandler: @escaping (Double, String) -> Void,
|
||||
logHandler: @escaping (String) -> Void
|
||||
progressHandler: @escaping (Double, String) -> Void
|
||||
) async throws {
|
||||
try await executeInstallation(
|
||||
at: appPath,
|
||||
progressHandler: progressHandler,
|
||||
logHandler: logHandler
|
||||
progressHandler: progressHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ class ModifySetup {
|
||||
}
|
||||
|
||||
static func isSetupBackup() -> Bool {
|
||||
return FileManager.default.fileExists(atPath: "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup.original")
|
||||
return isSetupExists() && FileManager.default.fileExists(atPath: "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup.original")
|
||||
}
|
||||
|
||||
static func checkComponentVersion() -> String {
|
||||
let setupPath = "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup"
|
||||
|
||||
guard isSetupExists() else {
|
||||
guard FileManager.default.fileExists(atPath: setupPath) else {
|
||||
cachedVersion = nil
|
||||
return String(localized: "未找到 Setup 组件")
|
||||
}
|
||||
@@ -30,42 +30,42 @@ class ModifySetup {
|
||||
if let cachedVersion = cachedVersion {
|
||||
return cachedVersion
|
||||
}
|
||||
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: setupPath)) else {
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
let process = Process()
|
||||
process.executableURL = URL(fileURLWithPath: "/usr/bin/strings")
|
||||
process.arguments = [setupPath]
|
||||
let versionMarkers = ["Version ", "Adobe Setup Version: "]
|
||||
|
||||
for marker in versionMarkers {
|
||||
if let markerData = marker.data(using: .utf8),
|
||||
let markerRange = data.range(of: markerData) {
|
||||
let versionStart = markerRange.upperBound
|
||||
let searchRange = versionStart..<min(versionStart + 30, data.count)
|
||||
|
||||
let pipe = Pipe()
|
||||
process.standardOutput = pipe
|
||||
process.standardError = pipe
|
||||
|
||||
do {
|
||||
try process.run()
|
||||
process.waitUntilExit()
|
||||
|
||||
if let output = try pipe.fileHandleForReading.readToEnd(),
|
||||
let outputString = String(data: output, encoding: .utf8) {
|
||||
let lines = outputString.components(separatedBy: .newlines)
|
||||
for (index, line) in lines.enumerated() {
|
||||
if line == "Adobe Setup Version: %s" && index + 1 < lines.count {
|
||||
let version = lines[index + 1].trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if version.range(of: "^[0-9.]+$", options: .regularExpression) != nil {
|
||||
cachedVersion = version
|
||||
return version
|
||||
}
|
||||
}
|
||||
var versionBytes: [UInt8] = []
|
||||
for i in searchRange {
|
||||
let byte = data[i]
|
||||
if (byte >= 0x30 && byte <= 0x39) || byte == 0x2E || byte == 0x20 {
|
||||
versionBytes.append(byte)
|
||||
} else if byte == 0x28 {
|
||||
break
|
||||
} else if versionBytes.isEmpty {
|
||||
continue
|
||||
} else { break }
|
||||
}
|
||||
|
||||
if let version = String(bytes: versionBytes, encoding: .utf8)?.trimmingCharacters(in: .whitespaces),
|
||||
!version.isEmpty {
|
||||
cachedVersion = version
|
||||
return version
|
||||
}
|
||||
}
|
||||
|
||||
let message = String(localized: "未知 Setup 组件版本号")
|
||||
cachedVersion = message
|
||||
return message
|
||||
} catch {
|
||||
print("Error checking Setup version: \(error)")
|
||||
let message = String(localized: "未知 Setup 组件版本号")
|
||||
cachedVersion = message
|
||||
return message
|
||||
}
|
||||
|
||||
let message = String(localized: "未知 Setup 组件版本号")
|
||||
cachedVersion = message
|
||||
return message
|
||||
}
|
||||
|
||||
static func clearVersionCache() {
|
||||
@@ -152,5 +152,25 @@ class ModifySetup {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func isSetupModified() -> Bool {
|
||||
let setupPath = "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup"
|
||||
|
||||
guard FileManager.default.fileExists(atPath: setupPath) else { return false }
|
||||
|
||||
let intelPattern = Data([0x55, 0x48, 0x89, 0xE5, 0x53, 0x50, 0x48, 0x89, 0xFB, 0x48, 0x8B, 0x05, 0x70, 0xC7, 0x03, 0x00, 0x48, 0x8B, 0x00, 0x48, 0x89, 0x45, 0xF0, 0xE8, 0x24, 0xD7, 0xFE, 0xFF, 0x48, 0x83, 0xC3, 0x08, 0x48, 0x39, 0xD8, 0x0F])
|
||||
|
||||
let armPattern = Data([0xFF, 0xC3, 0x00, 0xD1, 0xF4, 0x4F, 0x01, 0xA9, 0xFD, 0x7B, 0x02, 0xA9, 0xFD, 0x83, 0x00, 0x91, 0xF3, 0x03, 0x00, 0xAA, 0x1F, 0x20, 0x03, 0xD5, 0x68, 0xA1, 0x1D, 0x58, 0x08, 0x01, 0x40, 0xF9, 0xE8, 0x07, 0x00, 0xF9])
|
||||
|
||||
do {
|
||||
let fileData = try Data(contentsOf: URL(fileURLWithPath: setupPath))
|
||||
if fileData.range(of: intelPattern) != nil || fileData.range(of: armPattern) != nil { return false }
|
||||
return true
|
||||
|
||||
} catch {
|
||||
print("Error reading Setup file: \(error)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,11 +160,11 @@ struct GeneralSettingsView: View {
|
||||
Form {
|
||||
DownloadSettingsView(viewModel: viewModel)
|
||||
|
||||
OtherSettingsView(viewModel: viewModel,
|
||||
HelperSettingsView(viewModel: viewModel,
|
||||
showHelperAlert: $showHelperAlert,
|
||||
helperAlertMessage: $helperAlertMessage,
|
||||
helperAlertSuccess: $helperAlertSuccess)
|
||||
|
||||
CCSettingsView(viewModel: viewModel)
|
||||
UpdateSettingsView(viewModel: viewModel)
|
||||
}
|
||||
.padding()
|
||||
@@ -181,7 +181,7 @@ struct GeneralSettingsView: View {
|
||||
viewModel.isDownloadingSetup = true
|
||||
viewModel.isCancelled = false
|
||||
do {
|
||||
try await networkManager.downloadUtils.downloadSetupComponents(
|
||||
try await networkManager.downloadUtils.downloadX1a0HeCCPackages(
|
||||
progressHandler: { progress, status in
|
||||
viewModel.setupDownloadProgress = progress
|
||||
viewModel.setupDownloadStatus = status
|
||||
@@ -212,7 +212,7 @@ struct GeneralSettingsView: View {
|
||||
viewModel.isDownloadingSetup = true
|
||||
viewModel.isCancelled = false
|
||||
do {
|
||||
try await networkManager.downloadUtils.downloadSetupComponents(
|
||||
try await networkManager.downloadUtils.downloadX1a0HeCCPackages(
|
||||
progressHandler: { progress, status in
|
||||
viewModel.setupDownloadProgress = progress
|
||||
viewModel.setupDownloadStatus = status
|
||||
@@ -234,7 +234,7 @@ struct GeneralSettingsView: View {
|
||||
}
|
||||
}
|
||||
} message: {
|
||||
Text("确定要下载并安装 Setup 组件吗?")
|
||||
Text("确定要下载并安装 X1a0He CC 吗?")
|
||||
}
|
||||
.alert("确认重新处理", isPresented: $viewModel.showReprocessConfirmAlert) {
|
||||
Button("取消", role: .cancel) { }
|
||||
@@ -281,19 +281,30 @@ struct DownloadSettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct OtherSettingsView: View {
|
||||
struct HelperSettingsView: View {
|
||||
@ObservedObject var viewModel: GeneralSettingsViewModel
|
||||
@Binding var showHelperAlert: Bool
|
||||
@Binding var helperAlertMessage: String
|
||||
@Binding var helperAlertSuccess: Bool
|
||||
|
||||
var body: some View {
|
||||
GroupBox(label: Text("其他设置").padding(.bottom, 8)) {
|
||||
GroupBox(label: Text("Helper 设置").padding(.bottom, 8)) {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
HelperStatusRow(viewModel: viewModel, showHelperAlert: $showHelperAlert,
|
||||
helperAlertMessage: $helperAlertMessage,
|
||||
helperAlertSuccess: $helperAlertSuccess)
|
||||
Divider()
|
||||
}
|
||||
.padding(8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CCSettingsView: View {
|
||||
@ObservedObject var viewModel: GeneralSettingsViewModel
|
||||
|
||||
var body: some View {
|
||||
GroupBox(label: Text("X1a0He CC设置").padding(.bottom, 8)) {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
SetupComponentRow(viewModel: viewModel)
|
||||
}
|
||||
.padding(8)
|
||||
@@ -597,11 +608,35 @@ struct SetupComponentRow: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
HStack {
|
||||
Text("Setup 组件状态: ")
|
||||
Text("X1a0He CC 备份状态: ")
|
||||
if ModifySetup.isSetupBackup() {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("已备份处理")
|
||||
Text("已备份")
|
||||
} else {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundColor(.red)
|
||||
Text("(可能导致处理 Setup 组件失败)")
|
||||
}
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
if !ModifySetup.isSetupExists() {
|
||||
viewModel.showDownloadAlert = true
|
||||
} else {
|
||||
viewModel.showReprocessConfirmAlert = true
|
||||
}
|
||||
}) {
|
||||
Text("重新备份")
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
HStack {
|
||||
Text("X1a0He CC 处理状态: ")
|
||||
if ModifySetup.isSetupModified() {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("已处理")
|
||||
} else {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundColor(.red)
|
||||
@@ -621,7 +656,7 @@ struct SetupComponentRow: View {
|
||||
}
|
||||
Divider()
|
||||
HStack {
|
||||
Text("Setup 组件版本: \(viewModel.setupVersion)")
|
||||
Text("X1a0He CC 版本信息: \(viewModel.setupVersion)")
|
||||
Spacer()
|
||||
|
||||
if viewModel.isDownloadingSetup {
|
||||
@@ -637,7 +672,7 @@ struct SetupComponentRow: View {
|
||||
Button(action: {
|
||||
viewModel.showDownloadConfirmAlert = true
|
||||
}) {
|
||||
Text("从 GitHub 下载 Setup 组件")
|
||||
Text("下载 X1a0He CC")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,6 @@ struct InstallProgressView: View {
|
||||
ProgressSection(progress: progress, progressText: progressText)
|
||||
}
|
||||
|
||||
LogSection(logs: networkManager.installationLogs)
|
||||
|
||||
if isFailed {
|
||||
ErrorSection(
|
||||
status: status, isFailed: isFailed
|
||||
@@ -91,7 +89,7 @@ struct InstallProgressView: View {
|
||||
)
|
||||
}
|
||||
.padding()
|
||||
.frame(minWidth: 500, minHeight: 400)
|
||||
.frame(minWidth: 500)
|
||||
.background(Color(NSColor.windowBackgroundColor))
|
||||
.cornerRadius(8)
|
||||
}
|
||||
@@ -116,45 +114,6 @@ private struct ProgressSection: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct LogSection: View {
|
||||
let logs: [String]
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { proxy in
|
||||
ScrollView(showsIndicators: true) {
|
||||
LazyVStack(alignment: .leading, spacing: 2) {
|
||||
ForEach(Array(logs.suffix(1000).enumerated()), id: \.offset) { index, log in
|
||||
Text(log)
|
||||
.font(.system(.caption, design: .monospaced))
|
||||
.foregroundColor(.secondary)
|
||||
.id(index)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 2)
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.frame(height: 150)
|
||||
.background(Color(NSColor.textBackgroundColor))
|
||||
.cornerRadius(4)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(Color.secondary.opacity(0.2), lineWidth: 1)
|
||||
)
|
||||
.padding(.horizontal, 20)
|
||||
.onChange(of: logs) { newValue in
|
||||
if !newValue.isEmpty {
|
||||
withAnimation {
|
||||
proxy.scrollTo(newValue.count - 1, anchor: .bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ErrorSection: View {
|
||||
let status: String
|
||||
let isFailed: Bool
|
||||
@@ -301,7 +260,7 @@ private struct CommandPopover: View {
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("安装中带日志") {
|
||||
#Preview("安装中") {
|
||||
let networkManager = NetworkManager()
|
||||
return InstallProgressView(
|
||||
productName: "Adobe Photoshop",
|
||||
@@ -311,26 +270,9 @@ private struct CommandPopover: View {
|
||||
onRetry: nil
|
||||
)
|
||||
.environmentObject(networkManager)
|
||||
.onAppear {
|
||||
let previewLogs = [
|
||||
"正在准备安装...",
|
||||
"Progress: 10%",
|
||||
"Progress: 20%",
|
||||
"Progress: 30%",
|
||||
"Progress: 40%",
|
||||
"Progress: 45%",
|
||||
"正在安装核心组件...",
|
||||
]
|
||||
|
||||
for (index, log) in previewLogs.enumerated() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.5) {
|
||||
networkManager.installationLogs.append(log)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("安装失败带日志") {
|
||||
#Preview("安装失败") {
|
||||
let networkManager = NetworkManager()
|
||||
return InstallProgressView(
|
||||
productName: "Adobe Photoshop",
|
||||
@@ -342,25 +284,10 @@ private struct CommandPopover: View {
|
||||
.environmentObject(networkManager)
|
||||
.onAppear {
|
||||
networkManager.installCommand = "sudo \"/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup\" --install=1 --driverXML=\"/Users/demo/Downloads/Adobe Photoshop/driver.xml\""
|
||||
|
||||
let previewLogs = [
|
||||
"正在准备安装...",
|
||||
"Progress: 10%",
|
||||
"Progress: 20%",
|
||||
"检查权限...",
|
||||
"权限检查失败",
|
||||
"安装失败: 权限被拒绝"
|
||||
]
|
||||
|
||||
for (index, log) in previewLogs.enumerated() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.5) {
|
||||
networkManager.installationLogs.append(log)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("安装完成带日志") {
|
||||
#Preview("安装完成") {
|
||||
let networkManager = NetworkManager()
|
||||
return InstallProgressView(
|
||||
productName: "Adobe Photoshop",
|
||||
@@ -370,26 +297,9 @@ private struct CommandPopover: View {
|
||||
onRetry: nil
|
||||
)
|
||||
.environmentObject(networkManager)
|
||||
.onAppear {
|
||||
let previewLogs = [
|
||||
"正在准备安装...",
|
||||
"Progress: 25%",
|
||||
"Progress: 50%",
|
||||
"Progress: 75%",
|
||||
"Progress: 100%",
|
||||
"正在完成安装...",
|
||||
"安装完成"
|
||||
]
|
||||
|
||||
for (index, log) in previewLogs.enumerated() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.5) {
|
||||
networkManager.installationLogs.append(log)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("在深色模式下带日志") {
|
||||
#Preview("在深色模式下") {
|
||||
let networkManager = NetworkManager()
|
||||
return InstallProgressView(
|
||||
productName: "Adobe Photoshop",
|
||||
@@ -400,19 +310,4 @@ private struct CommandPopover: View {
|
||||
)
|
||||
.environmentObject(networkManager)
|
||||
.preferredColorScheme(.dark)
|
||||
.onAppear {
|
||||
let previewLogs = [
|
||||
"正在准备安装...",
|
||||
"Progress: 25%",
|
||||
"Progress: 50%",
|
||||
"Progress: 75%",
|
||||
"正在安装..."
|
||||
]
|
||||
|
||||
for (index, log) in previewLogs.enumerated() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.5) {
|
||||
networkManager.installationLogs.append(log)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,134 +20,19 @@ struct ShouldExistsSetUpView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 20) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.font(.system(size: 64))
|
||||
.foregroundColor(.orange)
|
||||
.padding(.bottom, 5)
|
||||
.frame(alignment: .bottomTrailing)
|
||||
|
||||
Text("未检测到 Adobe Setup 组件")
|
||||
.font(.system(size: 24))
|
||||
.bold()
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text("程序检测到你的系统中不存在 Adobe Setup 组件")
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Text("可能导致无法使用安装功能,请确保是否使用安装功能")
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
|
||||
VStack(spacing: 16) {
|
||||
Button(action: {
|
||||
showingAlert = true
|
||||
}) {
|
||||
Label("不使用安装功能", systemImage: "exclamationmark.triangle.fill")
|
||||
.frame(minWidth: 0,maxWidth: 360)
|
||||
.frame(height: 32)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.orange)
|
||||
.alert("确认", isPresented: $showingAlert) {
|
||||
Button("取消", role: .cancel) { }
|
||||
Button("确定", role: .destructive) {
|
||||
dismiss()
|
||||
}
|
||||
} message: {
|
||||
Text("你确定不使用安装功能吗?")
|
||||
}
|
||||
.disabled(isDownloading)
|
||||
Button(action: {
|
||||
isDownloading = true
|
||||
isCancelled = false
|
||||
Task {
|
||||
do {
|
||||
try await networkManager.downloadUtils.downloadSetupComponents(
|
||||
progressHandler: { progress, status in
|
||||
Task { @MainActor in
|
||||
downloadProgress = progress
|
||||
downloadStatus = status
|
||||
}
|
||||
},
|
||||
cancellationHandler: { isCancelled }
|
||||
)
|
||||
await MainActor.run {
|
||||
dismiss()
|
||||
}
|
||||
} catch NetworkError.cancelled {
|
||||
await MainActor.run {
|
||||
isDownloading = false
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
isDownloading = false
|
||||
errorMessage = error.localizedDescription
|
||||
showErrorAlert = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}) {
|
||||
if isDownloading {
|
||||
VStack {
|
||||
ProgressView(value: downloadProgress) {
|
||||
Text(downloadStatus)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
Text("\(Int(downloadProgress * 100))%")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.secondary)
|
||||
Button("取消") {
|
||||
isCancelled = true
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
}
|
||||
.frame(maxWidth: 360)
|
||||
.progressViewStyle(.linear)
|
||||
.tint(.green)
|
||||
} else {
|
||||
Label("下载 X1a0He CC 组件", systemImage: "arrow.down")
|
||||
.frame(minWidth: 0, maxWidth: 360)
|
||||
.frame(height: 32)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.blue)
|
||||
.disabled(isDownloading)
|
||||
|
||||
Button(action: {
|
||||
if let url = URL(string: "https://creativecloud.adobe.com/apps/download/creative-cloud") {
|
||||
NSWorkspace.shared.open(url)
|
||||
dismiss()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Label("前往 Adobe Creative Cloud", systemImage: "cloud.fill")
|
||||
.frame(minWidth: 0,maxWidth: 360)
|
||||
.frame(height: 32)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.disabled(isDownloading)
|
||||
|
||||
Button(action: {
|
||||
dismiss()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
}) {
|
||||
Label("退出", systemImage: "xmark")
|
||||
.frame(minWidth: 0, maxWidth: 360)
|
||||
.frame(height: 32)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.red)
|
||||
.keyboardShortcut(.cancelAction)
|
||||
.disabled(isDownloading)
|
||||
}
|
||||
HeaderView()
|
||||
MessageView()
|
||||
ButtonsView(
|
||||
isDownloading: $isDownloading,
|
||||
downloadProgress: $downloadProgress,
|
||||
downloadStatus: $downloadStatus,
|
||||
isCancelled: $isCancelled,
|
||||
showingAlert: $showingAlert,
|
||||
showErrorAlert: $showErrorAlert,
|
||||
errorMessage: $errorMessage,
|
||||
dismiss: dismiss,
|
||||
networkManager: networkManager
|
||||
)
|
||||
}
|
||||
.frame(width: 500)
|
||||
.padding()
|
||||
@@ -162,6 +47,179 @@ struct ShouldExistsSetUpView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct HeaderView: View {
|
||||
var body: some View {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.font(.system(size: 64))
|
||||
.foregroundColor(.orange)
|
||||
.padding(.bottom, 5)
|
||||
.frame(alignment: .bottomTrailing)
|
||||
|
||||
Text("未检测到 Adobe CC 组件")
|
||||
.font(.system(size: 24))
|
||||
.bold()
|
||||
}
|
||||
}
|
||||
|
||||
private struct MessageView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 4) {
|
||||
Text("程序检测到你的系统中不存在 Adobe CC 组件")
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Text("可能导致无法使用安装功能,请确保是否使用安装功能")
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ButtonsView: View {
|
||||
@Binding var isDownloading: Bool
|
||||
@Binding var downloadProgress: Double
|
||||
@Binding var downloadStatus: String
|
||||
@Binding var isCancelled: Bool
|
||||
@Binding var showingAlert: Bool
|
||||
@Binding var showErrorAlert: Bool
|
||||
@Binding var errorMessage: String
|
||||
let dismiss: DismissAction
|
||||
let networkManager: NetworkManager
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 16) {
|
||||
notUseButton
|
||||
downloadButton
|
||||
creativeCloudButton
|
||||
exitButton
|
||||
}
|
||||
}
|
||||
|
||||
private var notUseButton: some View {
|
||||
Button(action: { showingAlert = true }) {
|
||||
Label("不使用安装功能", systemImage: "exclamationmark.triangle.fill")
|
||||
.frame(minWidth: 0, maxWidth: 360)
|
||||
.frame(height: 32)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.orange)
|
||||
.alert("确认", isPresented: $showingAlert) {
|
||||
Button("取消", role: .cancel) { }
|
||||
Button("确定", role: .destructive) {
|
||||
dismiss()
|
||||
}
|
||||
} message: {
|
||||
Text("你确定不使用安装功能吗?")
|
||||
}
|
||||
.disabled(isDownloading)
|
||||
}
|
||||
|
||||
private var downloadButton: some View {
|
||||
Button(action: startDownload) {
|
||||
if isDownloading {
|
||||
downloadProgressView
|
||||
} else {
|
||||
Label("下载 X1a0He CC 组件", systemImage: "arrow.down")
|
||||
.frame(minWidth: 0, maxWidth: 360)
|
||||
.frame(height: 32)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.blue)
|
||||
.disabled(isDownloading)
|
||||
}
|
||||
|
||||
private var downloadProgressView: some View {
|
||||
VStack {
|
||||
ProgressView(value: downloadProgress) {
|
||||
Text(downloadStatus)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
Text("\(Int(downloadProgress * 100))%")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.secondary)
|
||||
Button("取消") {
|
||||
isCancelled = true
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
}
|
||||
.frame(maxWidth: 360)
|
||||
.progressViewStyle(.linear)
|
||||
.tint(.green)
|
||||
}
|
||||
|
||||
private var creativeCloudButton: some View {
|
||||
Button(action: openCreativeCloud) {
|
||||
Label("前往 Adobe Creative Cloud", systemImage: "cloud.fill")
|
||||
.frame(minWidth: 0, maxWidth: 360)
|
||||
.frame(height: 32)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.disabled(isDownloading)
|
||||
}
|
||||
|
||||
private var exitButton: some View {
|
||||
Button(action: exitApp) {
|
||||
Label("退出", systemImage: "xmark")
|
||||
.frame(minWidth: 0, maxWidth: 360)
|
||||
.frame(height: 32)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.red)
|
||||
.keyboardShortcut(.cancelAction)
|
||||
.disabled(isDownloading)
|
||||
}
|
||||
|
||||
private func startDownload() {
|
||||
isDownloading = true
|
||||
isCancelled = false
|
||||
Task {
|
||||
do {
|
||||
try await networkManager.downloadUtils.downloadX1a0HeCCPackages(
|
||||
progressHandler: { progress, status in
|
||||
Task { @MainActor in
|
||||
downloadProgress = progress
|
||||
downloadStatus = status
|
||||
}
|
||||
},
|
||||
cancellationHandler: { isCancelled }
|
||||
)
|
||||
await MainActor.run {
|
||||
dismiss()
|
||||
}
|
||||
} catch NetworkError.cancelled {
|
||||
await MainActor.run {
|
||||
isDownloading = false
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
isDownloading = false
|
||||
errorMessage = error.localizedDescription
|
||||
showErrorAlert = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func openCreativeCloud() {
|
||||
if let url = URL(string: "https://creativecloud.adobe.com/apps/download/creative-cloud") {
|
||||
NSWorkspace.shared.open(url)
|
||||
dismiss()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func exitApp() {
|
||||
dismiss()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ShouldExistsSetUpView()
|
||||
.environmentObject(NetworkManager())
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
"strings" : {
|
||||
"" : {
|
||||
|
||||
},
|
||||
"(可能导致处理 Setup 组件失败)" : {
|
||||
|
||||
},
|
||||
"(将导致无法使用安装功能)" : {
|
||||
"localizations" : {
|
||||
@@ -153,6 +156,9 @@
|
||||
},
|
||||
"Helper 当前状态: " : {
|
||||
|
||||
},
|
||||
"Helper 设置" : {
|
||||
|
||||
},
|
||||
"Helper未安装将导致无法执行需要管理员权限的操作" : {
|
||||
|
||||
@@ -171,6 +177,7 @@
|
||||
}
|
||||
},
|
||||
"Setup 组件版本: %@" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
@@ -181,6 +188,7 @@
|
||||
}
|
||||
},
|
||||
"Setup 组件状态: " : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
@@ -208,6 +216,18 @@
|
||||
},
|
||||
"v6" : {
|
||||
|
||||
},
|
||||
"X1a0He CC 处理状态: " : {
|
||||
|
||||
},
|
||||
"X1a0He CC 备份状态: " : {
|
||||
|
||||
},
|
||||
"X1a0He CC 版本信息: %@" : {
|
||||
|
||||
},
|
||||
"X1a0He CC设置" : {
|
||||
|
||||
},
|
||||
"下载" : {
|
||||
"localizations" : {
|
||||
@@ -228,6 +248,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"下载 X1a0He CC" : {
|
||||
|
||||
},
|
||||
"下载 X1a0He CC 组件" : {
|
||||
"localizations" : {
|
||||
@@ -343,6 +366,7 @@
|
||||
}
|
||||
},
|
||||
"从 GitHub 下载 Setup 组件" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
@@ -463,6 +487,7 @@
|
||||
}
|
||||
},
|
||||
"其他设置" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
@@ -667,7 +692,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"已备份处理" : {
|
||||
"已处理" : {
|
||||
|
||||
},
|
||||
"已备份" : {
|
||||
|
||||
},
|
||||
"已复制" : {
|
||||
@@ -937,8 +965,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"未检测到 Adobe CC 组件" : {
|
||||
|
||||
},
|
||||
"未检测到 Adobe Setup 组件" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
@@ -1120,7 +1152,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"确定要下载并安装 Setup 组件吗?" : {
|
||||
"确定要下载并安装 X1a0He CC 吗?" : {
|
||||
|
||||
},
|
||||
"确定要重新处理 Setup 组件吗?" : {
|
||||
@@ -1205,8 +1237,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"程序检测到你的系统中不存在 Adobe CC 组件" : {
|
||||
|
||||
},
|
||||
"程序检测到你的系统中不存在 Adobe Setup 组件" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
@@ -1420,6 +1456,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"重新备份" : {
|
||||
|
||||
},
|
||||
"重新安装" : {
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
```markdown
|
||||
1. 新增可选API版本 (v4, v5, v6)
|
||||
2. 引入 Privilege Helper 来处理所有需要权限的操作
|
||||
3. 修改从 Github 下载 Setup 组件功能,改为从官方下载简化版CC,称为 X1a0He CC
|
||||
4. 调整 CC 组件备份与处理状态检测,分离二者的检测机制
|
||||
5. 移除了安装日志显示
|
||||
6. 调整 Setup 组件版本号的获取方式
|
||||
```
|
||||
|
||||
## 2024-11-11 21:00 更新日志
|
||||
|
||||
Reference in New Issue
Block a user