mirror of
https://github.com/immich-app/immich.git
synced 2025-12-08 01:10:00 +03:00
Compare commits
1 Commits
fix/period
...
fix/drift-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bee2bbdfb |
@@ -20,6 +20,7 @@ targets:
|
||||
- lib/infrastructure/entities/*.drift
|
||||
- lib/infrastructure/repositories/db.repository.dart
|
||||
- lib/infrastructure/repositories/logger_db.repository.dart
|
||||
- lib/infrastructure/repositories/background_downloader.repository.dart
|
||||
drift_dev:modular:
|
||||
enabled: true
|
||||
options: *drift_options
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||
|
||||
class TaskRecordEntity extends Table with DriftDefaultsMixin {
|
||||
const TaskRecordEntity();
|
||||
|
||||
TextColumn get taskId => text()();
|
||||
|
||||
TextColumn get url => text()();
|
||||
|
||||
TextColumn get filename => text()();
|
||||
|
||||
TextColumn get group => text()();
|
||||
|
||||
TextColumn get metaData => text()();
|
||||
|
||||
IntColumn get creationTime => integer()();
|
||||
|
||||
IntColumn get status => integer()();
|
||||
|
||||
RealColumn get progress => real()();
|
||||
|
||||
TextColumn get objectJsonMap => text()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {taskId};
|
||||
}
|
||||
|
||||
class PausedTasksEntity extends Table with DriftDefaultsMixin {
|
||||
const PausedTasksEntity();
|
||||
|
||||
TextColumn get taskId => text()();
|
||||
|
||||
IntColumn get modified => integer()();
|
||||
|
||||
TextColumn get objectJsonMap => text()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {taskId};
|
||||
}
|
||||
|
||||
class ModifiedTasksEntity extends Table with DriftDefaultsMixin {
|
||||
const ModifiedTasksEntity();
|
||||
|
||||
TextColumn get taskId => text()();
|
||||
|
||||
IntColumn get modified => integer()();
|
||||
|
||||
TextColumn get objectJsonMap => text()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {taskId};
|
||||
}
|
||||
|
||||
class ResumeTasksEntity extends Table with DriftDefaultsMixin {
|
||||
const ResumeTasksEntity();
|
||||
|
||||
TextColumn get taskId => text()();
|
||||
|
||||
IntColumn get modified => integer()();
|
||||
|
||||
TextColumn get objectJsonMap => text()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {taskId};
|
||||
}
|
||||
2165
mobile/lib/infrastructure/entities/background_downloader.entity.drift.dart
generated
Normal file
2165
mobile/lib/infrastructure/entities/background_downloader.entity.drift.dart
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,195 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/background_downloader.entity.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'background_downloader.repository.drift.dart';
|
||||
|
||||
final driftPersistentStorage = DriftPersistentStorage(DriftBackgroundDownloader());
|
||||
|
||||
class _SqlPersistentStorageMigrator extends BasePersistentStorageMigrator {
|
||||
_SqlPersistentStorageMigrator();
|
||||
|
||||
@override
|
||||
Future<bool> migrateFrom(String persistentStorageName, PersistentStorage toStorage) =>
|
||||
switch (persistentStorageName.toLowerCase().replaceAll('_', '')) {
|
||||
'localstore' => migrateFromLocalStore(toStorage),
|
||||
_ => Future.value(false),
|
||||
};
|
||||
}
|
||||
|
||||
@DriftDatabase(tables: [TaskRecordEntity, PausedTasksEntity, ModifiedTasksEntity, ResumeTasksEntity])
|
||||
class DriftBackgroundDownloader extends $DriftBackgroundDownloader implements IDatabaseRepository {
|
||||
DriftBackgroundDownloader([QueryExecutor? executor])
|
||||
: super(
|
||||
executor ??
|
||||
driftDatabase(
|
||||
name: 'immich_background_downloader',
|
||||
native: const DriftNativeOptions(shareAcrossIsolates: true),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
onCreate: (migrator) async {
|
||||
await migrator.createAll();
|
||||
await _SqlPersistentStorageMigrator().migrateFromLocalStore(driftPersistentStorage);
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
await customStatement('PRAGMA synchronous = NORMAL');
|
||||
await customStatement('PRAGMA journal_mode = WAL');
|
||||
await customStatement('PRAGMA busy_timeout = 500');
|
||||
await driftPersistentStorage.purgeOldRecords();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class DriftPersistentStorage implements PersistentStorage {
|
||||
final log = Logger('DriftPersistentStorage');
|
||||
|
||||
late final DriftBackgroundDownloader db;
|
||||
|
||||
DriftPersistentStorage(this.db);
|
||||
|
||||
@override
|
||||
(String, int) get currentDatabaseVersion => ('Sqlite', 1);
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {}
|
||||
|
||||
Future<void> purgeOldRecords({Duration age = const Duration(days: 30)}) async {
|
||||
final cutOff = (DateTime.now().subtract(age).millisecondsSinceEpoch / 1000).floor();
|
||||
for (final TableInfo table in [db.modifiedTasksEntity, db.pausedTasksEntity, db.resumeTasksEntity]) {
|
||||
await db.customStatement('DELETE FROM ${table.actualTableName} WHERE modified < ?', [cutOff]);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _remove(TableInfo table, String? taskId) async {
|
||||
if (taskId == null) {
|
||||
await db.delete(table).go();
|
||||
} else {
|
||||
await db.customStatement('DELETE FROM ${table.actualTableName} WHERE task_id = ?', [taskId]);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removePausedTask(String? taskId) => _remove(db.pausedTasksEntity, taskId);
|
||||
|
||||
@override
|
||||
Future<void> removeResumeData(String? taskId) => _remove(db.resumeTasksEntity, taskId);
|
||||
|
||||
@override
|
||||
Future<void> removeTaskRecord(String? taskId) => _remove(db.taskRecordEntity, taskId);
|
||||
|
||||
@override
|
||||
Future<List<Task>> retrieveAllPausedTasks() async {
|
||||
final query = db.selectOnly(db.pausedTasksEntity)..addColumns([db.pausedTasksEntity.objectJsonMap]);
|
||||
final result = await query.map((row) => row.read(db.pausedTasksEntity.objectJsonMap)).get();
|
||||
return result.nonNulls.map((e) => Task.createFromJson(jsonDecode(e))).toList(growable: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ResumeData>> retrieveAllResumeData() async {
|
||||
final query = db.selectOnly(db.resumeTasksEntity)..addColumns([db.resumeTasksEntity.objectJsonMap]);
|
||||
final result = await query.map((row) => row.read(db.resumeTasksEntity.objectJsonMap)).get();
|
||||
return result.nonNulls.map((e) => ResumeData.fromJson(jsonDecode(e))).toList(growable: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TaskRecord>> retrieveAllTaskRecords() async {
|
||||
final query = db.selectOnly(db.taskRecordEntity)..addColumns([db.taskRecordEntity.objectJsonMap]);
|
||||
final result = await query.map((row) => row.read(db.taskRecordEntity.objectJsonMap)).get();
|
||||
return result.nonNulls.map((e) => TaskRecord.fromJson(jsonDecode(e))).toList(growable: false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Task?> retrievePausedTask(String taskId) async {
|
||||
final query = db.selectOnly(db.pausedTasksEntity)
|
||||
..addColumns([db.pausedTasksEntity.objectJsonMap])
|
||||
..where(db.pausedTasksEntity.taskId.equals(taskId));
|
||||
final result = await query.map((row) => row.read(db.pausedTasksEntity.objectJsonMap)).getSingleOrNull();
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return Task.createFromJson(jsonDecode(result));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ResumeData?> retrieveResumeData(String taskId) async {
|
||||
final query = db.selectOnly(db.resumeTasksEntity)
|
||||
..addColumns([db.resumeTasksEntity.objectJsonMap])
|
||||
..where(db.resumeTasksEntity.taskId.equals(taskId));
|
||||
final result = await query.map((row) => row.read(db.resumeTasksEntity.objectJsonMap)).getSingleOrNull();
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return ResumeData.fromJson(jsonDecode(result));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<TaskRecord?> retrieveTaskRecord(String taskId) async {
|
||||
final query = db.selectOnly(db.taskRecordEntity)
|
||||
..addColumns([db.taskRecordEntity.objectJsonMap])
|
||||
..where(db.taskRecordEntity.taskId.equals(taskId));
|
||||
final result = await query.map((row) => row.read(db.taskRecordEntity.objectJsonMap)).getSingleOrNull();
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return TaskRecord.fromJson(jsonDecode(result));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> storePausedTask(Task task) async {
|
||||
await db.managers.pausedTasksEntity.create(
|
||||
(o) => o(
|
||||
modified: (DateTime.now().millisecondsSinceEpoch / 1000).floor(),
|
||||
objectJsonMap: jsonEncode(task.toJson()),
|
||||
taskId: task.taskId,
|
||||
),
|
||||
mode: InsertMode.insertOrReplace,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> storeResumeData(ResumeData resumeData) async {
|
||||
await db.managers.resumeTasksEntity.create(
|
||||
(o) => o(
|
||||
modified: (DateTime.now().millisecondsSinceEpoch / 1000).floor(),
|
||||
objectJsonMap: jsonEncode(resumeData.toJson()),
|
||||
taskId: resumeData.taskId,
|
||||
),
|
||||
mode: InsertMode.insertOrReplace,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> storeTaskRecord(TaskRecord record) async {
|
||||
final task = record.task;
|
||||
await db.managers.taskRecordEntity.create(
|
||||
(o) => o(
|
||||
taskId: task.taskId,
|
||||
url: task.url,
|
||||
filename: task.filename,
|
||||
group: task.group,
|
||||
metaData: task.metaData,
|
||||
creationTime: (task.creationTime.millisecondsSinceEpoch / 1000).floor(),
|
||||
status: record.status.index,
|
||||
progress: record.progress,
|
||||
objectJsonMap: jsonEncode(record.toJson()),
|
||||
),
|
||||
mode: InsertMode.insertOrReplace,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<(String, int)> get storedDatabaseVersion async {
|
||||
return ('Sqlite', db.schemaVersion);
|
||||
}
|
||||
}
|
||||
45
mobile/lib/infrastructure/repositories/background_downloader.repository.drift.dart
generated
Normal file
45
mobile/lib/infrastructure/repositories/background_downloader.repository.drift.dart
generated
Normal file
@@ -0,0 +1,45 @@
|
||||
// dart format width=80
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart' as i0;
|
||||
import 'package:immich_mobile/infrastructure/entities/background_downloader.entity.drift.dart'
|
||||
as i1;
|
||||
|
||||
abstract class $DriftBackgroundDownloader extends i0.GeneratedDatabase {
|
||||
$DriftBackgroundDownloader(i0.QueryExecutor e) : super(e);
|
||||
$DriftBackgroundDownloaderManager get managers =>
|
||||
$DriftBackgroundDownloaderManager(this);
|
||||
late final i1.$TaskRecordEntityTable taskRecordEntity = i1
|
||||
.$TaskRecordEntityTable(this);
|
||||
late final i1.$PausedTasksEntityTable pausedTasksEntity = i1
|
||||
.$PausedTasksEntityTable(this);
|
||||
late final i1.$ModifiedTasksEntityTable modifiedTasksEntity = i1
|
||||
.$ModifiedTasksEntityTable(this);
|
||||
late final i1.$ResumeTasksEntityTable resumeTasksEntity = i1
|
||||
.$ResumeTasksEntityTable(this);
|
||||
@override
|
||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||
@override
|
||||
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [
|
||||
taskRecordEntity,
|
||||
pausedTasksEntity,
|
||||
modifiedTasksEntity,
|
||||
resumeTasksEntity,
|
||||
];
|
||||
@override
|
||||
i0.DriftDatabaseOptions get options =>
|
||||
const i0.DriftDatabaseOptions(storeDateTimeAsText: true);
|
||||
}
|
||||
|
||||
class $DriftBackgroundDownloaderManager {
|
||||
final $DriftBackgroundDownloader _db;
|
||||
$DriftBackgroundDownloaderManager(this._db);
|
||||
i1.$$TaskRecordEntityTableTableManager get taskRecordEntity =>
|
||||
i1.$$TaskRecordEntityTableTableManager(_db, _db.taskRecordEntity);
|
||||
i1.$$PausedTasksEntityTableTableManager get pausedTasksEntity =>
|
||||
i1.$$PausedTasksEntityTableTableManager(_db, _db.pausedTasksEntity);
|
||||
i1.$$ModifiedTasksEntityTableTableManager get modifiedTasksEntity =>
|
||||
i1.$$ModifiedTasksEntityTableTableManager(_db, _db.modifiedTasksEntity);
|
||||
i1.$$ResumeTasksEntityTableTableManager get resumeTasksEntity =>
|
||||
i1.$$ResumeTasksEntityTableTableManager(_db, _db.resumeTasksEntity);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/generated/codegen_loader.g.dart';
|
||||
import 'package:immich_mobile/generated/intl_keys.g.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/background_downloader.repository.dart';
|
||||
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
|
||||
import 'package:immich_mobile/providers/app_life_cycle.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
|
||||
@@ -103,7 +104,7 @@ Future<void> initApp() async {
|
||||
initializeTimeZones();
|
||||
|
||||
// Initialize the file downloader
|
||||
await FileDownloader().configure(
|
||||
await FileDownloader(persistentStorage: driftPersistentStorage).configure(
|
||||
// maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3
|
||||
|
||||
// On Android, if files are larger than 256MB, run in foreground service
|
||||
|
||||
@@ -7,8 +7,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:immich_mobile/utils/debug_print.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class UploadTaskWithFile {
|
||||
final File file;
|
||||
|
||||
Reference in New Issue
Block a user