More improvements to progress status

This commit is contained in:
ronaldheft 2022-08-08 20:05:09 -04:00
parent e9961f64a9
commit e275aa1699

View file

@ -21,8 +21,8 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
return URLSession(configuration: .default, delegate: self, delegateQueue: queue) return URLSession(configuration: .default, delegate: self, delegateQueue: queue)
}() }()
private let progressStatusQueue = DispatchQueue(label: "progress-status-queue", attributes: .concurrent) private let progressStatusQueue = DispatchQueue(label: "progress-status-queue", attributes: .concurrent)
private var progressStatusWorkItem: DispatchWorkItem?
private var downloadItemProgress = [String: DownloadItem]() private var downloadItemProgress = [String: DownloadItem]()
private var isMonitoringProgress = false
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
handleDownloadTaskUpdate(downloadTask: downloadTask) { downloadItem, downloadItemPart in handleDownloadTaskUpdate(downloadTask: downloadTask) { downloadItem, downloadItemPart in
@ -59,7 +59,7 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
// Calculate the download percentage // Calculate the download percentage
let percentDownloaded = (Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)) * 100 let percentDownloaded = (Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)) * 100
// Only update the progress if we received accurate progress data // Only update the progress if we received accurate progress data
if percentDownloaded >= 0.0 && percentDownloaded <= 1.0 { if percentDownloaded >= 0.0 && percentDownloaded <= 100.0 {
downloadItemPart.progress = percentDownloaded downloadItemPart.progress = percentDownloaded
} }
} }
@ -73,6 +73,9 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
// Find the download item // Find the download item
let downloadItem = Database.shared.getDownloadItem(downloadItemPartId: downloadItemPartId) let downloadItem = Database.shared.getDownloadItem(downloadItemPartId: downloadItemPartId)
guard var downloadItem = downloadItem else { throw LibraryItemDownloadError.downloadItemNotFound } guard var downloadItem = downloadItem else { throw LibraryItemDownloadError.downloadItemNotFound }
self.progressStatusQueue.sync {
downloadItem = self.downloadItemProgress[downloadItem.id!] ?? downloadItem
}
// Find the download item part // Find the download item part
let partIndex = downloadItem.downloadItemParts.firstIndex(where: { $0.id == downloadItemPartId }) let partIndex = downloadItem.downloadItemParts.firstIndex(where: { $0.id == downloadItemPartId })
@ -87,11 +90,10 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
} }
// Update the progress // Update the progress
Database.shared.updateDownloadItemPart(downloadItem.downloadItemParts[partIndex])
self.progressStatusQueue.async(flags: .barrier) { self.progressStatusQueue.async(flags: .barrier) {
self.downloadItemProgress.updateValue(downloadItem, forKey: downloadItem.id!) self.downloadItemProgress.updateValue(downloadItem, forKey: downloadItem.id!)
self.notifyDownloadProgress()
} }
self.notifyDownloadProgress()
} catch { } catch {
NSLog("DownloadItemError") NSLog("DownloadItemError")
debugPrint(error) debugPrint(error)
@ -100,35 +102,30 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
// We want to handle updating the UI in the background and throttled so we don't overload the UI with progress updates // We want to handle updating the UI in the background and throttled so we don't overload the UI with progress updates
private func notifyDownloadProgress() { private func notifyDownloadProgress() {
// Return if the loop is running if !self.isMonitoringProgress {
guard self.progressStatusWorkItem == nil else { return } self.isMonitoringProgress = true
DispatchQueue.global(qos: .userInteractive).async {
// Create a background thread to send the download status NSLog("Starting monitoring download progress...")
self.progressStatusWorkItem = DispatchWorkItem { [weak self] in
// Clean up the work item when done
defer { self?.progressStatusWorkItem = nil }
// Fetch active downloads in a thread-safe way // Fetch active downloads in a thread-safe way
func fetchActiveDownloads() -> [String: DownloadItem]? { func fetchActiveDownloads() -> [String: DownloadItem]? {
self?.progressStatusQueue.sync { self?.downloadItemProgress } self.progressStatusQueue.sync { self.downloadItemProgress }
} }
// Remove a completed download item in a thread-safe way // Remove a completed download item in a thread-safe way
func handleDoneDownloadItem(_ item: DownloadItem) { func handleDoneDownloadItem(_ item: DownloadItem) {
self?.progressStatusQueue.async(flags: .barrier) { self.progressStatusQueue.async(flags: .barrier) {
defer { self.downloadItemProgress.removeValue(forKey: item.id!)
}
Database.shared.removeDownloadItem(item) Database.shared.removeDownloadItem(item)
self?.downloadItemProgress.removeValue(forKey: item.id!) self.handleDownloadTaskCompleteFromDownloadItem(item)
}
self?.handleDownloadTaskCompleteFromDownloadItem(item)
}
} }
// While there are active download items, emit status updates // While there are active download items, emit status updates
while !(fetchActiveDownloads()?.isEmpty ?? false) { while !(fetchActiveDownloads()?.isEmpty ?? false) {
if let activeDownloads = fetchActiveDownloads() { if let activeDownloads = fetchActiveDownloads() {
for item in activeDownloads.values { for item in activeDownloads.values {
try? self?.notifyListeners("onItemDownloadUpdate", data: item.asDictionary()) try? self.notifyListeners("onItemDownloadUpdate", data: item.asDictionary())
if item.isDoneDownloading() { handleDoneDownloadItem(item) } if item.isDoneDownloading() { handleDoneDownloadItem(item) }
} }
} }
@ -136,10 +133,11 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate {
// Wait 200ms before reporting status again // Wait 200ms before reporting status again
Thread.sleep(forTimeInterval: TimeInterval(0.2)) Thread.sleep(forTimeInterval: TimeInterval(0.2))
} }
}
// Start the thread NSLog("Finished monitoring download progress...")
DispatchQueue.global(qos: .userInteractive).async(execute: self.progressStatusWorkItem!) self.isMonitoringProgress = false
}
}
} }
private func handleDownloadTaskCompleteFromDownloadItem(_ downloadItem: DownloadItem) { private func handleDownloadTaskCompleteFromDownloadItem(_ downloadItem: DownloadItem) {