diff --git a/ios/App/App/plugins/AbsDownloader.swift b/ios/App/App/plugins/AbsDownloader.swift index aaa8908f..dbbd3902 100644 --- a/ios/App/App/plugins/AbsDownloader.swift +++ b/ios/App/App/plugins/AbsDownloader.swift @@ -11,36 +11,53 @@ import Capacitor @objc(AbsDownloader) public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate { + typealias DownloadProgressHandler = (_ downloadItem: DownloadItem, _ downloadItemPart: inout DownloadItemPart) throws -> Void + private let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - private lazy var session = URLSession(configuration: .default, delegate: self, delegateQueue: .current) + private lazy var session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue.main) public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { - NSLog("Finished downloading \(downloadTask.taskDescription ?? "Unknown Task")") - - guard let downloadItemPartId = downloadTask.taskDescription else { return } - - let downloadItem = Database.shared.getDownloadItem(downloadItemPartId: downloadItemPartId) - guard let downloadItem = downloadItem else { - NSLog("Download item part (%@) not found! Unable to move file!", downloadItemPartId) - return + handleDownloadTaskUpdate(downloadTask: downloadTask) { downloadItem, downloadItemPart in + downloadItemPart.progress = 1 + downloadItemPart.completed = true + + do { + // Move the downloaded file into place + guard let destinationUrl = downloadItemPart.destinationURL() else { + throw LibraryItemDownloadError.downloadItemPartDestinationUrlNotDefined + } + try? FileManager.default.removeItem(at: destinationUrl) + try FileManager.default.moveItem(at: location, to: destinationUrl) + downloadItemPart.moved = true + } catch { + downloadItemPart.failed = true + throw error + } } - - NSLog("Found downloadItem(%@)", downloadItem.id) - self.moveLibraryItemToFileDirectory(tempUrl: location) } public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - NSLog("Error downloading \(task.taskDescription ?? "Unknown Task"): \(error ?? "Unknown Error")") + handleDownloadTaskUpdate(downloadTask: task) { downloadItem, downloadItemPart in + if let error = error { + downloadItemPart.failed = true + throw error + } + } } public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { - do { - guard let downloadItemPartId = downloadTask.taskDescription else { throw LibraryItemDownloadError.noTaskDescription } - + handleDownloadTaskUpdate(downloadTask: downloadTask) { downloadItem, downloadItemPart in // Calculate the download percentage let percentDownloaded = (Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)) * 100 - NSLog("Received download status \(downloadItemPartId): \(percentDownloaded)") - + downloadItemPart.progress = percentDownloaded + } + } + + private func handleDownloadTaskUpdate(downloadTask: URLSessionTask, progressHandler: DownloadProgressHandler) { + do { + guard let downloadItemPartId = downloadTask.taskDescription else { throw LibraryItemDownloadError.noTaskDescription } + NSLog("Received download update for \(downloadItemPartId)") + // Find the download item let downloadItem = Database.shared.getDownloadItem(downloadItemPartId: downloadItemPartId) guard let downloadItem = downloadItem else { throw LibraryItemDownloadError.downloadItemNotFound } @@ -49,14 +66,22 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate { let downloadItemPart = downloadItem.downloadItemParts.filter { $0.id == downloadItemPartId }.first guard var downloadItemPart = downloadItemPart else { throw LibraryItemDownloadError.downloadItemPartNotFound } + // Call the progress handler + do { + try progressHandler(downloadItem, &downloadItemPart) + } catch { + NSLog("Error while processing progress") + debugPrint(error) + } + // Update the progress - downloadItemPart.progress = percentDownloaded Database.shared.updateDownloadItemPart(downloadItemPart) - + // Notify the UI try! notifyListeners("onItemDownloadUpdate", data: downloadItem.asDictionary()) } catch { - NSLog("DownloadItemError: \(error)") + NSLog("DownloadItemError") + debugPrint(error) } } @@ -147,10 +172,6 @@ public class AbsDownloader: CAPPlugin, URLSessionDownloadDelegate { return itemDirectory } - private func moveLibraryItemToFileDirectory(tempUrl: URL) { - //try FileManager.default.moveItem(at: tempUrl, to: localUrl) - } - } enum LibraryItemDownloadError: String, Error { @@ -161,4 +182,5 @@ enum LibraryItemDownloadError: String, Error { case noTaskDescription = "No task description" case downloadItemNotFound = "DownloadItem not found" case downloadItemPartNotFound = "DownloadItemPart not found" + case downloadItemPartDestinationUrlNotDefined = "DownloadItemPart destination URL not defined" } diff --git a/ios/App/Shared/models/DownloadItem.swift b/ios/App/Shared/models/DownloadItem.swift index afd534db..241671a7 100644 --- a/ios/App/Shared/models/DownloadItem.swift +++ b/ios/App/Shared/models/DownloadItem.swift @@ -51,7 +51,6 @@ extension DownloadItem { struct DownloadItemPart: Realmable, Codable { var id: String = UUID().uuidString var filename: String? - var finalDestinationPath: String? var itemTitle: String? var serverPath: String? var audioTrack: AudioTrack? @@ -61,7 +60,6 @@ struct DownloadItemPart: Realmable, Codable { var failed: Bool = false var uri: String? var destinationUri: String? - var finalDestinationUri: String? var progress: Double = 0 var task: URLSessionDownloadTask! @@ -74,15 +72,40 @@ struct DownloadItemPart: Realmable, Codable { } private enum CodingKeys : String, CodingKey { - case id, filename, completed, moved, failed, progress + case id, filename, itemTitle, completed, moved, failed, progress } } extension DownloadItemPart { - init(filename: String, destination: URL, itemTitle: String, serverPath: String, audioTrack:AudioTrack?, episode: PodcastEpisode?) { - var downloadUrl = "" // TODO: Set this + init(filename: String, destination: URL, itemTitle: String, serverPath: String, audioTrack: AudioTrack?, episode: PodcastEpisode?) { + self.filename = filename + self.itemTitle = itemTitle + self.serverPath = serverPath + self.audioTrack = audioTrack + self.episode = episode + + let config = Store.serverConfig! + var downloadUrl = "\(config.address)\(serverPath)?token=\(config.token)" if (serverPath.hasSuffix("/cover")) { downloadUrl += "&format=jpeg" // For cover images force to jpeg } + self.uri = downloadUrl + self.destinationUri = destination.path + } + + func downloadURL() -> URL? { + if let uri = self.uri { + return URL(string: uri) + } else { + return nil + } + } + + func destinationURL() -> URL? { + if let destinationUri = self.destinationUri { + return URL(fileURLWithPath: destinationUri) + } else { + return nil + } } }