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 <santoshakil@users.noreply.github.com>
This commit is contained in:
immich-push-o-matic[bot]
2026-10-02 07:23:29 +00:00
committed by GitHub
co-authored by Santo Shakil
parent 20472b34dc
commit b9b7ddb569
2 changed files with 36 additions and 6 deletions
@@ -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<void> _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<UploadResult> _uploadSingleFile(
File file, {
required String deviceAssetId,
@@ -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 = <String>[];
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);
});
});
}