mirror of
https://github.com/X1a0He/Adobe-Downloader.git
synced 2025-11-25 19:27:35 +08:00
feat: 吃了两块西瓜,解决了一些警告
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
//
|
||||
import Foundation
|
||||
|
||||
class NewDownloadTask: Identifiable, ObservableObject {
|
||||
class NewDownloadTask: Identifiable, ObservableObject, @unchecked Sendable {
|
||||
let id = UUID()
|
||||
var productId: String
|
||||
let productVersion: String
|
||||
|
||||
@@ -98,7 +98,7 @@ extension ValidationInfo.SegmentInfo: Codable {
|
||||
}
|
||||
}
|
||||
|
||||
class ChunkedDownloadManager {
|
||||
class ChunkedDownloadManager: @unchecked Sendable {
|
||||
static let shared = ChunkedDownloadManager()
|
||||
|
||||
private var chunkSize: Int64 {
|
||||
@@ -113,7 +113,26 @@ class ChunkedDownloadManager {
|
||||
}
|
||||
|
||||
private var activeTasks: [String: Task<Void, Error>] = [:]
|
||||
private let taskLock = NSLock()
|
||||
|
||||
private let taskQueue = DispatchQueue(label: "com.x1a0he.macOS.Adobe-Downloader.chunkDownloadTasks", attributes: .concurrent)
|
||||
|
||||
private func setActiveTask(packageIdentifier: String, task: Task<Void, Error>) async {
|
||||
await withCheckedContinuation { continuation in
|
||||
taskQueue.async(flags: .barrier) { [weak self] in
|
||||
self?.activeTasks[packageIdentifier] = task
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func removeActiveTask(packageIdentifier: String) async {
|
||||
await withCheckedContinuation { continuation in
|
||||
taskQueue.async(flags: .barrier) { [weak self] in
|
||||
self?.activeTasks.removeValue(forKey: packageIdentifier)
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private init() {
|
||||
let containerURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
|
||||
@@ -212,9 +231,13 @@ class ChunkedDownloadManager {
|
||||
}
|
||||
|
||||
private func writeDataToFile(data: Data, destinationURL: URL, offset: Int64, totalSize: Int64? = nil) async throws {
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
DispatchQueue.global(qos: .utility).async {
|
||||
return try await withCheckedThrowingContinuation { [weak self] continuation in
|
||||
DispatchQueue.global(qos: .utility).async { [weak self] in
|
||||
do {
|
||||
guard let self = self else {
|
||||
continuation.resume(throwing: NSError(domain: "ChunkedDownloadManager", code: -1, userInfo: [NSLocalizedDescriptionKey: "Manager deallocated"]))
|
||||
return
|
||||
}
|
||||
let directory = destinationURL.deletingLastPathComponent()
|
||||
if !self.fileManager.fileExists(atPath: directory.path) {
|
||||
try self.fileManager.createDirectory(at: directory, withIntermediateDirectories: true)
|
||||
@@ -265,7 +288,7 @@ class ChunkedDownloadManager {
|
||||
}
|
||||
|
||||
if fileManager.fileExists(atPath: destinationURL.path) {
|
||||
if let expectedHash = chunk.expectedHash {
|
||||
if chunk.expectedHash != nil {
|
||||
if validateCompleteChunkFromFile(destinationURL: destinationURL, chunk: chunk) {
|
||||
modifiedChunk.downloadedSize = chunk.size
|
||||
modifiedChunk.isCompleted = true
|
||||
@@ -304,9 +327,7 @@ class ChunkedDownloadManager {
|
||||
headers.forEach { request.setValue($0.value, forHTTPHeaderField: $0.key) }
|
||||
|
||||
|
||||
return try await withTaskCancellationHandler {
|
||||
modifiedChunk.isPaused = true
|
||||
} operation: {
|
||||
return try await withTaskCancellationHandler(operation: {
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
|
||||
try Task.checkCancellation()
|
||||
@@ -329,7 +350,7 @@ class ChunkedDownloadManager {
|
||||
if modifiedChunk.downloadedSize >= chunk.size {
|
||||
modifiedChunk.isCompleted = true
|
||||
|
||||
if let expectedHash = chunk.expectedHash {
|
||||
if chunk.expectedHash != nil {
|
||||
if !validateCompleteChunkFromFile(destinationURL: destinationURL, chunk: chunk) {
|
||||
throw NetworkError.invalidData("分片哈希校验失败: \(chunk.index)")
|
||||
}
|
||||
@@ -343,26 +364,34 @@ class ChunkedDownloadManager {
|
||||
}
|
||||
|
||||
return modifiedChunk
|
||||
}
|
||||
}, onCancel: {})
|
||||
}
|
||||
|
||||
func pauseDownload(packageIdentifier: String) {
|
||||
taskLock.lock()
|
||||
defer { taskLock.unlock() }
|
||||
|
||||
if let task = activeTasks[packageIdentifier] {
|
||||
Task {
|
||||
await withCheckedContinuation { [weak self] continuation in
|
||||
self?.taskQueue.async(flags: .barrier) { [weak self] in
|
||||
if let task = self?.activeTasks[packageIdentifier] {
|
||||
task.cancel()
|
||||
activeTasks.removeValue(forKey: packageIdentifier)
|
||||
self?.activeTasks.removeValue(forKey: packageIdentifier)
|
||||
}
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancelDownload(packageIdentifier: String) {
|
||||
taskLock.lock()
|
||||
defer { taskLock.unlock() }
|
||||
|
||||
if let task = activeTasks[packageIdentifier] {
|
||||
Task {
|
||||
await withCheckedContinuation { [weak self] continuation in
|
||||
self?.taskQueue.async(flags: .barrier) { [weak self] in
|
||||
if let task = self?.activeTasks[packageIdentifier] {
|
||||
task.cancel()
|
||||
activeTasks.removeValue(forKey: packageIdentifier)
|
||||
self?.activeTasks.removeValue(forKey: packageIdentifier)
|
||||
}
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearChunkedDownloadState(packageIdentifier: packageIdentifier)
|
||||
@@ -471,7 +500,7 @@ class ChunkedDownloadManager {
|
||||
cancellationHandler: (() async -> Bool)? = nil
|
||||
) async throws {
|
||||
let downloadTask = Task<Void, Error> {
|
||||
let (supportsRange, totalSize, etag) = try await checkRangeSupport(url: url, headers: headers)
|
||||
let (supportsRange, totalSize, _) = try await checkRangeSupport(url: url, headers: headers)
|
||||
|
||||
guard totalSize > 0 else {
|
||||
throw NetworkError.invalidData("无法获取文件大小")
|
||||
@@ -548,7 +577,7 @@ class ChunkedDownloadManager {
|
||||
return false
|
||||
}
|
||||
|
||||
if let expectedHash = chunk.expectedHash,
|
||||
if chunk.expectedHash != nil &&
|
||||
fileManager.fileExists(atPath: destinationURL.path) {
|
||||
if validateCompleteChunkFromFile(destinationURL: destinationURL, chunk: chunk) {
|
||||
return false
|
||||
@@ -582,14 +611,12 @@ class ChunkedDownloadManager {
|
||||
clearChunkedDownloadState(packageIdentifier: packageIdentifier)
|
||||
}
|
||||
|
||||
taskLock.lock()
|
||||
activeTasks[packageIdentifier] = downloadTask
|
||||
taskLock.unlock()
|
||||
await setActiveTask(packageIdentifier: packageIdentifier, task: downloadTask)
|
||||
|
||||
defer {
|
||||
taskLock.lock()
|
||||
activeTasks.removeValue(forKey: packageIdentifier)
|
||||
taskLock.unlock()
|
||||
Task {
|
||||
await removeActiveTask(packageIdentifier: packageIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
try await downloadTask.value
|
||||
|
||||
@@ -183,13 +183,6 @@ class NewDownloadUtils {
|
||||
)
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
|
||||
completionHandler(.becomeDownload)
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didBecome downloadTask: URLSessionDownloadTask) {
|
||||
}
|
||||
|
||||
func cleanup() {
|
||||
completionHandler = { _, _, _ in }
|
||||
progressHandler = nil
|
||||
@@ -370,13 +363,12 @@ class NewDownloadUtils {
|
||||
}
|
||||
}
|
||||
|
||||
let initialProgress = await progressManager.getTotalProgress()
|
||||
|
||||
let packagesSnapshot = allPackages
|
||||
await MainActor.run {
|
||||
let totalPackages = allPackages.count
|
||||
task.currentPackage = allPackages.first?.package
|
||||
let totalPackages = packagesSnapshot.count
|
||||
task.currentPackage = packagesSnapshot.first?.package
|
||||
task.setStatus(.downloading(DownloadStatus.DownloadInfo(
|
||||
fileName: allPackages.first?.package.fullPackageName ?? "",
|
||||
fileName: packagesSnapshot.first?.package.fullPackageName ?? "",
|
||||
currentPackageIndex: 0,
|
||||
totalPackages: totalPackages,
|
||||
startTime: Date(),
|
||||
@@ -1295,7 +1287,7 @@ class NewDownloadUtils {
|
||||
let taskPackageMap = await globalCancelTracker.getTaskPackageMap()
|
||||
|
||||
|
||||
for (downloadTaskId, (downloadTask, _, _)) in taskPackageMap {
|
||||
for (_, (downloadTask, _, _)) in taskPackageMap {
|
||||
downloadTask.cancel()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
class TaskPersistenceManager {
|
||||
class TaskPersistenceManager: @unchecked Sendable {
|
||||
static let shared = TaskPersistenceManager()
|
||||
|
||||
private let fileManager = FileManager.default
|
||||
@@ -33,9 +33,9 @@ class TaskPersistenceManager {
|
||||
platform: task.platform
|
||||
)
|
||||
|
||||
await withCheckedContinuation { continuation in
|
||||
taskCacheQueue.async(flags: .barrier) {
|
||||
self.taskCache[fileName] = task
|
||||
await withCheckedContinuation { [weak self] continuation in
|
||||
self?.taskCacheQueue.async(flags: .barrier) { [weak self] in
|
||||
self?.taskCache[fileName] = task
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
@@ -100,18 +100,18 @@ class TaskPersistenceManager {
|
||||
for file in files where file.pathExtension == "json" {
|
||||
let fileName = file.lastPathComponent
|
||||
|
||||
let cachedTask = await withCheckedContinuation { continuation in
|
||||
taskCacheQueue.sync {
|
||||
continuation.resume(returning: self.taskCache[fileName])
|
||||
let cachedTask = await withCheckedContinuation { [weak self] continuation in
|
||||
self?.taskCacheQueue.sync { [weak self] in
|
||||
continuation.resume(returning: self?.taskCache[fileName])
|
||||
}
|
||||
}
|
||||
|
||||
if let cachedTask = cachedTask {
|
||||
tasks.append(cachedTask)
|
||||
} else if let task = await loadTask(from: file) {
|
||||
await withCheckedContinuation { continuation in
|
||||
taskCacheQueue.async(flags: .barrier) {
|
||||
self.taskCache[fileName] = task
|
||||
await withCheckedContinuation { [weak self] continuation in
|
||||
self?.taskCacheQueue.async(flags: .barrier) { [weak self] in
|
||||
self?.taskCache[fileName] = task
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
@@ -219,8 +219,8 @@ class TaskPersistenceManager {
|
||||
)
|
||||
let fileURL = tasksDirectory.appendingPathComponent(fileName)
|
||||
|
||||
taskCacheQueue.async(flags: .barrier) {
|
||||
self.taskCache.removeValue(forKey: fileName)
|
||||
taskCacheQueue.async(flags: .barrier) { [weak self] in
|
||||
self?.taskCache.removeValue(forKey: fileName)
|
||||
}
|
||||
|
||||
try? fileManager.removeItem(at: fileURL)
|
||||
@@ -277,9 +277,9 @@ class TaskPersistenceManager {
|
||||
)
|
||||
task.displayInstallButton = true
|
||||
|
||||
await withCheckedContinuation { continuation in
|
||||
taskCacheQueue.async(flags: .barrier) {
|
||||
self.taskCache[fileName] = task
|
||||
await withCheckedContinuation { [weak self] continuation in
|
||||
self?.taskCacheQueue.async(flags: .barrier) { [weak self] in
|
||||
self?.taskCache[fileName] = task
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,7 +609,6 @@ struct AlertModifier: ViewModifier {
|
||||
}
|
||||
|
||||
private func startRedownload() async {
|
||||
do {
|
||||
globalNetworkManager.downloadTasks.removeAll { task in
|
||||
task.productId == viewModel.uniqueProduct.id &&
|
||||
task.productVersion == viewModel.pendingVersion &&
|
||||
@@ -625,8 +624,5 @@ struct AlertModifier: ViewModifier {
|
||||
viewModel.selectedLanguage = viewModel.pendingLanguage
|
||||
viewModel.showVersionPicker = true
|
||||
}
|
||||
} catch {
|
||||
viewModel.handleError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user