NCAutoUpload.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. //
  2. // NCAutoUpload.swift
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 27/01/21.
  6. // Copyright © 2021 Marino Faggiana. All rights reserved.
  7. //
  8. // Author Marino Faggiana <marino.faggiana@nextcloud.com>
  9. //
  10. // This program is free software: you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation, either version 3 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. //
  23. import UIKit
  24. import CoreLocation
  25. import NextcloudKit
  26. import Photos
  27. class NCAutoUpload: NSObject {
  28. static let shared = NCAutoUpload()
  29. private let database = NCManageDatabase.shared
  30. private var endForAssetToUpload: Bool = false
  31. private var applicationState = UIApplication.shared.applicationState
  32. private let hud = NCHud()
  33. // MARK: -
  34. func initAutoUpload(controller: NCMainTabBarController?, account: String, completion: @escaping (_ num: Int) -> Void) {
  35. applicationState = UIApplication.shared.applicationState
  36. DispatchQueue.global().async {
  37. guard NCNetworking.shared.isOnline,
  38. let tableAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", account)),
  39. tableAccount.autoUpload else {
  40. return completion(0)
  41. }
  42. NCAskAuthorization().askAuthorizationPhotoLibrary(controller: controller) { hasPermission in
  43. guard hasPermission else {
  44. self.database.setAccountAutoUploadProperty("autoUpload", state: false)
  45. return completion(0)
  46. }
  47. self.uploadAssetsNewAndFull(controller: controller, selector: NCGlobal.shared.selectorUploadAutoUpload, log: "Init Auto Upload", account: account) { num in
  48. completion(num)
  49. }
  50. }
  51. }
  52. }
  53. func initAutoUpload(controller: NCMainTabBarController? = nil, account: String) async -> Int {
  54. await withUnsafeContinuation({ continuation in
  55. initAutoUpload(controller: controller, account: account) { num in
  56. continuation.resume(returning: num)
  57. }
  58. })
  59. }
  60. func autoUploadFullPhotos(controller: NCMainTabBarController?, log: String, account: String) {
  61. applicationState = UIApplication.shared.applicationState
  62. hud.initHudRing(view: controller?.view, text: nil, detailText: nil, tapToCancelDetailText: false)
  63. NCAskAuthorization().askAuthorizationPhotoLibrary(controller: controller) { hasPermission in
  64. guard hasPermission else { return }
  65. DispatchQueue.global().async {
  66. self.uploadAssetsNewAndFull(controller: controller, selector: NCGlobal.shared.selectorUploadAutoUploadAll, log: log, account: account) { _ in
  67. self.hud.dismiss()
  68. }
  69. }
  70. }
  71. }
  72. private func uploadAssetsNewAndFull(controller: NCMainTabBarController?, selector: String, log: String, account: String, completion: @escaping (_ num: Int) -> Void) {
  73. guard let tableAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", account)) else {
  74. return completion(0)
  75. }
  76. let session = NCSession.shared.getSession(account: account)
  77. let autoUploadPath = self.database.getAccountAutoUploadPath(session: session)
  78. var metadatas: [tableMetadata] = []
  79. self.getCameraRollAssets(controller: controller, selector: selector, alignPhotoLibrary: false, account: account) { assets in
  80. guard let assets, !assets.isEmpty else {
  81. NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Automatic upload, no new assets found [" + log + "]")
  82. return completion(0)
  83. }
  84. var num: Float = 0
  85. NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Automatic upload, new \(assets.count) assets found [" + log + "]")
  86. // Create the folder for auto upload & if request the subfolders
  87. self.hud.setText(text: NSLocalizedString("_creating_dir_progress_", comment: ""))
  88. if !NCNetworking.shared.createFolder(assets: assets, useSubFolder: tableAccount.autoUploadCreateSubfolder, withPush: false, hud: self.hud, session: session) {
  89. if selector == NCGlobal.shared.selectorUploadAutoUploadAll {
  90. let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_error_createsubfolders_upload_")
  91. NCContentPresenter().showError(error: error, priority: .max)
  92. }
  93. return completion(0)
  94. }
  95. self.hud.setText(text: NSLocalizedString("_creating_db_photo_progress", comment: ""))
  96. self.hud.progress(0.0)
  97. self.endForAssetToUpload = false
  98. for asset in assets {
  99. var isLivePhoto = false
  100. var uploadSession: String = ""
  101. let assetDate = asset.creationDate ?? Date()
  102. let assetMediaType = asset.mediaType
  103. var serverUrl: String = ""
  104. let fileName = NCUtilityFileSystem().createFileName(asset.originalFilename as String, fileDate: assetDate, fileType: assetMediaType)
  105. if tableAccount.autoUploadCreateSubfolder {
  106. serverUrl = NCUtilityFileSystem().createGranularityPath(asset: asset, serverUrl: autoUploadPath)
  107. } else {
  108. serverUrl = autoUploadPath
  109. }
  110. if asset.mediaSubtypes.contains(.photoLive), NCKeychain().livePhoto {
  111. isLivePhoto = true
  112. }
  113. if selector == NCGlobal.shared.selectorUploadAutoUploadAll {
  114. uploadSession = NCNetworking.shared.sessionUpload
  115. } else {
  116. if assetMediaType == PHAssetMediaType.image && tableAccount.autoUploadWWAnPhoto == false {
  117. uploadSession = NCNetworking.shared.sessionUploadBackground
  118. } else if assetMediaType == PHAssetMediaType.video && tableAccount.autoUploadWWAnVideo == false {
  119. uploadSession = NCNetworking.shared.sessionUploadBackground
  120. } else if assetMediaType == PHAssetMediaType.image && tableAccount.autoUploadWWAnPhoto {
  121. uploadSession = NCNetworking.shared.sessionUploadBackgroundWWan
  122. } else if assetMediaType == PHAssetMediaType.video && tableAccount.autoUploadWWAnVideo {
  123. uploadSession = NCNetworking.shared.sessionUploadBackgroundWWan
  124. } else {
  125. uploadSession = NCNetworking.shared.sessionUploadBackground
  126. }
  127. }
  128. // MOST COMPATIBLE SEARCH --> HEIC --> JPG
  129. var fileNameSearchMetadata = fileName
  130. let ext = (fileNameSearchMetadata as NSString).pathExtension.uppercased()
  131. if ext == "HEIC", NCKeychain().formatCompatibility {
  132. fileNameSearchMetadata = (fileNameSearchMetadata as NSString).deletingPathExtension + ".jpg"
  133. }
  134. if self.database.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@", session.account, serverUrl, fileNameSearchMetadata)) != nil {
  135. if selector == NCGlobal.shared.selectorUploadAutoUpload {
  136. self.database.addPhotoLibrary([asset], account: session.account)
  137. }
  138. } else {
  139. let metadata = self.database.createMetadata(fileName: fileName,
  140. fileNameView: fileName,
  141. ocId: NSUUID().uuidString,
  142. serverUrl: serverUrl,
  143. url: "",
  144. contentType: "",
  145. session: session,
  146. sceneIdentifier: controller?.sceneIdentifier)
  147. if isLivePhoto {
  148. metadata.livePhotoFile = (metadata.fileName as NSString).deletingPathExtension + ".mov"
  149. }
  150. metadata.assetLocalIdentifier = asset.localIdentifier
  151. metadata.session = uploadSession
  152. metadata.sessionSelector = selector
  153. metadata.status = NCGlobal.shared.metadataStatusWaitUpload
  154. metadata.sessionDate = Date()
  155. if assetMediaType == PHAssetMediaType.video {
  156. metadata.classFile = NKCommon.TypeClassFile.video.rawValue
  157. } else if assetMediaType == PHAssetMediaType.image {
  158. metadata.classFile = NKCommon.TypeClassFile.image.rawValue
  159. }
  160. if selector == NCGlobal.shared.selectorUploadAutoUpload {
  161. NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Automatic upload added \(metadata.fileNameView) with Identifier \(metadata.assetLocalIdentifier)")
  162. self.database.addPhotoLibrary([asset], account: account)
  163. }
  164. metadatas.append(metadata)
  165. }
  166. num += 1
  167. self.hud.progress(num: num, total: Float(assets.count))
  168. }
  169. self.endForAssetToUpload = true
  170. NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Start createProcessUploads")
  171. NCNetworkingProcess.shared.createProcessUploads(metadatas: metadatas, completion: completion)
  172. }
  173. }
  174. // MARK: -
  175. @objc func alignPhotoLibrary(controller: NCMainTabBarController?, account: String) {
  176. getCameraRollAssets(controller: controller, selector: NCGlobal.shared.selectorUploadAutoUploadAll, alignPhotoLibrary: true, account: account) { assets in
  177. self.database.clearTable(tablePhotoLibrary.self, account: account)
  178. guard let assets = assets else { return }
  179. self.database.addPhotoLibrary(assets, account: account)
  180. NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Align Photo Library \(assets.count)")
  181. }
  182. }
  183. private func getCameraRollAssets(controller: NCMainTabBarController?, selector: String, alignPhotoLibrary: Bool, account: String, completion: @escaping (_ assets: [PHAsset]?) -> Void) {
  184. NCAskAuthorization().askAuthorizationPhotoLibrary(controller: controller) { hasPermission in
  185. guard hasPermission,
  186. let tableAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", account)) else {
  187. return completion(nil)
  188. }
  189. let assetCollection = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.smartAlbum, subtype: PHAssetCollectionSubtype.smartAlbumUserLibrary, options: nil)
  190. guard let assetCollection = assetCollection.firstObject else { return completion(nil) }
  191. let predicateImage = NSPredicate(format: "mediaType == %i", PHAssetMediaType.image.rawValue)
  192. let predicateVideo = NSPredicate(format: "mediaType == %i", PHAssetMediaType.video.rawValue)
  193. var predicate: NSPredicate?
  194. let fetchOptions = PHFetchOptions()
  195. var newAssets: [PHAsset] = []
  196. if alignPhotoLibrary || (tableAccount.autoUploadImage && tableAccount.autoUploadVideo) {
  197. predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [predicateImage, predicateVideo])
  198. } else if tableAccount.autoUploadImage {
  199. predicate = predicateImage
  200. } else if tableAccount.autoUploadVideo {
  201. predicate = predicateVideo
  202. } else {
  203. return completion(nil)
  204. }
  205. fetchOptions.predicate = predicate
  206. let assets: PHFetchResult<PHAsset> = PHAsset.fetchAssets(in: assetCollection, options: fetchOptions)
  207. if selector == NCGlobal.shared.selectorUploadAutoUpload,
  208. let idAssets = self.database.getPhotoLibraryIdAsset(image: tableAccount.autoUploadImage, video: tableAccount.autoUploadVideo, account: account) {
  209. assets.enumerateObjects { asset, _, _ in
  210. var creationDateString = ""
  211. if let creationDate = asset.creationDate {
  212. creationDateString = String(describing: creationDate)
  213. }
  214. let idAsset = account + asset.localIdentifier + creationDateString
  215. if !idAssets.contains(idAsset) {
  216. if (asset.isFavorite && tableAccount.autoUploadFavoritesOnly) || !tableAccount.autoUploadFavoritesOnly {
  217. newAssets.append(asset)
  218. }
  219. }
  220. }
  221. } else {
  222. assets.enumerateObjects { asset, _, _ in
  223. if (asset.isFavorite && tableAccount.autoUploadFavoritesOnly) || !tableAccount.autoUploadFavoritesOnly {
  224. newAssets.append(asset)
  225. }
  226. }
  227. }
  228. completion(newAssets)
  229. }
  230. }
  231. }