diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index 8c95922a3a..03b6e6b6c8 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -15,3 +15,8 @@ const int kBatchHashSizeLimit = 1024 * 1024 * 1024; // 1GB // Secure storage keys const String kSecuredPinCode = "secured_pin_code"; + +// background_downloader task groups +const kManualUploadGroup = 'manual_upload_group'; +const kBackupGroup = 'backup_group'; +const kBackupLivePhotoGroup = 'backup_live_photo_group'; diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index 9de48a411a..2527bb27f1 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -5,7 +5,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/local_album.model.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -import 'package:platform/platform.dart'; import "package:immich_mobile/utils/database.utils.dart"; final backupRepositoryProvider = Provider( @@ -15,10 +14,7 @@ final backupRepositoryProvider = Provider( class DriftBackupRepository extends DriftDatabaseRepository implements IBackupRepository { final Drift _db; - final Platform _platform; - const DriftBackupRepository(this._db, {Platform? platform}) - : _platform = platform ?? const LocalPlatform(), - super(_db); + const DriftBackupRepository(this._db) : super(_db); @override Future> getAssets(String albumId) { diff --git a/mobile/lib/interfaces/upload.interface.dart b/mobile/lib/interfaces/upload.interface.dart index 4959b277e5..eec78d6a95 100644 --- a/mobile/lib/interfaces/upload.interface.dart +++ b/mobile/lib/interfaces/upload.interface.dart @@ -6,9 +6,9 @@ abstract interface class IUploadRepository { void enqueueAll(List tasks); Future cancel(String id); - Future cancelAll(); - Future pauseAll(); - Future deleteAllTrackingRecords(); + Future cancelAll(String group); + Future pauseAll(String group); + Future deleteAllTrackingRecords(String group); Future deleteRecordsWithIds(List id); Future> getRecords([TaskStatus? status]); } diff --git a/mobile/lib/pages/backup/exp_backup_controller.page.dart b/mobile/lib/pages/backup/exp_backup_controller.page.dart index 74d4f8df53..ea03a37468 100644 --- a/mobile/lib/pages/backup/exp_backup_controller.page.dart +++ b/mobile/lib/pages/backup/exp_backup_controller.page.dart @@ -1,6 +1,3 @@ -import 'dart:io'; -import 'dart:math'; - import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; diff --git a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart index f4eba6af88..541a5cc4b2 100644 --- a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart +++ b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart @@ -134,7 +134,7 @@ class ShareIntentUploadStateNotifier } Future upload(File file) { - return _uploadService.buildUploadTask(file); + return _uploadService.buildUploadTask(file, group: kManualUploadGroup); } Future cancelUpload(String id) { diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index 5c82ed27c8..a4f4fea45c 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -1,12 +1,10 @@ import 'dart:io'; -import 'package:background_downloader/background_downloader.dart'; import 'package:cancellation_token_http/http.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; 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/album.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; @@ -22,7 +20,6 @@ import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/success_upload_asset.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; -import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/backup/error_backup_list.provider.dart'; @@ -33,7 +30,6 @@ import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/services/backup.service.dart'; import 'package:immich_mobile/services/backup_album.service.dart'; import 'package:immich_mobile/services/server_info.service.dart'; -import 'package:immich_mobile/services/upload.service.dart'; import 'package:immich_mobile/utils/backup_progress.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:logging/logging.dart'; @@ -51,7 +47,6 @@ final backupProvider = ref.watch(albumMediaRepositoryProvider), ref.watch(fileMediaRepositoryProvider), ref.watch(backupAlbumServiceProvider), - ref.watch(uploadServiceProvider), ref, ); }); @@ -66,7 +61,6 @@ class BackupNotifier extends StateNotifier { this._albumMediaRepository, this._fileMediaRepository, this._backupAlbumService, - this._uploadService, this.ref, ) : super( BackUpState( @@ -106,10 +100,7 @@ class BackupNotifier extends StateNotifier { ), iCloudDownloadProgress: 0.0, ), - ) { - _uploadService.onUploadStatus = _uploadStatusCallback; - _uploadService.onTaskProgress = _taskProgressCallback; - } + ); final log = Logger('BackupNotifier'); final BackupService _backupService; @@ -120,7 +111,6 @@ class BackupNotifier extends StateNotifier { final IAlbumMediaRepository _albumMediaRepository; final IFileMediaRepository _fileMediaRepository; final BackupAlbumService _backupAlbumService; - final UploadService _uploadService; final Ref ref; /// @@ -498,7 +488,7 @@ class BackupNotifier extends StateNotifier { Future startBackupProcess() async { debugPrint("Start backup process"); assert(state.backupProgress == BackUpProgressEnum.idle); - // state = state.copyWith(backupProgress: BackUpProgressEnum.inProgress); + state = state.copyWith(backupProgress: BackUpProgressEnum.inProgress); await getBackupInfo(); @@ -532,89 +522,21 @@ class BackupNotifier extends StateNotifier { state = state.copyWith(iCloudDownloadProgress: progress); }); - // await _backupService.backupAsset( - // assetsWillBeBackup, - // state.cancelToken, - // pmProgressHandler: pmProgressHandler, - // onSuccess: _onAssetUploaded, - // onProgress: _onUploadProgress, - // onCurrentAsset: _onSetCurrentBackupAsset, - // onError: _onBackupError, - // ); - - await _backupService.uploadAssets(assetsWillBeBackup); + await _backupService.backupAsset( + assetsWillBeBackup, + state.cancelToken, + pmProgressHandler: pmProgressHandler, + onSuccess: _onAssetUploaded, + onProgress: _onUploadProgress, + onCurrentAsset: _onSetCurrentBackupAsset, + onError: _onBackupError, + ); await notifyBackgroundServiceCanRun(); } else { openAppSettings(); } } - void _updateUploadStatus(TaskStatusUpdate task, TaskStatus status) async { - if (status == TaskStatus.canceled) { - return; - } - - final taskId = task.task.taskId; - final uploadStatus = switch (task.status) { - TaskStatus.complete => UploadStatus.complete, - TaskStatus.failed => UploadStatus.failed, - TaskStatus.canceled => UploadStatus.canceled, - TaskStatus.enqueued => UploadStatus.enqueued, - TaskStatus.running => UploadStatus.running, - TaskStatus.paused => UploadStatus.paused, - TaskStatus.notFound => UploadStatus.notFound, - TaskStatus.waitingToRetry => UploadStatus.waitingtoRetry - }; - - // state = [ - // for (final attachment in state) - // if (attachment.id == taskId.toInt()) - // attachment.copyWith(status: uploadStatus) - // else - // attachment, - // ]; - } - - void _uploadStatusCallback(TaskStatusUpdate update) { - _updateUploadStatus(update, update.status); - - switch (update.status) { - case TaskStatus.complete: - // if (update.responseStatusCode == 200) { - // if (kDebugMode) { - // debugPrint("[COMPLETE] ${update.task.taskId} - DUPLICATE"); - // } - // } else { - // if (kDebugMode) { - // debugPrint("[COMPLETE] ${update.task.taskId}"); - // } - // } - break; - - default: - break; - } - } - - void _taskProgressCallback(TaskProgressUpdate update) { - // Ignore if the task is canceled or completed - if (update.progress == downloadFailed || - update.progress == downloadCompleted) { - return; - } - - // print("[_taskProgressCallback] $update"); - - final taskId = update.task.taskId; - // state = [ - // for (final attachment in state) - // if (attachment.id == taskId.toInt()) - // attachment.copyWith(uploadProgress: update.progress) - // else - // attachment, - // ]; - } - void setAvailableAlbums(availableAlbums) { state = state.copyWith( availableAlbums: availableAlbums, diff --git a/mobile/lib/providers/backup/exp_backup.provider.dart b/mobile/lib/providers/backup/exp_backup.provider.dart index 8495e4d1dd..01c105daa5 100644 --- a/mobile/lib/providers/backup/exp_backup.provider.dart +++ b/mobile/lib/providers/backup/exp_backup.provider.dart @@ -6,8 +6,6 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/utils/background_sync.dart'; -import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/services/exp_backup.service.dart'; import 'package:immich_mobile/services/upload.service.dart'; @@ -158,7 +156,6 @@ final expBackupProvider = return ExpBackupNotifier( ref.watch(expBackupServiceProvider), ref.watch(uploadServiceProvider), - ref.watch(backgroundSyncProvider), ); }); @@ -166,7 +163,6 @@ class ExpBackupNotifier extends StateNotifier { ExpBackupNotifier( this._backupService, this._uploadService, - this._backgroundSyncManager, ) : super( ExpBackupState( totalCount: 0, @@ -183,7 +179,6 @@ class ExpBackupNotifier extends StateNotifier { final ExpBackupService _backupService; final UploadService _uploadService; - final BackgroundSyncManager _backgroundSyncManager; StreamSubscription? _statusSubscription; StreamSubscription? _progressSubscription; diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index 32607c1a9c..6a3bf11fff 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -1,7 +1,7 @@ import 'package:background_downloader/background_downloader.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/interfaces/upload.interface.dart'; -import 'package:immich_mobile/utils/upload.dart'; final uploadRepositoryProvider = Provider((ref) => UploadRepository()); @@ -19,12 +19,12 @@ class UploadRepository implements IUploadRepository { taskQueue.maxConcurrent = 5; FileDownloader().addTaskQueue(taskQueue); FileDownloader().registerCallbacks( - group: kUploadGroup, + group: kBackupGroup, taskStatusCallback: (update) => onUploadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); FileDownloader().registerCallbacks( - group: kUploadLivePhotoGroup, + group: kBackupLivePhotoGroup, taskStatusCallback: (update) => onUploadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); @@ -36,8 +36,8 @@ class UploadRepository implements IUploadRepository { } @override - Future deleteAllTrackingRecords() { - return FileDownloader().database.deleteAllRecords(group: kUploadGroup); + Future deleteAllTrackingRecords(String group) { + return FileDownloader().database.deleteAllRecords(group: group); } @override @@ -46,14 +46,14 @@ class UploadRepository implements IUploadRepository { } @override - Future cancelAll() { - taskQueue.removeTasksWithGroup(kUploadGroup); - return FileDownloader().cancelAll(group: kUploadGroup); + Future cancelAll(String group) { + taskQueue.removeTasksWithGroup(group); + return FileDownloader().cancelAll(group: group); } @override - Future pauseAll() { - return FileDownloader().pauseAll(group: kUploadGroup); + Future pauseAll(String group) { + return FileDownloader().pauseAll(group: group); } @override @@ -64,7 +64,7 @@ class UploadRepository implements IUploadRepository { @override Future> getRecords([TaskStatus? status]) { if (status == null) { - return FileDownloader().database.allRecords(group: kUploadGroup); + return FileDownloader().database.allRecords(group: kBackupGroup); } return FileDownloader().database.allRecordsWithStatus(status); diff --git a/mobile/lib/services/backup.service.dart b/mobile/lib/services/backup.service.dart index 4da6bd7489..596ad8dc2e 100644 --- a/mobile/lib/services/backup.service.dart +++ b/mobile/lib/services/backup.service.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:background_downloader/background_downloader.dart'; import 'package:cancellation_token_http/http.dart' as http; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -29,7 +28,6 @@ import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/services/upload.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; import 'package:path/path.dart' as p; @@ -45,7 +43,6 @@ final backupServiceProvider = Provider( ref.watch(fileMediaRepositoryProvider), ref.watch(assetRepositoryProvider), ref.watch(assetMediaRepositoryProvider), - ref.watch(uploadServiceProvider), ), ); @@ -59,7 +56,6 @@ class BackupService { final IFileMediaRepository _fileMediaRepository; final IAssetRepository _assetRepository; final IAssetMediaRepository _assetMediaRepository; - final UploadService _uploadService; BackupService( this._apiService, @@ -69,7 +65,6 @@ class BackupService { this._fileMediaRepository, this._assetRepository, this._assetMediaRepository, - this._uploadService, ); Future?> getDeviceBackupAsset() async { @@ -254,52 +249,6 @@ class BackupService { ); } - uploadAssets( - Iterable assets, - ) async { - final hasPermission = await _checkPermissions(); - if (!hasPermission) { - return false; - } - - List candidates = assets.toList(); - List uploadTasks = []; - for (final candidate in candidates) { - final Asset asset = candidate.asset; - File? file; - File? livePhotoFile; - file = await asset.local!.originFile.timeout(const Duration(seconds: 5)); - - if (asset.local!.isLivePhoto) { - livePhotoFile = await asset.local!.originFileWithSubtype - .timeout(const Duration(seconds: 5)); - } - - if (file != null) { - String? originalFileName = - await _assetMediaRepository.getOriginalFilename(asset.localId!) ?? - asset.fileName; - - final task = await _uploadService.buildUploadTask( - file, - originalFileName: originalFileName, - deviceAssetId: asset.localId, - ); - - uploadTasks.add(task); - } - } - - if (uploadTasks.isEmpty) { - debugPrint("No assets to upload"); - return false; - } - - print("Uploading ${uploadTasks.length} assets"); - - _uploadService.enqueueTasks(uploadTasks); - } - Future backupAsset( Iterable assets, http.CancellationToken cancelToken, { diff --git a/mobile/lib/services/exp_backup.service.dart b/mobile/lib/services/exp_backup.service.dart index 2ca08f25a8..393de746e1 100644 --- a/mobile/lib/services/exp_backup.service.dart +++ b/mobile/lib/services/exp_backup.service.dart @@ -5,9 +5,9 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -import 'package:immich_mobile/utils/upload.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as p; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -172,6 +172,7 @@ class ExpBackupService { originalFileName: originalFileName, deviceAssetId: asset.id, metadata: metadata, + group: kBackupGroup, ); } @@ -198,13 +199,13 @@ class ExpBackupService { originalFileName: asset.name, deviceAssetId: asset.id, fields: fields, - group: kUploadLivePhotoGroup, + group: kBackupLivePhotoGroup, ); } Future cancel() async { shouldCancel = true; - await _uploadService.cancel(); + await _uploadService.cancelAllForGroup(kBackupGroup); } } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index 38592265d7..5476036ba2 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -3,12 +3,12 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; 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:immich_mobile/interfaces/upload.interface.dart'; import 'package:immich_mobile/repositories/upload.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/upload.dart'; import 'package:path/path.dart'; final uploadServiceProvider = Provider( @@ -61,13 +61,13 @@ class UploadService { return FileDownloader().cancelTaskWithId(id); } - Future cancel() async { - await _uploadRepository.cancelAll(); - await _uploadRepository.deleteAllTrackingRecords(); + Future cancelAllForGroup(String group) async { + await _uploadRepository.cancelAll(group); + await _uploadRepository.deleteAllTrackingRecords(group); } - Future pause() { - return _uploadRepository.pauseAll(); + Future pauseAll() { + return _uploadRepository.pauseAll(kBackupGroup); } Future> getRecords() async { @@ -81,11 +81,11 @@ class UploadService { Future buildUploadTask( File file, { + required String group, Map? fields, String? originalFileName, String? deviceAssetId, String? metadata, - String group = kUploadGroup, }) async { return _buildTask( deviceAssetId ?? hash(file.path).toString(), @@ -100,10 +100,10 @@ class UploadService { Future _buildTask( String id, File file, { + required String group, Map? fields, String? originalFileName, String? metadata, - String group = kUploadGroup, }) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); diff --git a/mobile/lib/utils/upload.dart b/mobile/lib/utils/upload.dart deleted file mode 100644 index 49062b74d2..0000000000 --- a/mobile/lib/utils/upload.dart +++ /dev/null @@ -1,2 +0,0 @@ -const kUploadGroup = 'upload_group'; -const kUploadLivePhotoGroup = 'upload_live_photo_group'; diff --git a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart index 3bd715c0f2..f254522ecb 100644 --- a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart +++ b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart @@ -1,15 +1,7 @@ -import 'dart:io'; - -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/backup/exp_backup.provider.dart'; -import 'package:immich_mobile/widgets/backup/asset_info_table.dart'; -import 'package:immich_mobile/widgets/backup/error_chip.dart'; -import 'package:immich_mobile/widgets/backup/icloud_download_progress_bar.dart'; -import 'package:immich_mobile/widgets/backup/upload_progress_bar.dart'; -import 'package:immich_mobile/widgets/backup/upload_stats.dart'; class CurrentUploadingAssetInfoBox extends ConsumerWidget { const CurrentUploadingAssetInfoBox({super.key});