mirror of
https://github.com/immich-app/immich
synced 2026-08-15 13:03:57 +00:00
simplify remote asset duplication handling
This commit is contained in:
parent
df060eec4f
commit
010230b5f3
1 changed files with 65 additions and 46 deletions
|
|
@ -9,7 +9,6 @@ import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart';
|
|||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/utils/cloud_id_resolver.dart';
|
||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||
|
|
@ -76,37 +75,31 @@ Future<void> _processCloudIdMappingsInBatches(
|
|||
Logger logger,
|
||||
Completer<void> cancellation,
|
||||
) async {
|
||||
String? lastLocalId;
|
||||
final seenRemoteAssetIds = <String>{};
|
||||
String? lastRemoteId;
|
||||
|
||||
while (true) {
|
||||
if (cancellation.isCompleted) {
|
||||
logger.warning('Cloud ID migration cancelled. Stopping batch processing.');
|
||||
break;
|
||||
}
|
||||
final mappings = await _fetchMapping(drift, userId, _kDbPageSize, lastLocalId);
|
||||
final mappings = await fetchMapping(drift, userId, _kDbPageSize, lastRemoteId);
|
||||
if (mappings.isEmpty) {
|
||||
break;
|
||||
}
|
||||
|
||||
final items = <AssetMetadataBulkUpsertItemDto>[];
|
||||
for (final mapping in mappings) {
|
||||
if (!seenRemoteAssetIds.add(mapping.remoteAssetId)) {
|
||||
logger.fine('Duplicate remote asset ID found: ${mapping.remoteAssetId}. Skipping duplicate entry.');
|
||||
continue;
|
||||
}
|
||||
|
||||
items.add(
|
||||
.new(
|
||||
assetId: mapping.remoteAssetId,
|
||||
key: kMobileMetadataKey,
|
||||
value: Map<String, Object>.from(
|
||||
RemoteAssetMobileAppMetadata(
|
||||
cloudId: mapping.localAsset.cloudId,
|
||||
createdAt: mapping.localAsset.createdAt.toIso8601String(),
|
||||
adjustmentTime: mapping.localAsset.adjustmentTime?.toIso8601String(),
|
||||
latitude: mapping.localAsset.latitude?.toString(),
|
||||
longitude: mapping.localAsset.longitude?.toString(),
|
||||
cloudId: mapping.cloudId,
|
||||
createdAt: mapping.createdAt.toIso8601String(),
|
||||
adjustmentTime: mapping.adjustmentTime?.toIso8601String(),
|
||||
latitude: mapping.latitude?.toString(),
|
||||
longitude: mapping.longitude?.toString(),
|
||||
).toJson(),
|
||||
),
|
||||
),
|
||||
|
|
@ -127,7 +120,7 @@ Future<void> _processCloudIdMappingsInBatches(
|
|||
}
|
||||
}
|
||||
|
||||
lastLocalId = mappings.last.localAsset.id;
|
||||
lastRemoteId = mappings.last.remoteAssetId;
|
||||
if (mappings.length < _kDbPageSize) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -144,44 +137,70 @@ Future<void> populateMissingCloudIds(Drift drift, NativeSyncApi nativeSyncApi, C
|
|||
await resolveCloudIds(nativeSyncApi, DriftLocalAlbumRepository(drift), ids, cancellation: cancellation);
|
||||
}
|
||||
|
||||
typedef _CloudIdMapping = ({String remoteAssetId, LocalAsset localAsset});
|
||||
@visibleForTesting
|
||||
typedef CloudIdMapping = ({
|
||||
String remoteAssetId,
|
||||
String cloudId,
|
||||
DateTime createdAt,
|
||||
DateTime? adjustmentTime,
|
||||
double? latitude,
|
||||
double? longitude,
|
||||
});
|
||||
|
||||
Future<List<_CloudIdMapping>> _fetchMapping(Drift drift, String userId, int limit, String? lastLocalId) async {
|
||||
final query =
|
||||
drift.localAssetEntity.select().join([
|
||||
innerJoin(
|
||||
drift.remoteAssetEntity,
|
||||
drift.localAssetEntity.checksum.equalsExp(drift.remoteAssetEntity.checksum),
|
||||
),
|
||||
leftOuterJoin(
|
||||
drift.remoteAssetCloudIdEntity,
|
||||
drift.remoteAssetEntity.id.equalsExp(drift.remoteAssetCloudIdEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
@visibleForTesting
|
||||
Future<List<CloudIdMapping>> fetchMapping(Drift db, String userId, int limit, String? lastRemoteId) async {
|
||||
final query = db.remoteAssetEntity.selectOnly()
|
||||
..addColumns([
|
||||
db.remoteAssetEntity.id,
|
||||
db.localAssetEntity.iCloudId,
|
||||
db.localAssetEntity.createdAt,
|
||||
db.localAssetEntity.adjustmentTime,
|
||||
db.localAssetEntity.latitude,
|
||||
db.localAssetEntity.longitude,
|
||||
])
|
||||
..join([
|
||||
innerJoin(
|
||||
db.localAssetEntity,
|
||||
db.localAssetEntity.id.isInQuery(
|
||||
db.localAssetEntity.selectOnly()
|
||||
..addColumns([db.localAssetEntity.id.min()])
|
||||
..where(db.localAssetEntity.checksum.equalsExp(db.remoteAssetEntity.checksum)),
|
||||
),
|
||||
useColumns: false,
|
||||
),
|
||||
leftOuterJoin(
|
||||
db.remoteAssetCloudIdEntity,
|
||||
db.remoteAssetEntity.id.equalsExp(db.remoteAssetCloudIdEntity.assetId),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..where(
|
||||
db.remoteAssetEntity.ownerId.equals(userId) &
|
||||
// Skip locked assets as we cannot update them without unlocking first
|
||||
db.remoteAssetEntity.visibility.isNotValue(AssetVisibility.locked.index) &
|
||||
db.localAssetEntity.iCloudId.isNotNull() &
|
||||
// Only select assets that have a local cloud ID but either no remote cloud ID or a mismatched eTag
|
||||
drift.localAssetEntity.iCloudId.isNotNull() &
|
||||
drift.remoteAssetEntity.ownerId.equals(userId) &
|
||||
// Skip locked assets as we cannot update them without unlocking first
|
||||
drift.remoteAssetEntity.visibility.isNotValue(AssetVisibility.locked.index) &
|
||||
(drift.remoteAssetCloudIdEntity.cloudId.isNull() |
|
||||
drift.remoteAssetCloudIdEntity.adjustmentTime.isNotExp(drift.localAssetEntity.adjustmentTime) |
|
||||
drift.remoteAssetCloudIdEntity.latitude.isNotExp(drift.localAssetEntity.latitude) |
|
||||
drift.remoteAssetCloudIdEntity.longitude.isNotExp(drift.localAssetEntity.longitude) |
|
||||
drift.remoteAssetCloudIdEntity.createdAt.isNotExp(drift.localAssetEntity.createdAt)),
|
||||
)
|
||||
..orderBy([.asc(drift.localAssetEntity.id)])
|
||||
..limit(limit);
|
||||
(db.remoteAssetCloudIdEntity.cloudId.isNull() |
|
||||
db.remoteAssetCloudIdEntity.adjustmentTime.isNotExp(db.localAssetEntity.adjustmentTime) |
|
||||
db.remoteAssetCloudIdEntity.latitude.isNotExp(db.localAssetEntity.latitude) |
|
||||
db.remoteAssetCloudIdEntity.longitude.isNotExp(db.localAssetEntity.longitude) |
|
||||
db.remoteAssetCloudIdEntity.createdAt.isNotExp(db.localAssetEntity.createdAt)),
|
||||
)
|
||||
..orderBy([.asc(db.remoteAssetEntity.id)])
|
||||
..limit(limit);
|
||||
|
||||
if (lastLocalId != null) {
|
||||
query.where(drift.localAssetEntity.id.isBiggerThanValue(lastLocalId));
|
||||
if (lastRemoteId != null) {
|
||||
query.where(db.remoteAssetEntity.id.isBiggerThanValue(lastRemoteId));
|
||||
}
|
||||
|
||||
return query.map((row) {
|
||||
return (
|
||||
remoteAssetId: row.read(drift.remoteAssetEntity.id)!,
|
||||
localAsset: row.readTable(drift.localAssetEntity).toDto(),
|
||||
remoteAssetId: row.read(db.remoteAssetEntity.id)!,
|
||||
cloudId: row.read(db.localAssetEntity.iCloudId)!,
|
||||
createdAt: row.read(db.localAssetEntity.createdAt)!,
|
||||
adjustmentTime: row.read(db.localAssetEntity.adjustmentTime),
|
||||
latitude: row.read(db.localAssetEntity.latitude),
|
||||
longitude: row.read(db.localAssetEntity.longitude),
|
||||
);
|
||||
}).get();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue