2024-10-31 22:35:22 +08:00
|
|
|
//
|
2024-11-05 20:30:18 +08:00
|
|
|
// Adobe Downloader
|
2024-10-31 22:35:22 +08:00
|
|
|
//
|
|
|
|
|
// Created by X1a0He on 2024/10/30.
|
|
|
|
|
//
|
|
|
|
|
import Foundation
|
|
|
|
|
|
|
|
|
|
actor CancelTracker {
|
|
|
|
|
private var cancelledIds: Set<UUID> = []
|
|
|
|
|
private var pausedIds: Set<UUID> = []
|
2024-11-09 23:15:50 +08:00
|
|
|
var downloadTasks: [UUID: URLSessionDownloadTask] = [:]
|
2024-10-31 22:35:22 +08:00
|
|
|
private var sessions: [UUID: URLSession] = [:]
|
|
|
|
|
private var resumeData: [UUID: Data] = [:]
|
2025-07-13 00:05:01 +08:00
|
|
|
private var taskPackageIdentifiers: [UUID: String] = [:]
|
|
|
|
|
private var taskFirstDataFlags: [UUID: AsyncFlag] = [:]
|
2024-10-31 22:35:22 +08:00
|
|
|
|
2025-07-13 00:05:01 +08:00
|
|
|
func registerTask(_ id: UUID, task: URLSessionDownloadTask, session: URLSession, packageIdentifier: String = "", hasReceivedFirstData: AsyncFlag? = nil) {
|
2024-10-31 22:35:22 +08:00
|
|
|
downloadTasks[id] = task
|
|
|
|
|
sessions[id] = session
|
2025-07-13 00:05:01 +08:00
|
|
|
if !packageIdentifier.isEmpty {
|
|
|
|
|
taskPackageIdentifiers[id] = packageIdentifier
|
|
|
|
|
}
|
|
|
|
|
if let flag = hasReceivedFirstData {
|
|
|
|
|
taskFirstDataFlags[id] = flag
|
|
|
|
|
}
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
|
|
|
|
|
2024-11-03 17:13:25 +08:00
|
|
|
func cancel(_ id: UUID) {
|
2024-10-31 22:35:22 +08:00
|
|
|
cancelledIds.insert(id)
|
|
|
|
|
pausedIds.remove(id)
|
|
|
|
|
resumeData.removeValue(forKey: id)
|
2025-07-13 00:05:01 +08:00
|
|
|
taskPackageIdentifiers.removeValue(forKey: id)
|
|
|
|
|
taskFirstDataFlags.removeValue(forKey: id)
|
2024-10-31 22:35:22 +08:00
|
|
|
|
|
|
|
|
if let task = downloadTasks[id] {
|
2024-11-03 17:13:25 +08:00
|
|
|
task.cancel()
|
2024-10-31 22:35:22 +08:00
|
|
|
downloadTasks.removeValue(forKey: id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let session = sessions[id] {
|
|
|
|
|
session.invalidateAndCancel()
|
|
|
|
|
sessions.removeValue(forKey: id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func pause(_ id: UUID) async {
|
|
|
|
|
if !cancelledIds.contains(id) {
|
|
|
|
|
pausedIds.insert(id)
|
|
|
|
|
if let task = downloadTasks[id] {
|
|
|
|
|
let data = await withCheckedContinuation { continuation in
|
|
|
|
|
task.cancel(byProducingResumeData: { data in
|
|
|
|
|
continuation.resume(returning: data)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
if let data = data {
|
|
|
|
|
resumeData[id] = data
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-13 00:05:01 +08:00
|
|
|
func resume(_ id: UUID) {
|
|
|
|
|
if pausedIds.contains(id) {
|
|
|
|
|
pausedIds.remove(id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 22:35:22 +08:00
|
|
|
func getResumeData(_ id: UUID) -> Data? {
|
|
|
|
|
return resumeData[id]
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-03 17:13:25 +08:00
|
|
|
func clearResumeData(_ id: UUID) {
|
|
|
|
|
resumeData.removeValue(forKey: id)
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
|
|
|
|
|
2024-11-03 17:13:25 +08:00
|
|
|
func isCancelled(_ id: UUID) -> Bool {
|
|
|
|
|
return cancelledIds.contains(id)
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
|
|
|
|
|
2024-11-03 17:13:25 +08:00
|
|
|
func isPaused(_ id: UUID) -> Bool {
|
|
|
|
|
return pausedIds.contains(id)
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|
2024-11-09 23:15:50 +08:00
|
|
|
|
|
|
|
|
func storeResumeData(_ id: UUID, data: Data) {
|
|
|
|
|
resumeData[id] = data
|
|
|
|
|
}
|
2025-07-13 00:05:01 +08:00
|
|
|
|
|
|
|
|
func cleanupCompletedTasks() {
|
|
|
|
|
let completedTaskIds = downloadTasks.compactMap { (id, task) in
|
|
|
|
|
task.state == .completed || task.state == .canceling ? id : nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for taskId in completedTaskIds {
|
|
|
|
|
downloadTasks.removeValue(forKey: taskId)
|
|
|
|
|
sessions.removeValue(forKey: taskId)
|
|
|
|
|
taskPackageIdentifiers.removeValue(forKey: taskId)
|
|
|
|
|
taskFirstDataFlags.removeValue(forKey: taskId)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getActiveTasksInfo() -> (total: Int, running: Int, suspended: Int) {
|
|
|
|
|
let total = downloadTasks.count
|
|
|
|
|
let running = downloadTasks.values.filter { $0.state == .running }.count
|
|
|
|
|
let suspended = downloadTasks.values.filter { $0.state == .suspended }.count
|
|
|
|
|
return (total: total, running: running, suspended: suspended)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getTaskPackageMap() -> [UUID: (task: URLSessionDownloadTask, packageIdentifier: String, hasReceivedFirstData: AsyncFlag?)] {
|
|
|
|
|
var result: [UUID: (URLSessionDownloadTask, String, AsyncFlag?)] = [:]
|
|
|
|
|
for (id, task) in downloadTasks {
|
|
|
|
|
let packageId = taskPackageIdentifiers[id] ?? ""
|
|
|
|
|
let firstDataFlag = taskFirstDataFlags[id]
|
|
|
|
|
result[id] = (task, packageId, firstDataFlag)
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
2024-10-31 22:35:22 +08:00
|
|
|
}
|