1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

fix(web): emit updated date when pressing enter (#9640)

wip uploading

format

wip first working version
This commit is contained in:
Lukas 2024-05-21 18:58:57 +02:00 committed by Marty Fuhry
parent a3489d604b
commit 4907916345
No known key found for this signature in database
GPG key ID: E2AB6392D894D900
4 changed files with 138 additions and 103 deletions

Binary file not shown.

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:background_downloader/background_downloader.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -33,6 +34,7 @@ final backupServiceProvider = Provider(
class BackupService { class BackupService {
final httpClient = http.Client(); final httpClient = http.Client();
final _fileDownloader = FileDownloader();
final ApiService _apiService; final ApiService _apiService;
final Isar _db; final Isar _db;
final Logger _log = Logger("BackupService"); final Logger _log = Logger("BackupService");
@ -242,121 +244,146 @@ class BackupService {
) )
: assetList.toList(); : assetList.toList();
final tasks = <UploadTask>[];
for (var entity in assetsToUpload) { for (var entity in assetsToUpload) {
File? file; final isAvailableLocally =
File? livePhotoFile; await entity.isLocallyAvailable(isOrigin: true);
try { // Handle getting files from iCloud
final isAvailableLocally = if (!isAvailableLocally && Platform.isIOS) {
await entity.isLocallyAvailable(isOrigin: true); // Skip iCloud assets if the user has disabled this feature
if (isIgnoreIcloudAssets) {
// Handle getting files from iCloud continue;
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));
}
}
} }
if (file != null) { setCurrentUploadAssetCb(
String originalFileName = await entity.titleAsync; CurrentUploadAsset(
var fileStream = file.openRead(); id: entity.id,
var assetRawUploadData = http.MultipartFile( fileCreatedAt: entity.createDateTime.year == 1970
"assetData", ? entity.modifiedDateTime
fileStream, : entity.createDateTime,
file.lengthSync(), fileName: await entity.titleAsync,
filename: originalFileName, fileType: _getAssetType(entity.type),
); iCloudAsset: true,
),
);
}
var req = MultipartRequest( final files = [];
'POST', // TODO: This is silly to have to load the file just to access the path
Uri.parse('$savedEndpoint/asset/upload'), // But there doesn't seem to be any other way to do it
onProgress: ((bytes, totalBytes) => final fileName = (await entity.originFile)?.path;
uploadProgressCb(bytes, totalBytes)), files.add(fileName);
);
req.headers["x-immich-user-token"] = Store.get(StoreKey.accessToken);
req.headers["Transfer-Encoding"] = "chunked";
req.fields['deviceAssetId'] = entity.id; if (entity.isLivePhoto) {
req.fields['deviceId'] = deviceId; final livePhotoFileName = (await entity.originFileWithSubtype)?.path;
req.fields['fileCreatedAt'] = if (livePhotoFileName != null) {
entity.createDateTime.toUtc().toIso8601String(); files.add(livePhotoFileName);
req.fields['fileModifiedAt'] = }
entity.modifiedDateTime.toUtc().toIso8601String(); }
req.fields['isFavorite'] = entity.isFavorite.toString();
req.fields['duration'] = entity.videoDuration.toString();
req.files.add(assetRawUploadData); final url = '$savedEndpoint/asset/upload';
final headers = {
'x-immich-user-token': Store.get(StoreKey.accessToken),
'Transfer-Encoding': 'chunked',
};
var fileSize = file.lengthSync(); 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(),
};
if (entity.isLivePhoto) { if (files.length == 1) {
if (livePhotoFile != null) { final String file = files.first;
final livePhotoTitle = p.setExtension( final split = file.split('/');
originalFileName, final name = split.last;
p.extension(livePhotoFile.path), final directory = split.take(split.length - 1).join('/');
);
final fileStream = livePhotoFile.openRead();
final livePhotoRawUploadData = http.MultipartFile(
"livePhotoData",
fileStream,
livePhotoFile.lengthSync(),
filename: livePhotoTitle,
);
req.files.add(livePhotoRawUploadData);
fileSize += livePhotoFile.lengthSync();
} else {
_log.warning(
"Failed to obtain motion part of the livePhoto - $originalFileName",
);
}
}
setCurrentUploadAssetCb( final task = UploadTask(
CurrentUploadAsset( url: url,
id: entity.id, group: 'backup',
fileCreatedAt: entity.createDateTime.year == 1970 fileField: 'assetData',
? entity.modifiedDateTime taskId: entity.id,
: entity.createDateTime, fields: fields,
fileName: originalFileName, headers: headers,
fileType: _getAssetType(entity.type), updates: Updates.statusAndProgress,
fileSize: fileSize, retries: 0,
iCloudAsset: false, 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,
);
var response = print('created task $task for files $files');
await httpClient.send(req, cancellationToken: cancelToken);
if (response.statusCode == 200) { 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) // asset is a duplicate (already exists on the server)
duplicatedAssetIds.add(entity.id); duplicatedAssetIds.add(entity.id);
uploadSuccessCb(entity.id, deviceId, true); uploadSuccessCb(entity.id, deviceId, true);
@ -409,6 +436,7 @@ class BackupService {
} }
} }
} }
*/
if (duplicatedAssetIds.isNotEmpty) { if (duplicatedAssetIds.isNotEmpty) {
await _saveDuplicatedAssetIds(duplicatedAssetIds); await _saveDuplicatedAssetIds(duplicatedAssetIds);
} }

View file

@ -60,6 +60,7 @@ dependencies:
octo_image: ^2.0.0 octo_image: ^2.0.0
thumbhash: 0.1.0+1 thumbhash: 0.1.0+1
async: ^2.11.0 async: ^2.11.0
background_downloader: ^8.0.0
openapi: openapi:
path: openapi path: openapi
@ -82,6 +83,7 @@ dependency_overrides:
#f url: https://github.com/Zverik/flutter-geolocator.git #f url: https://github.com/Zverik/flutter-geolocator.git
#f ref: floss #f ref: floss
#f path: geolocator_android #f path: geolocator_android
http: ^1.1.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View file

@ -17,4 +17,9 @@
{value} {value}
on:input={(e) => (updatedValue = e.currentTarget.value)} on:input={(e) => (updatedValue = e.currentTarget.value)}
on:blur={() => (value = updatedValue)} on:blur={() => (value = updatedValue)}
on:keydown={(e) => {
if (e.key === 'Enter') {
value = updatedValue;
}
}}
/> />