mirror of
https://github.com/immich-app/immich.git
synced 2024-12-21 01:39:59 +02:00
c28251b8b4
* Added selection icon to thumbnail * Added micro-interaction and video file indication * Added page to add page * Added image viewer * navigate assets * Added separate component for viewing the video file * Added FFmpeg modules * Added correct content-type header for serving image file * Added loading spinner
153 lines
5.1 KiB
Dart
153 lines
5.1 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:hive/hive.dart';
|
|
import 'package:immich_mobile/constants/hive_box.dart';
|
|
import 'package:immich_mobile/shared/services/network.service.dart';
|
|
import 'package:immich_mobile/shared/models/device_info.model.dart';
|
|
import 'package:immich_mobile/utils/dio_http_interceptor.dart';
|
|
import 'package:immich_mobile/utils/files_helper.dart';
|
|
import 'package:photo_manager/photo_manager.dart';
|
|
import 'package:http_parser/http_parser.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
class BackupService {
|
|
final NetworkService _networkService = NetworkService();
|
|
|
|
Future<List<String>> getDeviceBackupAsset() async {
|
|
String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
|
|
|
|
Response response = await _networkService.getRequest(url: "asset/$deviceId");
|
|
List<dynamic> result = jsonDecode(response.toString());
|
|
|
|
return result.cast<String>();
|
|
}
|
|
|
|
backupAsset(Set<AssetEntity> assetList, CancelToken cancelToken, Function(String, String) singleAssetDoneCb,
|
|
Function(int, int) uploadProgress) async {
|
|
var dio = Dio();
|
|
dio.interceptors.add(AuthenticatedRequestInterceptor());
|
|
|
|
String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
|
|
String savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
|
File? file;
|
|
|
|
MultipartFile assetRawUploadData;
|
|
MultipartFile thumbnailUploadData;
|
|
|
|
for (var entity in assetList) {
|
|
try {
|
|
if (entity.type == AssetType.video) {
|
|
file = await entity.originFile;
|
|
} else {
|
|
file = await entity.originFile.timeout(const Duration(seconds: 5));
|
|
}
|
|
|
|
if (file != null) {
|
|
FormData formData;
|
|
String originalFileName = await entity.titleAsync;
|
|
String fileNameWithoutPath = originalFileName.toString().split(".")[0];
|
|
var fileExtension = p.extension(file.path);
|
|
var mimeType = FileHelper.getMimeType(file.path);
|
|
assetRawUploadData = await MultipartFile.fromFile(
|
|
file.path,
|
|
filename: fileNameWithoutPath,
|
|
contentType: MediaType(
|
|
mimeType["type"],
|
|
mimeType["subType"],
|
|
),
|
|
);
|
|
formData = FormData.fromMap({
|
|
'deviceAssetId': entity.id,
|
|
'deviceId': deviceId,
|
|
'assetType': _getAssetType(entity.type),
|
|
'createdAt': entity.createDateTime.toIso8601String(),
|
|
'modifiedAt': entity.modifiedDateTime.toIso8601String(),
|
|
'isFavorite': entity.isFavorite,
|
|
'fileExtension': fileExtension,
|
|
'duration': entity.videoDuration,
|
|
'assetData': [assetRawUploadData]
|
|
});
|
|
|
|
// Build thumbnail multipart data
|
|
var thumbnailData = await entity.thumbnailDataWithSize(const ThumbnailSize(1440, 2560));
|
|
if (thumbnailData != null) {
|
|
thumbnailUploadData = MultipartFile.fromBytes(
|
|
List.from(thumbnailData),
|
|
filename: fileNameWithoutPath,
|
|
contentType: MediaType(
|
|
"image",
|
|
"jpeg",
|
|
),
|
|
);
|
|
|
|
// Send thumbnail data if it is exist
|
|
formData = FormData.fromMap({
|
|
'deviceAssetId': entity.id,
|
|
'deviceId': deviceId,
|
|
'assetType': _getAssetType(entity.type),
|
|
'createdAt': entity.createDateTime.toIso8601String(),
|
|
'modifiedAt': entity.modifiedDateTime.toIso8601String(),
|
|
'isFavorite': entity.isFavorite,
|
|
'fileExtension': fileExtension,
|
|
'duration': entity.videoDuration,
|
|
'thumbnailData': [thumbnailUploadData],
|
|
'assetData': [assetRawUploadData]
|
|
});
|
|
}
|
|
|
|
Response res = await dio.post(
|
|
'$savedEndpoint/asset/upload',
|
|
data: formData,
|
|
cancelToken: cancelToken,
|
|
onSendProgress: (sent, total) => uploadProgress(sent, total),
|
|
);
|
|
|
|
if (res.statusCode == 201) {
|
|
singleAssetDoneCb(entity.id, deviceId);
|
|
}
|
|
}
|
|
} on DioError catch (e) {
|
|
debugPrint("DioError backupAsset: ${e.response}");
|
|
if (e.type == DioErrorType.cancel || e.type == DioErrorType.other) {
|
|
return;
|
|
}
|
|
continue;
|
|
} catch (e) {
|
|
debugPrint("ERROR backupAsset: ${e.toString()}");
|
|
continue;
|
|
} finally {
|
|
if (Platform.isIOS) {
|
|
file?.deleteSync();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
String _getAssetType(AssetType assetType) {
|
|
switch (assetType) {
|
|
case AssetType.audio:
|
|
return "AUDIO";
|
|
case AssetType.image:
|
|
return "IMAGE";
|
|
case AssetType.video:
|
|
return "VIDEO";
|
|
case AssetType.other:
|
|
return "OTHER";
|
|
}
|
|
}
|
|
|
|
Future<DeviceInfoRemote> setAutoBackup(bool status, String deviceId, String deviceType) async {
|
|
var res = await _networkService.patchRequest(url: 'device-info', data: {
|
|
"isAutoBackup": status,
|
|
"deviceId": deviceId,
|
|
"deviceType": deviceType,
|
|
});
|
|
|
|
return DeviceInfoRemote.fromJson(res.toString());
|
|
}
|
|
}
|