From a9687c65f386c3d02c489f064cba6696a3a08a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Coye=20de=20Brune=CC=81lis?= Date: Thu, 20 Feb 2025 16:32:57 +0100 Subject: [PATCH 1/2] fix(PhotoLibraryUploader): Prevent photo library from adding multiple times an UploadFile to the upload queue --- .../UploadQueue/Queue/UploadQueue+Queue.swift | 2 +- .../Servicies/PhotoLibraryUploader+Scan.swift | 75 ++++++++++++------- .../Servicies/PhotoLibraryUploader.swift | 10 +++ 3 files changed, 60 insertions(+), 27 deletions(-) diff --git a/kDriveCore/Data/UploadQueue/Queue/UploadQueue+Queue.swift b/kDriveCore/Data/UploadQueue/Queue/UploadQueue+Queue.swift index 8850db104..75b1f899f 100644 --- a/kDriveCore/Data/UploadQueue/Queue/UploadQueue+Queue.swift +++ b/kDriveCore/Data/UploadQueue/Queue/UploadQueue+Queue.swift @@ -74,7 +74,7 @@ extension UploadQueue: UploadQueueable { public func rebuildUploadQueueFromObjectsInRealm(_ caller: StaticString = #function) { Log.uploadQueue("rebuildUploadQueueFromObjectsInRealm caller:\(caller)") - concurrentQueue.sync { + serialQueue.sync { // Clean cache if necessary before we try to restart the uploads. @InjectService var freeSpaceService: FreeSpaceService freeSpaceService.cleanCacheIfAlmostFull() diff --git a/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader+Scan.swift b/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader+Scan.swift index 0b55e953e..50831737c 100644 --- a/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader+Scan.swift +++ b/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader+Scan.swift @@ -34,36 +34,41 @@ public extension PhotoLibraryUploader { var newAssetsCount = 0 - let options = PHFetchOptions() - options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)] - - let typesPredicates = getAssetPredicates(forSettings: frozenSettings) - let datePredicate = getDatePredicate(with: frozenSettings) - let typePredicate = NSCompoundPredicate(orPredicateWithSubpredicates: typesPredicates) - options.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [datePredicate, typePredicate]) - - Log.photoLibraryUploader("Fetching new pictures/videos with predicate: \(options.predicate!.predicateFormat)") - let assetsFetchResult = PHAsset.fetchAssets(with: options) - let syncDate = Date() - - try? uploadsDatabase.writeTransaction { writableRealm in - do { - try addImageAssetsToUploadQueue( - assetsFetchResult: assetsFetchResult, - initial: frozenSettings.lastSync.timeIntervalSince1970 == 0, - writableRealm: writableRealm - ) + serialQueue.sync { + let options = PHFetchOptions() + options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)] + + let typesPredicates = getAssetPredicates(forSettings: frozenSettings) + let datePredicate = getDatePredicate(with: frozenSettings) + let typePredicate = NSCompoundPredicate(orPredicateWithSubpredicates: typesPredicates) + options.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [datePredicate, typePredicate]) + + Log.photoLibraryUploader("Fetching new pictures/videos with predicate: \(options.predicate!.predicateFormat)") + let assetsFetchResult = PHAsset.fetchAssets(with: options) + let syncDate = Date() + + try? uploadsDatabase.writeTransaction { writableRealm in + do { + try addImageAssetsToUploadQueue( + assetsFetchResult: assetsFetchResult, + initial: frozenSettings.lastSync.timeIntervalSince1970 == 0, + writableRealm: writableRealm + ) - updateLastSyncDate(syncDate, writableRealm: writableRealm) + updateLastSyncDate(syncDate, writableRealm: writableRealm) - newAssetsCount = assetsFetchResult.count - Log.photoLibraryUploader("New assets count:\(newAssetsCount)") - } catch ErrorDomain.importCancelledBySystem { - Log.photoLibraryUploader("System is requesting to stop", level: .error) - } catch { - Log.photoLibraryUploader("addImageAssetsToUploadQueue error:\(error)", level: .error) + newAssetsCount = assetsFetchResult.count + Log.photoLibraryUploader("New assets count:\(newAssetsCount)") + } catch ErrorDomain.importCancelledBySystem { + Log.photoLibraryUploader("System is requesting to stop", level: .error) + } catch { + Log.photoLibraryUploader("addImageAssetsToUploadQueue error:\(error)", level: .error) + } } + + Log.photoLibraryUploader("scheduleNewPicturesForUpload FINISHED") } + return newAssetsCount } @@ -73,6 +78,7 @@ public extension PhotoLibraryUploader { if let settings = writableRealm.objects(PhotoSyncSettings.self).first, !settings.isInvalidated { settings.lastSync = date + Log.photoLibraryUploader("updateLastSyncDate: \(date)") } } @@ -151,6 +157,12 @@ public extension PhotoLibraryUploader { return } + guard !assetAlreadyPendingUpload(bestResourceSHA256: bestResourceSHA256, + writableRealm: writableRealm) else { + Log.photoLibraryUploader("Asset already in pending upload") + return + } + let algorithmImportVersion = currentDiffAlgorithmVersion // New UploadFile to be uploaded. Priority is `.low`, first sync is `.normal` @@ -193,6 +205,17 @@ public extension PhotoLibraryUploader { } } + private func assetAlreadyPendingUpload(bestResourceSHA256: String?, + writableRealm: Realm) -> Bool { + guard let bestResourceSHA256 else { + return false + } + guard !writableRealm.objects(UploadFile.self).filter("bestResourceSHA256 == %@", bestResourceSHA256).isEmpty else { + return false + } + return true + } + private func getPhotoLibraryName( forAsset asset: PHAsset, settings: PhotoSyncSettings, diff --git a/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader.swift b/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader.swift index 8c6adca5e..4d636d77c 100644 --- a/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader.swift +++ b/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader.swift @@ -29,6 +29,16 @@ public final class PhotoLibraryUploader { @LazyInjectService var uploadQueue: UploadQueue @LazyInjectService(customTypeIdentifier: kDriveDBID.uploads) var uploadsDatabase: Transactionable + let serialQueue: DispatchQueue = { + @LazyInjectService var appContextService: AppContextServiceable + let autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = appContextService.isExtension ? .workItem : .inherit + + return DispatchQueue( + label: "com.infomaniak.drive.photo-library", + autoreleaseFrequency: autoreleaseFrequency + ) + }() + /// Threshold value to trigger cleaning of photo roll if enabled static let removeAssetsCountThreshold = 10 From f60bc773beeeb6c6d814191de73e8f5ccda28758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Coye=20de=20Brune=CC=81lis?= Date: Fri, 21 Feb 2025 08:15:40 +0100 Subject: [PATCH 2/2] refactor: Removed extraneous logs --- .../Data/UploadQueue/Servicies/PhotoLibraryUploader+Scan.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader+Scan.swift b/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader+Scan.swift index 50831737c..3909ca1cc 100644 --- a/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader+Scan.swift +++ b/kDriveCore/Data/UploadQueue/Servicies/PhotoLibraryUploader+Scan.swift @@ -65,8 +65,6 @@ public extension PhotoLibraryUploader { Log.photoLibraryUploader("addImageAssetsToUploadQueue error:\(error)", level: .error) } } - - Log.photoLibraryUploader("scheduleNewPicturesForUpload FINISHED") } return newAssetsCount @@ -78,7 +76,6 @@ public extension PhotoLibraryUploader { if let settings = writableRealm.objects(PhotoSyncSettings.self).first, !settings.isInvalidated { settings.lastSync = date - Log.photoLibraryUploader("updateLastSyncDate: \(date)") } }