feat: migrate backup albums to sqlite (#20049)
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Docker / pre-job (push) Waiting to run
Docker / Re-Tag ML () (push) Blocked by required conditions
Docker / Re-Tag ML (-armnn) (push) Blocked by required conditions
Docker / Re-Tag ML (-cuda) (push) Blocked by required conditions
Docker / Re-Tag ML (-openvino) (push) Blocked by required conditions
Docker / Re-Tag ML (-rknn) (push) Blocked by required conditions
Docker / Re-Tag ML (-rocm) (push) Blocked by required conditions
Docker / Re-Tag Server () (push) Blocked by required conditions
Docker / Build and Push ML (armnn, linux/arm64, -armnn) (push) Blocked by required conditions
Docker / Build and Push ML (cpu, ) (push) Blocked by required conditions
Docker / Build and Push ML (cuda, linux/amd64, -cuda) (push) Blocked by required conditions
Docker / Build and Push ML (openvino, linux/amd64, -openvino) (push) Blocked by required conditions
Docker / Build and Push ML (rknn, linux/arm64, -rknn) (push) Blocked by required conditions
Docker / Build and Push ML (rocm, linux/amd64, {"linux/amd64": "mich"}, -rocm) (push) Blocked by required conditions
Docker / Build and Push Server (push) Blocked by required conditions
Docker / Docker Build & Push Server Success (push) Blocked by required conditions
Docker / Docker Build & Push ML Success (push) Blocked by required conditions
Docs build / pre-job (push) Waiting to run
Docs build / Docs Build (push) Blocked by required conditions
Static Code Analysis / pre-job (push) Waiting to run
Static Code Analysis / Run Dart Code Analysis (push) Blocked by required conditions
Static Code Analysis / zizmor (push) Waiting to run
Test / pre-job (push) Waiting to run
Test / Test & Lint Server (push) Blocked by required conditions
Test / Unit Test CLI (push) Blocked by required conditions
Test / Unit Test CLI (Windows) (push) Blocked by required conditions
Test / Lint Web (push) Blocked by required conditions
Test / Test Web (push) Blocked by required conditions
Test / Test i18n (push) Blocked by required conditions
Test / End-to-End Lint (push) Blocked by required conditions
Test / Medium Tests (Server) (push) Blocked by required conditions
Test / End-to-End Tests (Server & CLI) (ubuntu-24.04-arm) (push) Blocked by required conditions
Test / End-to-End Tests (Server & CLI) (ubuntu-latest) (push) Blocked by required conditions
Test / End-to-End Tests (Web) (ubuntu-24.04-arm) (push) Blocked by required conditions
Test / End-to-End Tests (Web) (ubuntu-latest) (push) Blocked by required conditions
Test / End-to-End Tests Success (push) Blocked by required conditions
Test / Unit Test Mobile (push) Blocked by required conditions
Test / Unit Test ML (push) Blocked by required conditions
Test / .github Files Formatting (push) Blocked by required conditions
Test / ShellCheck (push) Waiting to run
Test / OpenAPI Clients (push) Waiting to run
Test / SQL Schema Checks (push) Waiting to run

* do not migrate again on app start

* migrate backup albums over to sqlite

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong
2025-07-21 23:26:49 +05:30
committed by GitHub
parent dee6d072fb
commit 5fc4393e7a
3 changed files with 87 additions and 14 deletions

View File

@@ -13,6 +13,18 @@ class BackupAlbum {
BackupAlbum(this.id, this.lastBackup, this.selection);
Id get isarId => fastHash(id);
BackupAlbum copyWith({
String? id,
DateTime? lastBackup,
BackupSelection? selection,
}) {
return BackupAlbum(
id ?? this.id,
lastBackup ?? this.lastBackup,
selection ?? this.selection,
);
}
}
enum BackupSelection {

View File

@@ -56,6 +56,10 @@ class _ChangeExperiencePageState extends ConsumerState<ChangeExperiencePage> {
ref.read(isarProvider),
ref.read(driftProvider),
);
await migrateBackupAlbumsToSqlite(
ref.read(isarProvider),
ref.read(driftProvider),
);
}
} else {
await ref.read(backgroundSyncProvider).cancel();

View File

@@ -2,19 +2,23 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/utils/background_sync.dart';
import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/android_device_asset.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/backup_album.entity.dart'
as isar_backup_album;
import 'package:immich_mobile/entities/etag.entity.dart';
import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
@@ -27,7 +31,7 @@ import 'package:logging/logging.dart';
// ignore: import_rule_photo_manager
import 'package:photo_manager/photo_manager.dart';
const int targetVersion = 14;
const int targetVersion = 13;
Future<void> migrateDatabaseIfNeeded(Isar db) async {
final int version = Store.get(StoreKey.version, targetVersion);
@@ -56,18 +60,6 @@ Future<void> migrateDatabaseIfNeeded(Isar db) async {
await Store.put(StoreKey.photoManagerCustomFilter, true);
}
if (version < 14) {
if (!Store.isBetaTimelineEnabled) {
// Try again when beta timeline is enabled and the app is restarted
return;
}
final backgroundSync = BackgroundSyncManager();
await backgroundSync.syncLocal();
final drift = Drift();
await migrateDeviceAssetToSqlite(db, drift);
await drift.close();
}
if (targetVersion >= 12) {
await Store.put(StoreKey.version, targetVersion);
return;
@@ -204,6 +196,71 @@ Future<void> migrateDeviceAssetToSqlite(Isar db, Drift drift) async {
}
}
Future<void> migrateBackupAlbumsToSqlite(
Isar db,
Drift drift,
) async {
try {
final isarBackupAlbums = await db.backupAlbums.where().findAll();
// Recents is a virtual album on Android, and we don't have it with the new sync
// If recents is selected previously, select all albums during migration except the excluded ones
if (Platform.isAndroid) {
final recentAlbum =
isarBackupAlbums.firstWhereOrNull((album) => album.id == 'isAll');
if (recentAlbum != null) {
await drift.localAlbumEntity.update().write(
const LocalAlbumEntityCompanion(
backupSelection: Value(BackupSelection.selected),
),
);
final excluded = isarBackupAlbums
.where(
(album) =>
album.selection == isar_backup_album.BackupSelection.exclude,
)
.map((album) => album.id)
.toList();
await drift.batch((batch) async {
for (final id in excluded) {
batch.update(
drift.localAlbumEntity,
const LocalAlbumEntityCompanion(
backupSelection: Value(BackupSelection.excluded),
),
where: (t) => t.id.equals(id),
);
}
});
}
return;
}
await drift.batch((batch) {
for (final album in isarBackupAlbums) {
batch.update(
drift.localAlbumEntity,
LocalAlbumEntityCompanion(
backupSelection: Value(
switch (album.selection) {
isar_backup_album.BackupSelection.none => BackupSelection.none,
isar_backup_album.BackupSelection.select =>
BackupSelection.selected,
isar_backup_album.BackupSelection.exclude =>
BackupSelection.excluded,
},
),
),
where: (t) => t.id.equals(album.id),
);
}
});
} catch (error) {
debugPrint(
"[MIGRATION] Error while migrating backup albums to SQLite: $error",
);
}
}
class _DeviceAsset {
final String assetId;
final List<int>? hash;