123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- //
- // NCNetworking+Download.swift
- // Nextcloud
- //
- // Created by Marino Faggiana on 07/02/24.
- // Copyright © 2024 Marino Faggiana. All rights reserved.
- //
- // Author Marino Faggiana <marino.faggiana@nextcloud.com>
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
- //
- import UIKit
- import JGProgressHUD
- import NextcloudKit
- import Alamofire
- import Queuer
- extension NCNetworking {
- func download(metadata: tableMetadata,
- withNotificationProgressTask: Bool,
- hudView: UIView? = nil,
- hud: JGProgressHUD? = nil,
- start: @escaping () -> Void = { },
- requestHandler: @escaping (_ request: DownloadRequest) -> Void = { _ in },
- progressHandler: @escaping (_ progress: Progress) -> Void = { _ in },
- completion: @escaping (_ afError: AFError?, _ error: NKError) -> Void = { _, _ in }) {
- if metadata.session == NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload {
- downloadFile(metadata: metadata, withNotificationProgressTask: withNotificationProgressTask, hudView: hudView, hud: hud) {
- start()
- } requestHandler: { request in
- requestHandler(request)
- } progressHandler: { progress in
- progressHandler(progress)
- } completion: { afError, error in
- completion(afError, error)
- }
- } else {
- downloadFileInBackground(metadata: metadata, start: start, completion: { afError, error in
- completion(afError, error)
- })
- }
- }
- private func downloadFile(metadata: tableMetadata,
- withNotificationProgressTask: Bool,
- hudView: UIView?,
- hud: JGProgressHUD?,
- start: @escaping () -> Void = { },
- requestHandler: @escaping (_ request: DownloadRequest) -> Void = { _ in },
- progressHandler: @escaping (_ progress: Progress) -> Void = { _ in },
- completion: @escaping (_ afError: AFError?, _ error: NKError) -> Void = { _, _ in }) {
- guard !metadata.isInTransfer else { return completion(nil, NKError()) }
- var downloadTask: URLSessionTask?
- let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
- let fileNameLocalPath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)
- let options = NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
- if NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) == nil {
- NCManageDatabase.shared.addMetadata(metadata)
- }
- NextcloudKit.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, options: options, requestHandler: { request in
- self.downloadRequest[fileNameLocalPath] = request
- NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
- status: NCGlobal.shared.metadataStatusDownloading)
- requestHandler(request)
- }, taskHandler: { task in
- downloadTask = task
- NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
- taskIdentifier: task.taskIdentifier)
- NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadStartFile),
- object: nil,
- userInfo: ["ocId": metadata.ocId,
- "serverUrl": metadata.serverUrl,
- "account": metadata.account])
- start()
- }, progressHandler: { progress in
- if withNotificationProgressTask, Int(floor(progress.fractionCompleted * 100)).isMultiple(of: 5) {
- NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask),
- object: nil,
- userInfo: ["account": metadata.account,
- "ocId": metadata.ocId,
- "fileName": metadata.fileName,
- "serverUrl": metadata.serverUrl,
- "status": NSNumber(value: NCGlobal.shared.metadataStatusDownloading),
- "progress": NSNumber(value: progress.fractionCompleted),
- "totalBytes": NSNumber(value: progress.totalUnitCount),
- "totalBytesExpected": NSNumber(value: progress.completedUnitCount)])
- }
- progressHandler(progress)
- }) { _, etag, date, length, allHeaderFields, afError, error in
- var error = error
- self.downloadRequest.removeValue(forKey: fileNameLocalPath)
- var dateLastModified: NSDate?
- if let downloadTask = downloadTask {
- if let header = allHeaderFields, let dateString = header["Last-Modified"] as? String {
- dateLastModified = NextcloudKit.shared.nkCommonInstance.convertDate(dateString, format: "EEE, dd MMM y HH:mm:ss zzz")
- }
- if afError?.isExplicitlyCancelledError ?? false {
- error = NKError(errorCode: NCGlobal.shared.errorRequestExplicityCancelled, errorDescription: "error request explicity cancelled")
- }
- self.downloadComplete(fileName: metadata.fileName, serverUrl: metadata.serverUrl, etag: etag, date: date, dateLastModified: dateLastModified, length: length, fileNameLocalPath: fileNameLocalPath, task: downloadTask, error: error)
- }
- completion(afError, error)
- }
- }
- private func downloadFileInBackground(metadata: tableMetadata,
- start: @escaping () -> Void = { },
- requestHandler: @escaping (_ request: DownloadRequest) -> Void = { _ in },
- progressHandler: @escaping (_ progress: Progress) -> Void = { _ in },
- completion: @escaping (_ afError: AFError?, _ error: NKError) -> Void = { _, _ in }) {
- let session: URLSession = sessionManagerDownloadBackground
- let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
- let fileNameLocalPath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)
- start()
- if let task = nkBackground.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, session: session) {
- NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Download file \(metadata.fileNameView) with task with taskIdentifier \(task.taskIdentifier)")
- NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
- status: NCGlobal.shared.metadataStatusDownloading,
- taskIdentifier: task.taskIdentifier)
- NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadStartFile),
- object: nil,
- userInfo: ["ocId": metadata.ocId,
- "serverUrl": metadata.serverUrl,
- "account": metadata.account])
- completion(nil, NKError())
- } else {
- NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
- session: "",
- sessionError: "",
- selector: "",
- status: NCGlobal.shared.metadataStatusNormal)
- completion(nil, NKError(errorCode: NCGlobal.shared.errorResourceNotFound, errorDescription: "task null"))
- }
- }
- func downloadComplete(fileName: String,
- serverUrl: String,
- etag: String?,
- date: NSDate?,
- dateLastModified: NSDate?,
- length: Int64,
- fileNameLocalPath: String?,
- task: URLSessionTask,
- error: NKError) {
- DispatchQueue.global().async {
- guard let metadata = NCManageDatabase.shared.getMetadataFromFileNameLocalPath(fileNameLocalPath) else { return }
- if error == .success {
- #if !EXTENSION
- if let result = NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "fileNameIdentifier == %@ AND serverUrl == %@", metadata.fileName, metadata.serverUrl)) {
- NCEndToEndEncryption.sharedManager()?.decryptFile(metadata.fileName, fileNameView: metadata.fileNameView, ocId: metadata.ocId, key: result.key, initializationVector: result.initializationVector, authenticationTag: result.authenticationTag)
- }
- #endif
- NCManageDatabase.shared.addLocalFile(metadata: metadata)
- NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
- session: "",
- sessionError: "",
- status: NCGlobal.shared.metadataStatusNormal,
- etag: etag)
- NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile),
- object: nil,
- userInfo: ["ocId": metadata.ocId,
- "serverUrl": metadata.serverUrl,
- "account": metadata.account,
- "selector": metadata.sessionSelector,
- "error": error])
- } else if error.errorCode == NSURLErrorCancelled || error.errorCode == NCGlobal.shared.errorRequestExplicityCancelled {
- NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
- session: "",
- sessionError: "",
- selector: "",
- status: NCGlobal.shared.metadataStatusNormal)
- NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadCancelFile),
- object: nil,
- userInfo: ["ocId": metadata.ocId,
- "serverUrl": metadata.serverUrl,
- "account": metadata.account])
- } else {
- NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
- session: "",
- sessionError: "",
- selector: "",
- status: NCGlobal.shared.metadataStatusNormal)
- NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile),
- object: nil,
- userInfo: ["ocId": metadata.ocId,
- "serverUrl": metadata.serverUrl,
- "account": metadata.account,
- "selector": metadata.sessionSelector,
- "error": error])
- }
- self.downloadMetadataInBackground.removeValue(forKey: FileNameServerUrl(fileName: fileName, serverUrl: serverUrl))
- self.delegate?.downloadComplete?(fileName: fileName, serverUrl: serverUrl, etag: etag, date: date, dateLastModified: dateLastModified, length: length, fileNameLocalPath: fileNameLocalPath, task: task, error: error)
- }
- }
- func downloadProgress(_ progress: Float,
- totalBytes: Int64,
- totalBytesExpected: Int64,
- fileName: String,
- serverUrl: String,
- session: URLSession,
- task: URLSessionTask) {
- DispatchQueue.global().async {
- self.delegate?.downloadProgress?(progress, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected, fileName: fileName, serverUrl: serverUrl, session: session, task: task)
- var metadata: tableMetadata?
- if let metadataTmp = self.downloadMetadataInBackground[FileNameServerUrl(fileName: fileName, serverUrl: serverUrl)] {
- metadata = metadataTmp
- } else if let metadataTmp = NCManageDatabase.shared.getMetadataFromFileName(fileName, serverUrl: serverUrl) {
- self.downloadMetadataInBackground[FileNameServerUrl(fileName: fileName, serverUrl: serverUrl)] = metadataTmp
- metadata = metadataTmp
- }
- if let metadata = metadata, Int(floor(progress * 100)).isMultiple(of: 5) {
- NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask),
- object: nil,
- userInfo: ["account": metadata.account,
- "ocId": metadata.ocId,
- "fileName": metadata.fileName,
- "serverUrl": metadata.serverUrl,
- "status": NSNumber(value: NCGlobal.shared.metadataStatusDownloading),
- "progress": NSNumber(value: progress),
- "totalBytes": NSNumber(value: totalBytes),
- "totalBytesExpected": NSNumber(value: totalBytesExpected)])
- }
- }
- }
- #if !EXTENSION
- func downloadAvatar(user: String,
- dispalyName: String?,
- fileName: String,
- cell: NCCellProtocol,
- view: UIView?,
- cellImageView: UIImageView?) {
- let fileNameLocalPath = utilityFileSystem.directoryUserData + "/" + fileName
- if let image = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) {
- cellImageView?.image = image
- cell.fileAvatarImageView?.image = image
- return
- }
- if let account = NCManageDatabase.shared.getActiveAccount() {
- cellImageView?.image = utility.loadUserImage(for: user, displayName: dispalyName, userBaseUrl: account)
- }
- for case let operation as NCOperationDownloadAvatar in downloadAvatarQueue.operations where operation.fileName == fileName { return }
- downloadAvatarQueue.addOperation(NCOperationDownloadAvatar(user: user, fileName: fileName, fileNameLocalPath: fileNameLocalPath, cell: cell, view: view, cellImageView: cellImageView))
- }
- #endif
- func cancelDownloadTasks() {
- downloadRequest.removeAll()
- let sessionManager = NextcloudKit.shared.sessionManager
- sessionManager.session.getTasksWithCompletionHandler { _, _, downloadTasks in
- downloadTasks.forEach {
- $0.cancel()
- }
- }
- if let results = NCManageDatabase.shared.getResultsMetadatas(predicate: NSPredicate(format: "status < 0 AND session == %@", NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload)) {
- NCManageDatabase.shared.clearMetadataSession(metadatas: results)
- }
- }
- func cancelDownloadBackgroundTask() {
- Task {
- let tasksBackground = await NCNetworking.shared.sessionManagerDownloadBackground.tasks
- for task in tasksBackground.2 { // ([URLSessionDataTask], [URLSessionUploadTask], [URLSessionDownloadTask])
- task.cancel()
- }
- if let results = NCManageDatabase.shared.getResultsMetadatas(predicate: NSPredicate(format: "status < 0 AND session == %@", NCNetworking.shared.sessionDownloadBackground)) {
- NCManageDatabase.shared.clearMetadataSession(metadatas: results)
- }
- }
- }
- }
- class NCOperationDownload: ConcurrentOperation {
- var metadata: tableMetadata
- var selector: String
- init(metadata: tableMetadata, selector: String) {
- self.metadata = tableMetadata.init(value: metadata)
- self.selector = selector
- }
- override func start() {
- guard !isCancelled else { return self.finish() }
- metadata.session = NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload
- metadata.sessionError = ""
- metadata.sessionSelector = selector
- metadata.sessionTaskIdentifier = 0
- metadata.status = NCGlobal.shared.metadataStatusWaitDownload
- NCManageDatabase.shared.addMetadata(metadata)
- NCNetworking.shared.download(metadata: metadata, withNotificationProgressTask: true) {
- } completion: { _, _ in
- self.finish()
- }
- }
- }
|