1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-17 01:06:46 +01: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,11 +244,8 @@ class BackupService {
) )
: assetList.toList(); : assetList.toList();
final tasks = <UploadTask>[];
for (var entity in assetsToUpload) { for (var entity in assetsToUpload) {
File? file;
File? livePhotoFile;
try {
final isAvailableLocally = final isAvailableLocally =
await entity.isLocallyAvailable(isOrigin: true); await entity.isLocallyAvailable(isOrigin: true);
@ -268,95 +267,123 @@ class BackupService {
iCloudAsset: true, 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) { final files = [];
String originalFileName = await entity.titleAsync; // TODO: This is silly to have to load the file just to access the path
var fileStream = file.openRead(); // But there doesn't seem to be any other way to do it
var assetRawUploadData = http.MultipartFile( final fileName = (await entity.originFile)?.path;
"assetData", files.add(fileName);
fileStream,
file.lengthSync(),
filename: originalFileName,
);
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";
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();
req.files.add(assetRawUploadData);
var fileSize = file.lengthSync();
if (entity.isLivePhoto) { if (entity.isLivePhoto) {
if (livePhotoFile != null) { final livePhotoFileName = (await entity.originFileWithSubtype)?.path;
final livePhotoTitle = p.setExtension( if (livePhotoFileName != null) {
originalFileName, files.add(livePhotoFileName);
p.extension(livePhotoFile.path), }
}
final url = '$savedEndpoint/asset/upload';
final headers = {
'x-immich-user-token': Store.get(StoreKey.accessToken),
'Transfer-Encoding': 'chunked',
};
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 (files.length == 1) {
final String file = files.first;
final split = file.split('/');
final name = split.last;
final directory = split.take(split.length - 1).join('/');
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,
); );
final fileStream = livePhotoFile.openRead(); tasks.add(task);
final livePhotoRawUploadData = http.MultipartFile(
"livePhotoData",
fileStream,
livePhotoFile.lengthSync(),
filename: livePhotoTitle,
);
req.files.add(livePhotoRawUploadData);
fileSize += livePhotoFile.lengthSync();
} else { } else {
_log.warning( final task = MultiUploadTask(
"Failed to obtain motion part of the livePhoto - $originalFileName", 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,
); );
print('created task $task for files $files');
tasks.add(task);
} }
} }
setCurrentUploadAssetCb( final permission = await _fileDownloader.permissions
CurrentUploadAsset( .status(PermissionType.androidSharedStorage);
id: entity.id, print('has permission $permission');
fileCreatedAt: entity.createDateTime.year == 1970
? entity.modifiedDateTime if (tasks.length == 1) {
: entity.createDateTime, final result = await _fileDownloader.upload(
fileName: originalFileName, tasks.first,
fileType: _getAssetType(entity.type), onProgress: (percent) => print('${percent * 100} done'),
fileSize: fileSize, onStatus: (status) => print('status $status'),
iCloudAsset: false, onElapsedTime: (t) => print('time is $t'),
), elapsedTimeInterval: const Duration(seconds: 1),
); );
var response = print('$result is done with ${result.status}');
await httpClient.send(req, cancellationToken: cancelToken); 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),
);
if (response.statusCode == 200) { 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;
}
}}
/> />