mirror of
https://github.com/immich-app/immich.git
synced 2025-01-21 00:52:43 -05:00
success upload
This commit is contained in:
parent
fbe2fc52d9
commit
5619955eb7
5 changed files with 107 additions and 174 deletions
|
@ -1,7 +1,11 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
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:photo_manager/photo_manager.dart';
|
||||
|
||||
import 'package:immich_mobile/models/backup/available_album.model.dart';
|
||||
|
@ -18,178 +22,38 @@ enum BackUpProgressEnum {
|
|||
|
||||
class BackUpState {
|
||||
// enum
|
||||
final BackUpProgressEnum backupProgress;
|
||||
final List<String> allAssetsInDatabase;
|
||||
final double progressInPercentage;
|
||||
final String progressInFileSize;
|
||||
final double progressInFileSpeed;
|
||||
final List<double> progressInFileSpeeds;
|
||||
final DateTime progressInFileSpeedUpdateTime;
|
||||
final int progressInFileSpeedUpdateSentBytes;
|
||||
final double iCloudDownloadProgress;
|
||||
final CancellationToken cancelToken;
|
||||
final ServerDiskInfo serverInfo;
|
||||
final bool autoBackup;
|
||||
final bool backgroundBackup;
|
||||
final bool backupRequireWifi;
|
||||
final bool backupRequireCharging;
|
||||
final int backupTriggerDelay;
|
||||
final BackUpProgressEnum progress;
|
||||
|
||||
/// All available albums on the device
|
||||
final List<AvailableAlbum> availableAlbums;
|
||||
final Set<AvailableAlbum> selectedBackupAlbums;
|
||||
final Set<AvailableAlbum> excludedBackupAlbums;
|
||||
final List<UploadTask> uploadTasks;
|
||||
|
||||
/// Assets that are not overlapping in selected backup albums and excluded backup albums
|
||||
final Set<AssetEntity> allUniqueAssets;
|
||||
|
||||
/// All assets from the selected albums that have been backup
|
||||
final Set<String> selectedAlbumsBackupAssetsIds;
|
||||
|
||||
// Current Backup Asset
|
||||
final CurrentUploadAsset currentUploadAsset;
|
||||
|
||||
const BackUpState({
|
||||
required this.backupProgress,
|
||||
required this.allAssetsInDatabase,
|
||||
required this.progressInPercentage,
|
||||
required this.progressInFileSize,
|
||||
required this.progressInFileSpeed,
|
||||
required this.progressInFileSpeeds,
|
||||
required this.progressInFileSpeedUpdateTime,
|
||||
required this.progressInFileSpeedUpdateSentBytes,
|
||||
required this.iCloudDownloadProgress,
|
||||
required this.cancelToken,
|
||||
required this.serverInfo,
|
||||
required this.autoBackup,
|
||||
required this.backgroundBackup,
|
||||
required this.backupRequireWifi,
|
||||
required this.backupRequireCharging,
|
||||
required this.backupTriggerDelay,
|
||||
required this.availableAlbums,
|
||||
required this.selectedBackupAlbums,
|
||||
required this.excludedBackupAlbums,
|
||||
required this.allUniqueAssets,
|
||||
required this.selectedAlbumsBackupAssetsIds,
|
||||
required this.currentUploadAsset,
|
||||
BackUpState({
|
||||
required this.progress,
|
||||
required this.uploadTasks,
|
||||
});
|
||||
|
||||
BackUpState copyWith({
|
||||
BackUpProgressEnum? backupProgress,
|
||||
List<String>? allAssetsInDatabase,
|
||||
double? progressInPercentage,
|
||||
String? progressInFileSize,
|
||||
double? progressInFileSpeed,
|
||||
List<double>? progressInFileSpeeds,
|
||||
DateTime? progressInFileSpeedUpdateTime,
|
||||
int? progressInFileSpeedUpdateSentBytes,
|
||||
double? iCloudDownloadProgress,
|
||||
CancellationToken? cancelToken,
|
||||
ServerDiskInfo? serverInfo,
|
||||
bool? autoBackup,
|
||||
bool? backgroundBackup,
|
||||
bool? backupRequireWifi,
|
||||
bool? backupRequireCharging,
|
||||
int? backupTriggerDelay,
|
||||
List<AvailableAlbum>? availableAlbums,
|
||||
Set<AvailableAlbum>? selectedBackupAlbums,
|
||||
Set<AvailableAlbum>? excludedBackupAlbums,
|
||||
Set<AssetEntity>? allUniqueAssets,
|
||||
Set<String>? selectedAlbumsBackupAssetsIds,
|
||||
CurrentUploadAsset? currentUploadAsset,
|
||||
BackUpProgressEnum? progress,
|
||||
List<UploadTask>? uploadTasks,
|
||||
}) {
|
||||
return BackUpState(
|
||||
backupProgress: backupProgress ?? this.backupProgress,
|
||||
allAssetsInDatabase: allAssetsInDatabase ?? this.allAssetsInDatabase,
|
||||
progressInPercentage: progressInPercentage ?? this.progressInPercentage,
|
||||
progressInFileSize: progressInFileSize ?? this.progressInFileSize,
|
||||
progressInFileSpeed: progressInFileSpeed ?? this.progressInFileSpeed,
|
||||
progressInFileSpeeds: progressInFileSpeeds ?? this.progressInFileSpeeds,
|
||||
progressInFileSpeedUpdateTime:
|
||||
progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime,
|
||||
progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ??
|
||||
this.progressInFileSpeedUpdateSentBytes,
|
||||
iCloudDownloadProgress:
|
||||
iCloudDownloadProgress ?? this.iCloudDownloadProgress,
|
||||
cancelToken: cancelToken ?? this.cancelToken,
|
||||
serverInfo: serverInfo ?? this.serverInfo,
|
||||
autoBackup: autoBackup ?? this.autoBackup,
|
||||
backgroundBackup: backgroundBackup ?? this.backgroundBackup,
|
||||
backupRequireWifi: backupRequireWifi ?? this.backupRequireWifi,
|
||||
backupRequireCharging:
|
||||
backupRequireCharging ?? this.backupRequireCharging,
|
||||
backupTriggerDelay: backupTriggerDelay ?? this.backupTriggerDelay,
|
||||
availableAlbums: availableAlbums ?? this.availableAlbums,
|
||||
selectedBackupAlbums: selectedBackupAlbums ?? this.selectedBackupAlbums,
|
||||
excludedBackupAlbums: excludedBackupAlbums ?? this.excludedBackupAlbums,
|
||||
allUniqueAssets: allUniqueAssets ?? this.allUniqueAssets,
|
||||
selectedAlbumsBackupAssetsIds:
|
||||
selectedAlbumsBackupAssetsIds ?? this.selectedAlbumsBackupAssetsIds,
|
||||
currentUploadAsset: currentUploadAsset ?? this.currentUploadAsset,
|
||||
progress: progress ?? this.progress,
|
||||
uploadTasks: uploadTasks ?? this.uploadTasks,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BackUpState(backupProgress: $backupProgress, allAssetsInDatabase: $allAssetsInDatabase, progressInPercentage: $progressInPercentage, progressInFileSize: $progressInFileSize, progressInFileSpeed: $progressInFileSpeed, progressInFileSpeeds: $progressInFileSpeeds, progressInFileSpeedUpdateTime: $progressInFileSpeedUpdateTime, progressInFileSpeedUpdateSentBytes: $progressInFileSpeedUpdateSentBytes, iCloudDownloadProgress: $iCloudDownloadProgress, cancelToken: $cancelToken, serverInfo: $serverInfo, autoBackup: $autoBackup, backgroundBackup: $backgroundBackup, backupRequireWifi: $backupRequireWifi, backupRequireCharging: $backupRequireCharging, backupTriggerDelay: $backupTriggerDelay, availableAlbums: $availableAlbums, selectedBackupAlbums: $selectedBackupAlbums, excludedBackupAlbums: $excludedBackupAlbums, allUniqueAssets: $allUniqueAssets, selectedAlbumsBackupAssetsIds: $selectedAlbumsBackupAssetsIds, currentUploadAsset: $currentUploadAsset)';
|
||||
}
|
||||
String toString() =>
|
||||
'BackUpState(progress: $progress, uploadTasks: $uploadTasks)';
|
||||
|
||||
@override
|
||||
bool operator ==(covariant BackUpState other) {
|
||||
if (identical(this, other)) return true;
|
||||
final collectionEquals = const DeepCollectionEquality().equals;
|
||||
final listEquals = const DeepCollectionEquality().equals;
|
||||
|
||||
return other.backupProgress == backupProgress &&
|
||||
collectionEquals(other.allAssetsInDatabase, allAssetsInDatabase) &&
|
||||
other.progressInPercentage == progressInPercentage &&
|
||||
other.progressInFileSize == progressInFileSize &&
|
||||
other.progressInFileSpeed == progressInFileSpeed &&
|
||||
collectionEquals(other.progressInFileSpeeds, progressInFileSpeeds) &&
|
||||
other.progressInFileSpeedUpdateTime == progressInFileSpeedUpdateTime &&
|
||||
other.progressInFileSpeedUpdateSentBytes ==
|
||||
progressInFileSpeedUpdateSentBytes &&
|
||||
other.iCloudDownloadProgress == iCloudDownloadProgress &&
|
||||
other.cancelToken == cancelToken &&
|
||||
other.serverInfo == serverInfo &&
|
||||
other.autoBackup == autoBackup &&
|
||||
other.backgroundBackup == backgroundBackup &&
|
||||
other.backupRequireWifi == backupRequireWifi &&
|
||||
other.backupRequireCharging == backupRequireCharging &&
|
||||
other.backupTriggerDelay == backupTriggerDelay &&
|
||||
collectionEquals(other.availableAlbums, availableAlbums) &&
|
||||
collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) &&
|
||||
collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) &&
|
||||
collectionEquals(other.allUniqueAssets, allUniqueAssets) &&
|
||||
collectionEquals(
|
||||
other.selectedAlbumsBackupAssetsIds,
|
||||
selectedAlbumsBackupAssetsIds,
|
||||
) &&
|
||||
other.currentUploadAsset == currentUploadAsset;
|
||||
return other.progress == progress &&
|
||||
listEquals(other.uploadTasks, uploadTasks);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return backupProgress.hashCode ^
|
||||
allAssetsInDatabase.hashCode ^
|
||||
progressInPercentage.hashCode ^
|
||||
progressInFileSize.hashCode ^
|
||||
progressInFileSpeed.hashCode ^
|
||||
progressInFileSpeeds.hashCode ^
|
||||
progressInFileSpeedUpdateTime.hashCode ^
|
||||
progressInFileSpeedUpdateSentBytes.hashCode ^
|
||||
iCloudDownloadProgress.hashCode ^
|
||||
cancelToken.hashCode ^
|
||||
serverInfo.hashCode ^
|
||||
autoBackup.hashCode ^
|
||||
backgroundBackup.hashCode ^
|
||||
backupRequireWifi.hashCode ^
|
||||
backupRequireCharging.hashCode ^
|
||||
backupTriggerDelay.hashCode ^
|
||||
availableAlbums.hashCode ^
|
||||
selectedBackupAlbums.hashCode ^
|
||||
excludedBackupAlbums.hashCode ^
|
||||
allUniqueAssets.hashCode ^
|
||||
selectedAlbumsBackupAssetsIds.hashCode ^
|
||||
currentUploadAsset.hashCode;
|
||||
}
|
||||
int get hashCode => progress.hashCode ^ uploadTasks.hashCode;
|
||||
}
|
||||
|
|
|
@ -15,21 +15,31 @@ class BackupPage extends StatefulHookConsumerWidget {
|
|||
class _BackupPageState extends ConsumerState<BackupPage> {
|
||||
@override
|
||||
void initState() {
|
||||
ref.read(backupNotifierProvider.notifier).backup();
|
||||
ref.read(backupNotifierProvider.notifier).getBackupCandidates();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final provider = ref.watch(backupNotifierProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Backup"),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.play_circle_outline_rounded),
|
||||
onPressed: () {
|
||||
ref.read(backupNotifierProvider.notifier).startBackup();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: const Center(
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text("Test"),
|
||||
Text("Test ${provider.uploadTasks.length}"),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,25 +1,35 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
||||
|
||||
import 'package:immich_mobile/services/backup.service.dart';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
class BackupNotifier extends StateNotifier<bool> {
|
||||
class BackupNotifier extends StateNotifier<BackUpState> {
|
||||
BackupNotifier(
|
||||
this._backupService,
|
||||
) : super(
|
||||
true,
|
||||
BackUpState(
|
||||
progress: BackUpProgressEnum.idle,
|
||||
uploadTasks: [],
|
||||
),
|
||||
);
|
||||
|
||||
final log = Logger('BackupNotifier');
|
||||
final BackupService _backupService;
|
||||
|
||||
Future<void> backup() async {
|
||||
_backupService.buildBackupCandidates();
|
||||
Future<void> getBackupCandidates() async {
|
||||
state = state.copyWith(
|
||||
uploadTasks: await _backupService.getBackupCandidates(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> startBackup() async {
|
||||
await _backupService.startBackup(state.uploadTasks);
|
||||
}
|
||||
}
|
||||
|
||||
final backupNotifierProvider =
|
||||
StateNotifierProvider<BackupNotifier, bool>((ref) {
|
||||
StateNotifierProvider<BackupNotifier, BackUpState>((ref) {
|
||||
return BackupNotifier(ref.watch(backupServiceProvider));
|
||||
});
|
||||
|
|
|
@ -30,9 +30,23 @@ class BackupService {
|
|||
final _fileDownloader = FileDownloader();
|
||||
final ApiService _apiService;
|
||||
|
||||
BackupService(this._apiService);
|
||||
BackupService(this._apiService) {
|
||||
debugPrint("BackupService created");
|
||||
// final subscription = FileDownloader().updates.listen((update) {
|
||||
// switch (update) {
|
||||
// case TaskStatusUpdate():
|
||||
// print(
|
||||
// 'Status update for ${update.task} with status ${update.status}',
|
||||
// );
|
||||
// case TaskProgressUpdate():
|
||||
// print(
|
||||
// 'Progress update for ${update.task} with progress ${update.progress}',
|
||||
// );
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
buildBackupCandidates() async {
|
||||
Future<List<UploadTask>> getBackupCandidates() async {
|
||||
// Get assets on devices
|
||||
final albums = await PhotoManager.getAssetPathList(hasAll: true);
|
||||
List<AssetEntity> assetsOnDevice = await _getAssetsOnDevice(albums);
|
||||
|
@ -45,13 +59,15 @@ class BackupService {
|
|||
// Get assets not on server
|
||||
if (assetsOnServer != null && assetsOnServer.isNotEmpty) {
|
||||
assetsOnDevice.removeWhere(
|
||||
(asset) => !assetsOnServer.contains(asset.id),
|
||||
(asset) => assetsOnServer.contains(asset.id),
|
||||
);
|
||||
}
|
||||
|
||||
// Remvove duplicate
|
||||
assetsOnDevice = assetsOnDevice.toSet().toList();
|
||||
|
||||
// Build UploadTask
|
||||
final uploadTasks = await _constructUploadTask(assetsOnDevice);
|
||||
print(uploadTasks);
|
||||
return await _constructUploadTask(assetsOnDevice);
|
||||
}
|
||||
|
||||
Future<List<UploadTask>> _constructUploadTask(
|
||||
|
@ -61,7 +77,7 @@ class BackupService {
|
|||
|
||||
final String savedEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||
final String deviceId = Store.get(StoreKey.deviceId);
|
||||
final url = '$savedEndpoint/asset/upload';
|
||||
final url = '$savedEndpoint/assets';
|
||||
final headers = {
|
||||
'x-immich-user-token': Store.get(StoreKey.accessToken),
|
||||
'Transfer-Encoding': 'chunked',
|
||||
|
@ -69,8 +85,17 @@ class BackupService {
|
|||
|
||||
for (final entity in assets) {
|
||||
final file = await entity.originFile;
|
||||
final fileName = await entity.titleAsync;
|
||||
final (baseDirectory, directory, _) = await Task.split(file: file);
|
||||
if (file == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final originalFileName = await entity.titleAsync;
|
||||
|
||||
// final filePath = file.path;
|
||||
// final split = filePath.split('/');
|
||||
// final filename = split.last;
|
||||
// final directory = split.take(split.length - 1).join('/');
|
||||
final (baseDirectory, directory, filename) = await Task.split(file: file);
|
||||
|
||||
final fields = {
|
||||
'deviceAssetId': entity.id,
|
||||
|
@ -79,13 +104,14 @@ class BackupService {
|
|||
'fileModifiedAt': entity.modifiedDateTime.toUtc().toIso8601String(),
|
||||
'isFavorite': entity.isFavorite.toString(),
|
||||
'duration': entity.videoDuration.toString(),
|
||||
'originalFileName': originalFileName,
|
||||
};
|
||||
|
||||
final task = UploadTask(
|
||||
url: url,
|
||||
baseDirectory: baseDirectory,
|
||||
directory: directory,
|
||||
filename: fileName,
|
||||
filename: filename,
|
||||
group: 'backup',
|
||||
fileField: 'assetData',
|
||||
taskId: entity.id,
|
||||
|
@ -125,4 +151,27 @@ class BackupService {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<void> startBackup(List<UploadTask> tasks) async {
|
||||
try {
|
||||
debugPrint("Start backup ${tasks.length} tasks");
|
||||
|
||||
for (final task in tasks) {
|
||||
final result = await _fileDownloader.upload(
|
||||
task,
|
||||
onProgress: (percent) => print('${percent * 100} done'),
|
||||
onStatus: (status) => print('Status for ${task.taskId} is $status)'),
|
||||
onElapsedTime: (t) => print('time is $t'),
|
||||
elapsedTimeInterval: const Duration(seconds: 1),
|
||||
);
|
||||
print("--------------------");
|
||||
print('Result Status Code ${result.responseStatusCode}');
|
||||
print('$result is done with ${result.status}');
|
||||
print('result ${result.responseBody}');
|
||||
print('result ${result.responseHeaders}');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Error uploading tasks: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -341,10 +341,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55"
|
||||
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.3.6"
|
||||
dartx:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
Loading…
Add table
Reference in a new issue