Skip to content

Commit

Permalink
Add: Support task record persistence.
Browse files Browse the repository at this point in the history
  • Loading branch information
X1a0He committed Nov 9, 2024
1 parent 8897554 commit 6de8104
Show file tree
Hide file tree
Showing 19 changed files with 1,052 additions and 331 deletions.
8 changes: 4 additions & 4 deletions Adobe Downloader.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
CODE_SIGN_ENTITLEMENTS = "Adobe Downloader/Adobe Downloader.entitlements";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 102;
CURRENT_PROJECT_VERSION = 110;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"Adobe Downloader/Preview Content\"";
DEVELOPMENT_TEAM = "";
Expand All @@ -300,7 +300,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.0.2;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.x1a0he.macOS.Adobe-Downloader";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -318,7 +318,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 102;
CURRENT_PROJECT_VERSION = 110;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"Adobe Downloader/Preview Content\"";
DEVELOPMENT_TEAM = "";
Expand All @@ -332,7 +332,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.0.2;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.x1a0he.macOS.Adobe-Downloader";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
filePath = "Adobe Downloader/Utils/DownloadUtils.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "468"
endingLineNumber = "468"
landmarkName = "retryPackage(task:package:)"
startingLineNumber = "463"
endingLineNumber = "463"
landmarkName = "startDownloadProcess(task:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
Expand Down
4 changes: 4 additions & 0 deletions Adobe Downloader/Adobe DownloaderApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ struct Adobe_DownloaderApp: App {
.frame(width: 850, height: 800)
.tint(.blue)
.onAppear {
appDelegate.networkManager = networkManager

networkManager.loadSavedTasks()

checkCreativeCloudSetup()

if ModifySetup.checkSetupBackup() {
Expand Down
73 changes: 72 additions & 1 deletion Adobe Downloader/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,78 @@ import Cocoa
import SwiftUI

class AppDelegate: NSObject, NSApplicationDelegate {
private var eventMonitor: Any?
var networkManager: NetworkManager?

func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.mainMenu = nil
NSApp.mainMenu = nil
eventMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
if event.modifierFlags.contains(.command) && event.characters?.lowercased() == "q" {
if let mainWindow = NSApp.mainWindow,
mainWindow.sheets.isEmpty && !mainWindow.isSheet {
self?.handleQuitCommand()
return nil
}
}
return event
}
}

@MainActor private func handleQuitCommand() {
guard let manager = networkManager else {
NSApplication.shared.terminate(nil)
return
}

let hasActiveDownloads = manager.downloadTasks.contains { task in
if case .downloading = task.totalStatus {
return true
}
return false
}

if hasActiveDownloads {
Task {
for task in manager.downloadTasks {
if case .downloading = task.totalStatus {
await manager.downloadUtils.pauseDownloadTask(
taskId: task.id,
reason: .other(String(localized: "程序即将退出"))
)
}
}

await MainActor.run {
let alert = NSAlert()
alert.messageText = String(localized: "确认退出")
alert.informativeText = String(localized:"有正在进行的下载任务,确定要退出吗?\n所有下载任务的进度已保存,下次启动可以继续下载")
alert.alertStyle = .warning
alert.addButton(withTitle: String(localized:"退出"))
alert.addButton(withTitle: String(localized:"取消"))

let response = alert.runModal()
if response == .alertSecondButtonReturn {
Task {
for task in manager.downloadTasks {
if case .paused = task.totalStatus {
await manager.downloadUtils.resumeDownloadTask(taskId: task.id)
}
}
}
} else {
NSApplication.shared.terminate(0)
}
}
}
} else {
NSApplication.shared.terminate(nil)
}
}

deinit {
if let monitor = eventMonitor {
NSEvent.removeMonitor(monitor)
}
networkManager = nil
}
}
118 changes: 108 additions & 10 deletions Adobe Downloader/Commons/Enums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import Foundation
import SwiftUI

enum PackageStatus: Equatable {
enum PackageStatus: Equatable, Codable {
case waiting
case downloading
case paused
Expand Down Expand Up @@ -165,7 +165,7 @@ enum NetworkError: Error, LocalizedError {
}
}

enum DownloadStatus: Equatable {
enum DownloadStatus: Equatable, Codable {
case waiting
case preparing(PrepareInfo)
case downloading(DownloadInfo)
Expand All @@ -174,12 +174,12 @@ enum DownloadStatus: Equatable {
case failed(FailureInfo)
case retrying(RetryInfo)

struct PrepareInfo {
struct PrepareInfo: Codable {
let message: String
let timestamp: Date
let stage: PrepareStage

enum PrepareStage {
enum PrepareStage: Codable {
case initializing
case creatingInstaller
case signingApp
Expand All @@ -188,47 +188,145 @@ enum DownloadStatus: Equatable {
}
}

struct DownloadInfo {
struct DownloadInfo: Codable {
let fileName: String
let currentPackageIndex: Int
let totalPackages: Int
let startTime: Date
let estimatedTimeRemaining: TimeInterval?
}

struct PauseInfo {
struct PauseInfo: Codable {
let reason: PauseReason
let timestamp: Date
let resumable: Bool

enum PauseReason {
enum PauseReason: Codable {
case userRequested
case networkIssue
case systemSleep
case other(String)
}
}

struct CompletionInfo {
struct CompletionInfo: Codable {
let timestamp: Date
let totalTime: TimeInterval
let totalSize: Int64
}

struct FailureInfo {
struct FailureInfo: Codable {
let message: String
let error: Error?
let timestamp: Date
let recoverable: Bool

enum CodingKeys: CodingKey {
case message
case timestamp
case recoverable
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(message, forKey: .message)
try container.encode(timestamp, forKey: .timestamp)
try container.encode(recoverable, forKey: .recoverable)
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
message = try container.decode(String.self, forKey: .message)
timestamp = try container.decode(Date.self, forKey: .timestamp)
recoverable = try container.decode(Bool.self, forKey: .recoverable)
error = nil
}

init(message: String, error: Error?, timestamp: Date, recoverable: Bool) {
self.message = message
self.error = error
self.timestamp = timestamp
self.recoverable = recoverable
}
}

struct RetryInfo {
struct RetryInfo: Codable {
let attempt: Int
let maxAttempts: Int
let reason: String
let nextRetryDate: Date
}

private enum CodingKeys: String, CodingKey {
case type
case info
}

private enum StatusType: String, Codable {
case waiting
case preparing
case downloading
case paused
case completed
case failed
case retrying
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

switch self {
case .waiting:
try container.encode(StatusType.waiting, forKey: .type)
case .preparing(let info):
try container.encode(StatusType.preparing, forKey: .type)
try container.encode(info, forKey: .info)
case .downloading(let info):
try container.encode(StatusType.downloading, forKey: .type)
try container.encode(info, forKey: .info)
case .paused(let info):
try container.encode(StatusType.paused, forKey: .type)
try container.encode(info, forKey: .info)
case .completed(let info):
try container.encode(StatusType.completed, forKey: .type)
try container.encode(info, forKey: .info)
case .failed(let info):
try container.encode(StatusType.failed, forKey: .type)
try container.encode(info, forKey: .info)
case .retrying(let info):
try container.encode(StatusType.retrying, forKey: .type)
try container.encode(info, forKey: .info)
}
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(StatusType.self, forKey: .type)

switch type {
case .waiting:
self = .waiting
case .preparing:
let info = try container.decode(PrepareInfo.self, forKey: .info)
self = .preparing(info)
case .downloading:
let info = try container.decode(DownloadInfo.self, forKey: .info)
self = .downloading(info)
case .paused:
let info = try container.decode(PauseInfo.self, forKey: .info)
self = .paused(info)
case .completed:
let info = try container.decode(CompletionInfo.self, forKey: .info)
self = .completed(info)
case .failed:
let info = try container.decode(FailureInfo.self, forKey: .info)
self = .failed(info)
case .retrying:
let info = try container.decode(RetryInfo.self, forKey: .info)
self = .retrying(info)
}
}

var description: String {
switch self {
case .waiting:
Expand Down
1 change: 1 addition & 0 deletions Adobe Downloader/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct ContentView: View {
Image(systemName: "arrow.down.circle")
.imageScale(.medium)
}
.disabled(isRefreshing)
.buttonStyle(.borderless)
.overlay(
Group {
Expand Down
4 changes: 3 additions & 1 deletion Adobe Downloader/Models/DownloadTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class NewDownloadTask: Identifiable, ObservableObject, Equatable {
objectWillChange.send()
}
}
let platform: String

var status: DownloadStatus {
totalStatus ?? .waiting
Expand Down Expand Up @@ -88,7 +89,7 @@ class NewDownloadTask: Identifiable, ObservableObject, Equatable {
objectWillChange.send()
}

init(sapCode: String, version: String, language: String, displayName: String, directory: URL, productsToDownload: [ProductsToDownload] = [], retryCount: Int = 0, createAt: Date, totalStatus: DownloadStatus? = nil, totalProgress: Double, totalDownloadedSize: Int64 = 0, totalSize: Int64 = 0, totalSpeed: Double = 0, currentPackage: Package? = nil) {
init(sapCode: String, version: String, language: String, displayName: String, directory: URL, productsToDownload: [ProductsToDownload] = [], retryCount: Int = 0, createAt: Date, totalStatus: DownloadStatus? = nil, totalProgress: Double, totalDownloadedSize: Int64 = 0, totalSize: Int64 = 0, totalSpeed: Double = 0, currentPackage: Package? = nil, platform: String) {
self.sapCode = sapCode
self.version = version
self.language = language
Expand All @@ -104,6 +105,7 @@ class NewDownloadTask: Identifiable, ObservableObject, Equatable {
self.totalSpeed = totalSpeed
self.currentPackage = currentPackage
self.displayInstallButton = sapCode != "APRO"
self.platform = platform
}

static func == (lhs: NewDownloadTask, rhs: NewDownloadTask) -> Bool {
Expand Down
Loading

0 comments on commit 6de8104

Please sign in to comment.