From 60fabf586c4106b3957833cc6ca14eb02b22e1b1 Mon Sep 17 00:00:00 2001 From: Alberto Sendra Date: Fri, 18 Oct 2024 13:12:18 +0200 Subject: [PATCH] Fix handling of download finalization in MediaDownloader --- .../OfflineEngine/Internal/DownloadTask.swift | 48 +++++++++----- .../Internal/MediaDownloader.swift | 63 +++++++++++-------- 2 files changed, 68 insertions(+), 43 deletions(-) diff --git a/Sources/Player/OfflineEngine/Internal/DownloadTask.swift b/Sources/Player/OfflineEngine/Internal/DownloadTask.swift index 982971d6..94248f72 100644 --- a/Sources/Player/OfflineEngine/Internal/DownloadTask.swift +++ b/Sources/Player/OfflineEngine/Internal/DownloadTask.swift @@ -21,6 +21,8 @@ final class DownloadTask { private weak var monitor: DownloadTaskMonitor? + private let queue: OperationQueue + var playbackInfo: PlaybackInfo? var startTimestamp: UInt64? @@ -43,6 +45,10 @@ final class DownloadTask { self.mediaProduct = mediaProduct self.monitor = monitor + queue = OperationQueue() + queue.maxConcurrentOperationCount = 1 + queue.qualityOfService = .userInitiated + contentKeySession = AVContentKeySession(keySystem: .fairPlayStreaming) let now = PlayerWorld.timeProvider.timestamp() @@ -87,34 +93,42 @@ final class DownloadTask { } func setLicenseUrl(_ url: URL) { - localLicenseUrl = url - finalize() + queue.dispatch { + self.localLicenseUrl = url + self.finalize() + } } func setMediaUrl(_ url: URL) { - localAssetUrl = url - localAssetSize = calculateSize() - finalize() + queue.dispatch { + self.localAssetUrl = url + self.localAssetSize = self.calculateSize() + self.finalize() + } } func reportProgress(_ progress: Double) { - monitor?.progress(downloadTask: self, progress: progress) + queue.dispatch { + self.monitor?.progress(downloadTask: self, progress: progress) + } } func failed(with error: Error) { - self.error = error - endTimestamp = PlayerWorld.timeProvider.timestamp() + queue.dispatch { + self.error = error + self.endTimestamp = PlayerWorld.timeProvider.timestamp() - let fileManager = PlayerWorld.fileManagerClient - if let localAssetUrl { - try? fileManager.removeItem(at: localAssetUrl) - } - if let localLicenseUrl { - try? fileManager.removeItem(at: localLicenseUrl) - } + let fileManager = PlayerWorld.fileManagerClient + if let localAssetUrl = self.localAssetUrl { + try? fileManager.removeItem(at: localAssetUrl) + } + if let localLicenseUrl = self.localLicenseUrl { + try? fileManager.removeItem(at: localLicenseUrl) + } - PlayerWorld.logger?.log(loggable: PlayerLoggable.downloadFailed(error: error)) - monitor?.failed(downloadTask: self, with: error) + PlayerWorld.logger?.log(loggable: PlayerLoggable.downloadFailed(error: error)) + self.monitor?.failed(downloadTask: self, with: error) + } } } diff --git a/Sources/Player/OfflineEngine/Internal/MediaDownloader.swift b/Sources/Player/OfflineEngine/Internal/MediaDownloader.swift index cd51c9db..01c1594f 100644 --- a/Sources/Player/OfflineEngine/Internal/MediaDownloader.swift +++ b/Sources/Player/OfflineEngine/Internal/MediaDownloader.swift @@ -62,28 +62,19 @@ private extension MediaDownloader { } } -// MARK: AVAssetDownloadDelegate, URLSessionDownloadDelegate +// MARK: URLSessionDownloadDelegate -extension MediaDownloader: AVAssetDownloadDelegate, URLSessionDownloadDelegate { +extension MediaDownloader: URLSessionDownloadDelegate { func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - defer { - activeTasks.removeValue(forKey: task) - } - guard let error else { return } - activeTasks[task]?.failed(with: error) - } - - func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) { defer { - activeTasks.removeValue(forKey: assetDownloadTask) + activeTasks.removeValue(forKey: task) } - let task = activeTasks[assetDownloadTask] - task?.setMediaUrl(location) + activeTasks[task]?.failed(with: error) } func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { @@ -92,9 +83,14 @@ extension MediaDownloader: AVAssetDownloadDelegate, URLSessionDownloadDelegate { } do { - let uuid = PlayerWorld.uuidProvider.uuidString() let fileManager = PlayerWorld.fileManagerClient - let url = fileManager.cachesDirectory().appendingPathComponent(uuid) + let appSupportURL = fileManager.applicationSupportDirectory() + let directoryURL = appSupportURL.appendingPathComponent("ProgressiveDownloads", isDirectory: true) + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true) + + let uuid = PlayerWorld.uuidProvider.uuidString() + let url = directoryURL.appendingPathComponent(uuid) + try fileManager.moveFile(location, url) let task = activeTasks[downloadTask] @@ -105,6 +101,30 @@ extension MediaDownloader: AVAssetDownloadDelegate, URLSessionDownloadDelegate { } } + func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64 + ) { + let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite) + activeTasks[downloadTask]?.reportProgress(progress) + } +} + +// MARK: AVAssetDownloadDelegate + +extension MediaDownloader: AVAssetDownloadDelegate { + func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) { + defer { + activeTasks.removeValue(forKey: assetDownloadTask) + } + + let task = activeTasks[assetDownloadTask] + task?.setMediaUrl(location) + } + func urlSession( _ session: URLSession, assetDownloadTask: AVAssetDownloadTask, @@ -124,16 +144,7 @@ extension MediaDownloader: AVAssetDownloadDelegate, URLSessionDownloadDelegate { ) ) - activeTasks[assetDownloadTask]?.reportProgress(loadedDuration / expectedDuration) - } - - func urlSession( - _ session: URLSession, - downloadTask: URLSessionDownloadTask, - didWriteData bytesWritten: Int64, - totalBytesWritten: Int64, - totalBytesExpectedToWrite: Int64 - ) { - activeTasks[downloadTask]?.reportProgress(Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)) + let progress = loadedDuration / expectedDuration + activeTasks[assetDownloadTask]?.reportProgress(progress) } }