more fixes

This commit is contained in:
shenlong-tanwen
2025-12-04 02:09:52 +05:30
parent b06f591b7a
commit a2a5b7e758
9 changed files with 91 additions and 48 deletions

View File

@@ -1,4 +1,5 @@
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/extensions/num_extensions.dart';
part 'local_asset.model.dart'; part 'local_asset.model.dart';
part 'remote_asset.model.dart'; part 'remote_asset.model.dart';

View File

@@ -42,8 +42,13 @@ class LocalAsset extends BaseAsset {
@override @override
String get heroTag => '${id}_${remoteId ?? checksum}'; String get heroTag => '${id}_${remoteId ?? checksum}';
String get eTag => String get eTag {
"${createdAt.millisecondsSinceEpoch ~/ 1000}$kUploadETagDelimiter${(adjustmentTime?.millisecondsSinceEpoch ?? 0) ~/ 1000}$kUploadETagDelimiter${latitude ?? 0}$kUploadETagDelimiter${longitude ?? 0}"; final createdAt = this.createdAt.millisecondsSinceEpoch ~/ 1000;
final adjustmentTime = this.adjustmentTime?.millisecondsSinceEpoch ?? 0;
final latitude = this.latitude?.truncateTo(2).toStringAsFixed(2) ?? "0.00";
final longitude = this.longitude?.truncateTo(2).toStringAsFixed(2) ?? "0.00";
return "$createdAt$kUploadETagDelimiter$adjustmentTime$kUploadETagDelimiter$latitude$kUploadETagDelimiter$longitude";
}
bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0; bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0;

View File

@@ -1,7 +1,8 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart'; 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/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
import 'package:immich_mobile/platform/native_sync_api.g.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart';
@@ -33,12 +34,12 @@ Future<void> syncCloudIds(ProviderContainer ref) async {
for (final mapping in mappingsToUpdate) { for (final mapping in mappingsToUpdate) {
final mobileMeta = AssetMetadataUpsertItemDto( final mobileMeta = AssetMetadataUpsertItemDto(
key: AssetMetadataKey.mobileApp, key: AssetMetadataKey.mobileApp,
value: RemoteAssetMobileAppMetadata(cloudId: mapping.cloudId, eTag: mapping.eTag), value: RemoteAssetMobileAppMetadata(cloudId: mapping.localAsset.cloudId, eTag: mapping.localAsset.eTag),
); );
try { try {
await assetApi.updateAssetMetadata(mapping.assetId, AssetMetadataUpsertDto(items: [mobileMeta])); await assetApi.updateAssetMetadata(mapping.remoteAssetId, AssetMetadataUpsertDto(items: [mobileMeta]));
} catch (error, stack) { } catch (error, stack) {
Logger('migrateCloudIds').warning('Failed to update metadata for asset ${mapping.assetId}', error, stack); Logger('migrateCloudIds').warning('Failed to update metadata for asset ${mapping.remoteAssetId}', error, stack);
} }
} }
} }
@@ -52,47 +53,30 @@ Future<void> _populateCloudIds(Drift drift) async {
await DriftLocalAlbumRepository(drift).updateCloudMapping(cloudMapping); await DriftLocalAlbumRepository(drift).updateCloudMapping(cloudMapping);
} }
typedef _CloudIdMapping = ({String assetId, String cloudId, String eTag}); typedef _CloudIdMapping = ({String remoteAssetId, LocalAsset localAsset});
Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId) async { Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId) async {
final query = final query =
drift.remoteAssetEntity.selectOnly().join([ drift.remoteAssetEntity.select().join([
leftOuterJoin( leftOuterJoin(
drift.localAssetEntity, drift.localAssetEntity,
drift.localAssetEntity.checksum.equalsExp(drift.remoteAssetEntity.checksum), drift.localAssetEntity.checksum.equalsExp(drift.remoteAssetEntity.checksum),
useColumns: false,
), ),
leftOuterJoin( leftOuterJoin(
drift.remoteAssetCloudIdEntity, drift.remoteAssetCloudIdEntity,
drift.localAssetEntity.iCloudId.equalsExp(drift.remoteAssetCloudIdEntity.cloudId), drift.localAssetEntity.iCloudId.equalsExp(drift.remoteAssetCloudIdEntity.cloudId),
useColumns: false, useColumns: false,
), ),
]) ])..where(
..addColumns([
drift.remoteAssetEntity.id,
drift.localAssetEntity.iCloudId,
drift.localAssetEntity.createdAt,
drift.localAssetEntity.adjustmentTime,
drift.localAssetEntity.latitude,
drift.localAssetEntity.longitude,
])
..where(
drift.localAssetEntity.id.isNotNull() & drift.localAssetEntity.id.isNotNull() &
drift.localAssetEntity.iCloudId.isNotNull() & drift.localAssetEntity.iCloudId.isNotNull() &
drift.remoteAssetEntity.ownerId.equals(userId) & drift.remoteAssetEntity.ownerId.equals(userId) &
drift.remoteAssetCloudIdEntity.cloudId.isNull(), drift.remoteAssetCloudIdEntity.cloudId.isNull(),
); );
return query.map((row) { return query.map((row) {
final createdAt = row.read(drift.localAssetEntity.createdAt)!;
final adjustmentTime = row.read(drift.localAssetEntity.adjustmentTime);
final latitude = row.read(drift.localAssetEntity.latitude);
final longitude = row.read(drift.localAssetEntity.longitude);
final eTag =
"${createdAt.millisecondsSinceEpoch ~/ 1000}$kUploadETagDelimiter${(adjustmentTime?.millisecondsSinceEpoch ?? 0) ~/ 1000}$kUploadETagDelimiter${latitude ?? 0}$kUploadETagDelimiter${longitude ?? 0}";
return ( return (
assetId: row.read(drift.remoteAssetEntity.id)!, remoteAssetId: row.read(drift.remoteAssetEntity.id)!,
cloudId: row.read(drift.localAssetEntity.iCloudId)!, localAsset: row.readTable(drift.localAssetEntity).toDto(),
eTag: eTag,
); );
}).get(); }).get();
} }

View File

@@ -0,0 +1,17 @@
import 'dart:math';
import 'package:drift/drift.dart';
// ignore: invalid_use_of_internal_member, implementation_imports
import 'package:drift/src/runtime/query_builder/expressions/internal.dart';
extension DoubleTruncateExpression<T extends num> on Expression<T> {
Expression<T> truncateTo(int fractionDigits) {
final mod = Constant(pow(10, fractionDigits).toDouble());
return BaseInfixOperator(
BaseInfixOperator(this, '*', mod, precedence: Precedence.mulDivide).cast(DriftSqlType.int),
'/',
mod,
precedence: Precedence.mulDivide,
);
}
}

View File

@@ -0,0 +1,8 @@
import 'dart:math' as math;
extension DoubleTruncate on double {
double truncateTo(int fractionDigits) {
final mod = math.pow(10.0, fractionDigits);
return ((this * mod).truncate() / mod);
}
}

View File

@@ -22,7 +22,10 @@ SELECT
rae.live_photo_video_id, rae.live_photo_video_id,
0 as orientation, 0 as orientation,
rae.stack_id, rae.stack_id,
NULL as i_cloud_id NULL as i_cloud_id,
NULL as latitude,
NULL as longitude,
NULL as adjustmentTime
FROM FROM
remote_asset_entity rae remote_asset_entity rae
LEFT JOIN LEFT JOIN
@@ -55,7 +58,10 @@ SELECT
NULL as live_photo_video_id, NULL as live_photo_video_id,
lae.orientation, lae.orientation,
NULL as stack_id, NULL as stack_id,
lae.i_cloud_id lae.i_cloud_id,
lae.latitude,
lae.longitude,
lae.adjustment_time
FROM FROM
local_asset_entity lae local_asset_entity lae
WHERE NOT EXISTS ( WHERE NOT EXISTS (

View File

@@ -29,7 +29,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
); );
$arrayStartIndex += generatedlimit.amountOfVariables; $arrayStartIndex += generatedlimit.amountOfVariables;
return customSelect( return customSelect(
'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, NULL AS i_cloud_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, lae.i_cloud_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}', 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, NULL AS i_cloud_id, NULL AS latitude, NULL AS longitude, NULL AS adjustmentTime FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, lae.i_cloud_id, lae.latitude, lae.longitude, lae.adjustment_time FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}',
variables: [ variables: [
for (var $ in userIds) i0.Variable<String>($), for (var $ in userIds) i0.Variable<String>($),
...generatedlimit.introducedVariables, ...generatedlimit.introducedVariables,
@@ -63,6 +63,9 @@ class MergedAssetDrift extends i1.ModularAccessor {
orientation: row.read<int>('orientation'), orientation: row.read<int>('orientation'),
stackId: row.readNullable<String>('stack_id'), stackId: row.readNullable<String>('stack_id'),
iCloudId: row.readNullable<String>('i_cloud_id'), iCloudId: row.readNullable<String>('i_cloud_id'),
latitude: row.readNullable<double>('latitude'),
longitude: row.readNullable<double>('longitude'),
adjustmentTime: row.readNullable<DateTime>('adjustmentTime'),
), ),
); );
} }
@@ -131,6 +134,9 @@ class MergedAssetResult {
final int orientation; final int orientation;
final String? stackId; final String? stackId;
final String? iCloudId; final String? iCloudId;
final double? latitude;
final double? longitude;
final DateTime? adjustmentTime;
MergedAssetResult({ MergedAssetResult({
this.remoteId, this.remoteId,
this.localId, this.localId,
@@ -149,6 +155,9 @@ class MergedAssetResult {
required this.orientation, required this.orientation,
this.stackId, this.stackId,
this.iCloudId, this.iCloudId,
this.latitude,
this.longitude,
this.adjustmentTime,
}); });
} }

View File

@@ -1,8 +1,11 @@
import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/drift_extensions.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
@@ -133,10 +136,17 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
} }
Future<Map<String, String>> getHashMappingFromCloudId() async { Future<Map<String, String>> getHashMappingFromCloudId() async {
final createdAt = coalesce([_db.localAssetEntity.createdAt.strftime('%s'), const Constant('0')]); final createdAt = _db.localAssetEntity.createdAt.strftime('%s');
final adjustmentTime = coalesce([_db.localAssetEntity.adjustmentTime.strftime('%s'), const Constant('0')]); final adjustmentTime = coalesce([_db.localAssetEntity.adjustmentTime.strftime('%s'), const Constant('0')]);
final latitude = coalesce([_db.localAssetEntity.latitude.cast(DriftSqlType.string), const Constant('0')]); final latitude = coalesce([
final longitude = coalesce([_db.localAssetEntity.longitude.cast(DriftSqlType.string), const Constant('0')]); _db.localAssetEntity.latitude,
const Constant(0.0),
]).truncateTo(2).cast(DriftSqlType.string);
final longitude = coalesce([
_db.localAssetEntity.longitude,
const Constant(0.0),
]).truncateTo(2).cast(DriftSqlType.string);
final delimiter = const Constant(kUploadETagDelimiter); final delimiter = const Constant(kUploadETagDelimiter);
final eTag = createdAt + delimiter + adjustmentTime + delimiter + latitude + delimiter + longitude; final eTag = createdAt + delimiter + adjustmentTime + delimiter + latitude + delimiter + longitude;

View File

@@ -85,6 +85,9 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
durationInSeconds: row.durationInSeconds, durationInSeconds: row.durationInSeconds,
orientation: row.orientation, orientation: row.orientation,
cloudId: row.iCloudId, cloudId: row.iCloudId,
latitude: row.latitude,
longitude: row.longitude,
adjustmentTime: row.adjustmentTime,
), ),
) )
.get(); .get();