From b9b7ddb5695e1a1eb7760ee6cef1e2f7fa3ce470 Mon Sep 17 00:00:00 2001 From: "immich-push-o-matic[bot]" <179150890+immich-push-o-matic[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 07:23:29 +0000 Subject: [PATCH] fix(mobile): don't re-upload a photo the server already has (#32004) fix(mobile): don't re-upload a photo the server already has (#31941) (cherry picked from commit 02063972e82b1bfd17b6fc2e3b3ff39936229ea5) Co-authored-by: Santo Shakil --- .../services/foreground_upload.service.dart | 22 ++++++++++++++----- .../foreground_upload.service_test.dart | 20 +++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/mobile/lib/services/foreground_upload.service.dart b/mobile/lib/services/foreground_upload.service.dart index bd6a65f45..77042512a 100644 --- a/mobile/lib/services/foreground_upload.service.dart +++ b/mobile/lib/services/foreground_upload.service.dart @@ -255,6 +255,12 @@ class ForegroundUploadService { File? livePhotoFile; try { + final remoteId = (await _assetService.getLocalAsset(asset.id))?.remoteId; + if (remoteId != null) { + await _handleUploadSuccess(asset, remoteId, callbacks); + return; + } + final entity = await _storageRepository.getAssetEntityForAsset(asset); if (entity == null) { callbacks.onError?.call(asset.localId!, assetNotFoundOnDevice); @@ -383,12 +389,7 @@ class ForegroundUploadService { ); if (result.isSuccess && result.remoteAssetId != null) { - callbacks.onSuccess?.call(asset.localId!, result.remoteAssetId!); - try { - await _assetService.stackEditedUpload(asset.localId!, result.remoteAssetId!, asset.checksum); - } catch (error) { - _logger.warning("Failed to stack the upload of ${asset.localId}: $error"); - } + await _handleUploadSuccess(asset, result.remoteAssetId!, callbacks); } else if (result.isCancelled) { shouldAbortUpload = true; } else if (result.errorMessage != null) { @@ -418,6 +419,15 @@ class ForegroundUploadService { } } + Future _handleUploadSuccess(LocalAsset asset, String remoteId, UploadCallbacks callbacks) async { + callbacks.onSuccess?.call(asset.localId!, remoteId); + try { + await _assetService.stackEditedUpload(asset.localId!, remoteId, asset.checksum); + } catch (error) { + _logger.warning("Failed to stack the upload of ${asset.localId}: $error"); + } + } + Future _uploadSingleFile( File file, { required String deviceAssetId, diff --git a/mobile/test/services/foreground_upload.service_test.dart b/mobile/test/services/foreground_upload.service_test.dart index bbfe2e649..2f6e80b52 100644 --- a/mobile/test/services/foreground_upload.service_test.dart +++ b/mobile/test/services/foreground_upload.service_test.dart @@ -55,6 +55,7 @@ void main() { mockConnectivityApi = MockConnectivityApi(); mockAssetMediaRepository = MockAssetMediaRepository(); mockAssetService = MockAssetService(); + when(() => mockAssetService.getLocalAsset(any())).thenAnswer((_) async => null); when(() => mockAssetService.stackEditedUpload(any(), any(), any())).thenAnswer((_) async {}); sut = ForegroundUploadService( @@ -216,6 +217,7 @@ void main() { await sut.uploadSingleAsset(asset, null, callbacks: const UploadCallbacks()); + verify(() => mockAssetService.getLocalAsset(asset.id)).called(1); verify(() => mockAssetService.stackEditedUpload(asset.localId!, 'remote-1', 'sha')).called(1); verifyNoMoreInteractions(mockAssetService); }); @@ -236,8 +238,26 @@ void main() { await sut.uploadSingleAsset(asset, null, callbacks: const UploadCallbacks()); + verify(() => mockAssetService.getLocalAsset(asset.id)).called(1); verify(() => mockAssetService.stackEditedUpload(asset.localId!, 'remote-2', 'sha')).called(1); verifyNoMoreInteractions(mockAssetService); }); + + test('does not upload a photo the server already has', () async { + final asset = LocalAssetStub.image1.copyWith(checksum: 'sha'); + final succeeded = []; + when(() => mockAssetService.getLocalAsset(asset.id)).thenAnswer((_) async => asset.copyWith(remoteId: 'remote')); + + await sut.uploadSingleAsset( + asset, + null, + callbacks: UploadCallbacks(onSuccess: (_, remoteId) => succeeded.add(remoteId)), + ); + + expect(succeeded, equals(['remote'])); + verify(() => mockAssetService.stackEditedUpload(asset.localId!, 'remote', 'sha')).called(1); + verifyZeroInteractions(mockStorageRepository); + verifyZeroInteractions(mockUploadRepository); + }); }); }