mirror of
https://github.com/immich-app/immich.git
synced 2026-05-15 03:52:13 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 694de3c9c0 | |||
| cb3bccf169 |
@@ -276,7 +276,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
|
||||
private const val NOTIFICATION_CHANNEL_ERROR_ID = "immich/backgroundServiceError"
|
||||
private const val NOTIFICATION_DEFAULT_TITLE = "Immich"
|
||||
private const val NOTIFICATION_ID = 1
|
||||
private const val NOTIFICATION_ERROR_ID = 2
|
||||
private const val NOTIFICATION_ERROR_ID = 2
|
||||
private const val NOTIFICATION_DETAIL_ID = 3
|
||||
private const val ONE_MINUTE = 60000L
|
||||
|
||||
@@ -304,12 +304,12 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
|
||||
val workInfoList = workInfoFuture.get(1000, TimeUnit.MILLISECONDS)
|
||||
if (workInfoList != null) {
|
||||
for (workInfo in workInfoList) {
|
||||
if (workInfo.getState() == WorkInfo.State.ENQUEUED) {
|
||||
//if (workInfo.getState() == WorkInfo.State.ENQUEUED) {
|
||||
val workRequest = buildWorkRequest(requireWifi, requireCharging)
|
||||
wm.enqueueUniqueWork(TASK_NAME_BACKUP, ExistingWorkPolicy.REPLACE, workRequest)
|
||||
Log.d(TAG, "updateBackupWorker updated BackupWorker constraints")
|
||||
return
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "updateBackupWorker: BackupWorker not enqueued")
|
||||
@@ -346,7 +346,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
|
||||
.setRequiresBatteryNotLow(true)
|
||||
.setRequiresCharging(requireCharging)
|
||||
.build();
|
||||
|
||||
|
||||
val work = OneTimeWorkRequest.Builder(BackupWorker::class.java)
|
||||
.setConstraints(constraints)
|
||||
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, ONE_MINUTE, TimeUnit.MILLISECONDS)
|
||||
@@ -359,4 +359,4 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
|
||||
}
|
||||
}
|
||||
|
||||
private const val TAG = "BackupWorker"
|
||||
private const val TAG = "BackupWorker"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.8.20'
|
||||
ext.kotlin_version = '1.9.21'
|
||||
ext.kotlin_coroutines_version = '1.7.1'
|
||||
ext.work_version = '2.7.1'
|
||||
ext.concurrent_version = '1.1.0'
|
||||
|
||||
Binary file not shown.
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
@@ -33,6 +34,7 @@ final backupServiceProvider = Provider(
|
||||
|
||||
class BackupService {
|
||||
final httpClient = http.Client();
|
||||
final _fileDownloader = FileDownloader();
|
||||
final ApiService _apiService;
|
||||
final Isar _db;
|
||||
final Logger _log = Logger("BackupService");
|
||||
@@ -242,117 +244,146 @@ class BackupService {
|
||||
)
|
||||
: assetList.toList();
|
||||
|
||||
final tasks = <UploadTask>[];
|
||||
for (var entity in assetsToUpload) {
|
||||
File? file;
|
||||
File? livePhotoFile;
|
||||
final isAvailableLocally =
|
||||
await entity.isLocallyAvailable(isOrigin: true);
|
||||
|
||||
try {
|
||||
final isAvailableLocally =
|
||||
await entity.isLocallyAvailable(isOrigin: true);
|
||||
|
||||
// Handle getting files from iCloud
|
||||
if (!isAvailableLocally && Platform.isIOS) {
|
||||
// Skip iCloud assets if the user has disabled this feature
|
||||
if (isIgnoreIcloudAssets) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setCurrentUploadAssetCb(
|
||||
CurrentUploadAsset(
|
||||
id: entity.id,
|
||||
fileCreatedAt: entity.createDateTime.year == 1970
|
||||
? entity.modifiedDateTime
|
||||
: entity.createDateTime,
|
||||
fileName: await entity.titleAsync,
|
||||
fileType: _getAssetType(entity.type),
|
||||
iCloudAsset: true,
|
||||
),
|
||||
);
|
||||
|
||||
file = await entity.loadFile(progressHandler: pmProgressHandler);
|
||||
livePhotoFile = await entity.loadFile(
|
||||
withSubtype: true,
|
||||
progressHandler: pmProgressHandler,
|
||||
);
|
||||
} else {
|
||||
if (entity.type == AssetType.video) {
|
||||
file = await entity.originFile;
|
||||
} else {
|
||||
file = await entity.originFile.timeout(const Duration(seconds: 5));
|
||||
if (entity.isLivePhoto) {
|
||||
livePhotoFile = await entity.originFileWithSubtype
|
||||
.timeout(const Duration(seconds: 5));
|
||||
}
|
||||
}
|
||||
// Handle getting files from iCloud
|
||||
if (!isAvailableLocally && Platform.isIOS) {
|
||||
// Skip iCloud assets if the user has disabled this feature
|
||||
if (isIgnoreIcloudAssets) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file != null) {
|
||||
String originalFileName = await entity.titleAsync;
|
||||
var fileStream = file.openRead();
|
||||
var assetRawUploadData = http.MultipartFile(
|
||||
"assetData",
|
||||
fileStream,
|
||||
file.lengthSync(),
|
||||
filename: originalFileName,
|
||||
);
|
||||
setCurrentUploadAssetCb(
|
||||
CurrentUploadAsset(
|
||||
id: entity.id,
|
||||
fileCreatedAt: entity.createDateTime.year == 1970
|
||||
? entity.modifiedDateTime
|
||||
: entity.createDateTime,
|
||||
fileName: await entity.titleAsync,
|
||||
fileType: _getAssetType(entity.type),
|
||||
iCloudAsset: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
var req = MultipartRequest(
|
||||
'POST',
|
||||
Uri.parse('$savedEndpoint/asset/upload'),
|
||||
onProgress: ((bytes, totalBytes) =>
|
||||
uploadProgressCb(bytes, totalBytes)),
|
||||
);
|
||||
req.headers["x-immich-user-token"] = Store.get(StoreKey.accessToken);
|
||||
req.headers["Transfer-Encoding"] = "chunked";
|
||||
final files = [];
|
||||
// TODO: This is silly to have to load the file just to access the path
|
||||
// But there doesn't seem to be any other way to do it
|
||||
final fileName = (await entity.originFile)?.path;
|
||||
files.add(fileName);
|
||||
|
||||
req.fields['deviceAssetId'] = entity.id;
|
||||
req.fields['deviceId'] = deviceId;
|
||||
req.fields['fileCreatedAt'] =
|
||||
entity.createDateTime.toUtc().toIso8601String();
|
||||
req.fields['fileModifiedAt'] =
|
||||
entity.modifiedDateTime.toUtc().toIso8601String();
|
||||
req.fields['isFavorite'] = entity.isFavorite.toString();
|
||||
req.fields['duration'] = entity.videoDuration.toString();
|
||||
if (entity.isLivePhoto) {
|
||||
final livePhotoFileName = (await entity.originFileWithSubtype)?.path;
|
||||
if (livePhotoFileName != null) {
|
||||
files.add(livePhotoFileName);
|
||||
}
|
||||
}
|
||||
|
||||
req.files.add(assetRawUploadData);
|
||||
final url = '$savedEndpoint/asset/upload';
|
||||
final headers = {
|
||||
'x-immich-user-token': Store.get(StoreKey.accessToken),
|
||||
'Transfer-Encoding': 'chunked',
|
||||
};
|
||||
|
||||
if (entity.isLivePhoto) {
|
||||
if (livePhotoFile != null) {
|
||||
final livePhotoTitle = p.setExtension(
|
||||
originalFileName,
|
||||
p.extension(livePhotoFile.path),
|
||||
);
|
||||
final fileStream = livePhotoFile.openRead();
|
||||
final livePhotoRawUploadData = http.MultipartFile(
|
||||
"livePhotoData",
|
||||
fileStream,
|
||||
livePhotoFile.lengthSync(),
|
||||
filename: livePhotoTitle,
|
||||
);
|
||||
req.files.add(livePhotoRawUploadData);
|
||||
} else {
|
||||
_log.warning(
|
||||
"Failed to obtain motion part of the livePhoto - $originalFileName",
|
||||
);
|
||||
}
|
||||
}
|
||||
final fields = {
|
||||
'deviceAssetId': entity.id,
|
||||
'deviceId': deviceId,
|
||||
'fileCreatedAt': entity.createDateTime.toUtc().toIso8601String(),
|
||||
'fileModifiedAt': entity.modifiedDateTime.toUtc().toIso8601String(),
|
||||
'isFavorite': entity.isFavorite.toString(),
|
||||
'duration': entity.videoDuration.toString(),
|
||||
};
|
||||
|
||||
setCurrentUploadAssetCb(
|
||||
CurrentUploadAsset(
|
||||
id: entity.id,
|
||||
fileCreatedAt: entity.createDateTime.year == 1970
|
||||
? entity.modifiedDateTime
|
||||
: entity.createDateTime,
|
||||
fileName: originalFileName,
|
||||
fileType: _getAssetType(entity.type),
|
||||
iCloudAsset: false,
|
||||
),
|
||||
);
|
||||
if (files.length == 1) {
|
||||
final String file = files.first;
|
||||
final split = file.split('/');
|
||||
final name = split.last;
|
||||
final directory = split.take(split.length - 1).join('/');
|
||||
|
||||
var response =
|
||||
await httpClient.send(req, cancellationToken: cancelToken);
|
||||
final task = UploadTask(
|
||||
url: url,
|
||||
group: 'backup',
|
||||
fileField: 'assetData',
|
||||
taskId: entity.id,
|
||||
fields: fields,
|
||||
headers: headers,
|
||||
updates: Updates.statusAndProgress,
|
||||
retries: 0,
|
||||
httpRequestMethod: 'POST',
|
||||
displayName: 'Immich',
|
||||
filename: name,
|
||||
directory: directory,
|
||||
baseDirectory: BaseDirectory.root,
|
||||
);
|
||||
tasks.add(task);
|
||||
} else {
|
||||
final task = MultiUploadTask(
|
||||
url: url,
|
||||
files: files,
|
||||
headers: headers,
|
||||
fields: fields,
|
||||
updates: Updates.statusAndProgress,
|
||||
group: 'backup',
|
||||
taskId: entity.id,
|
||||
retries: 0,
|
||||
displayName: 'Immich',
|
||||
httpRequestMethod: 'POST',
|
||||
baseDirectory: BaseDirectory.root,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print('created task $task for files $files');
|
||||
|
||||
tasks.add(task);
|
||||
}
|
||||
}
|
||||
|
||||
final permission = await _fileDownloader.permissions
|
||||
.status(PermissionType.androidSharedStorage);
|
||||
print('has permission $permission');
|
||||
|
||||
if (tasks.length == 1) {
|
||||
final result = await _fileDownloader.upload(
|
||||
tasks.first,
|
||||
onProgress: (percent) => print('${percent * 100} done'),
|
||||
onStatus: (status) => print('status $status'),
|
||||
onElapsedTime: (t) => print('time is $t'),
|
||||
elapsedTimeInterval: const Duration(seconds: 1),
|
||||
);
|
||||
|
||||
print('$result is done with ${result.status}');
|
||||
print('result ${result.responseBody}');
|
||||
print('result ${result.responseHeaders}');
|
||||
} else {
|
||||
final result = await _fileDownloader.uploadBatch(
|
||||
tasks,
|
||||
batchProgressCallback: (succeeded, failed) =>
|
||||
print('$succeeded succeeded, $failed failed'),
|
||||
taskStatusCallback: (status) => print('status $status'),
|
||||
taskProgressCallback: (update) => print('update $update'),
|
||||
onElapsedTime: (t) => print('time is $t'),
|
||||
elapsedTimeInterval: const Duration(seconds: 1),
|
||||
);
|
||||
|
||||
print(
|
||||
'$result is done with ${result.succeeded.length} succeeded and ${result.failed.length} failed',
|
||||
);
|
||||
|
||||
for (final task in result.succeeded) {
|
||||
final r = result.results[task];
|
||||
print('successful task $task with result $r');
|
||||
}
|
||||
|
||||
for (final task in result.failed) {
|
||||
final r = result.results[task];
|
||||
print('failed task $task with result $r');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (result.status == 200) {
|
||||
// asset is a duplicate (already exists on the server)
|
||||
duplicatedAssetIds.add(entity.id);
|
||||
uploadSuccessCb(entity.id, deviceId, true);
|
||||
@@ -405,6 +436,7 @@ class BackupService {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (duplicatedAssetIds.isNotEmpty) {
|
||||
await _saveDuplicatedAssetIds(duplicatedAssetIds);
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ part of 'map_state.provider.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$mapStateNotifierHash() => r'3b509b57b7400b09817e9caee9debf899172cd52';
|
||||
String _$mapStateNotifierHash() => r'6408d616ec9fc0d1ff26e25692417c43504ff754';
|
||||
|
||||
/// See also [MapStateNotifier].
|
||||
@ProviderFor(MapStateNotifier)
|
||||
|
||||
@@ -1393,7 +1393,7 @@ class VideoViewerRoute extends PageRouteInfo<VideoViewerRouteArgs> {
|
||||
void Function()? onPaused,
|
||||
Widget? placeholder,
|
||||
bool showControls = true,
|
||||
Duration hideControlsTimer = const Duration(milliseconds: 1500),
|
||||
Duration hideControlsTimer = const Duration(seconds: 5),
|
||||
bool showDownloadingIndicator = true,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
@@ -1429,7 +1429,7 @@ class VideoViewerRouteArgs {
|
||||
this.onPaused,
|
||||
this.placeholder,
|
||||
this.showControls = true,
|
||||
this.hideControlsTimer = const Duration(milliseconds: 1500),
|
||||
this.hideControlsTimer = const Duration(seconds: 5),
|
||||
this.showDownloadingIndicator = true,
|
||||
});
|
||||
|
||||
|
||||
+26
-42
@@ -73,6 +73,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.3.2"
|
||||
background_downloader:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: background_downloader
|
||||
sha256: "39ac21607259ec24012cbf81816aa209e0d765fa086ca03a3a1d112cfad68b52"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.2.1"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -413,10 +421,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "6.1.4"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -707,10 +715,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
|
||||
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.5"
|
||||
version: "1.2.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -860,30 +868,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -931,18 +915,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
version: "0.12.16"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
version: "0.5.0"
|
||||
meta:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
@@ -1026,10 +1010,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
version: "1.8.3"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1162,10 +1146,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
version: "3.1.2"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1194,10 +1178,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
|
||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
version: "4.2.4"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1663,10 +1647,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
version: "11.10.0"
|
||||
wakelock_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1711,10 +1695,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webdriver
|
||||
sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e"
|
||||
sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
version: "3.0.2"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -58,6 +58,7 @@ dependencies:
|
||||
timezone: ^0.9.2
|
||||
octo_image: ^2.0.0
|
||||
thumbhash: 0.1.0+1
|
||||
background_downloader: ^8.0.0
|
||||
|
||||
openapi:
|
||||
path: openapi
|
||||
@@ -80,6 +81,7 @@ dependency_overrides:
|
||||
#f url: https://github.com/Zverik/flutter-geolocator.git
|
||||
#f ref: floss
|
||||
#f path: geolocator_android
|
||||
http: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user