mirror of
https://github.com/immich-app/immich.git
synced 2025-06-23 15:30:51 -04:00
fix: filename interpretation
This commit is contained in:
parent
3ccde454b1
commit
12a472ebbe
@ -156,7 +156,21 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
||||
}
|
||||
|
||||
void _configureFileDownloaderNotifications() {
|
||||
FileDownloader().configureNotification(
|
||||
FileDownloader().configureNotificationForGroup(
|
||||
downloadGroupImage,
|
||||
running: TaskNotification(
|
||||
'downloading_media'.tr(),
|
||||
'${'file_name'.tr()}: {filename}',
|
||||
),
|
||||
complete: TaskNotification(
|
||||
'download_finished'.tr(),
|
||||
'${'file_name'.tr()}: {filename}',
|
||||
),
|
||||
progressBar: true,
|
||||
);
|
||||
|
||||
FileDownloader().configureNotificationForGroup(
|
||||
downloadGroupVideo,
|
||||
running: TaskNotification(
|
||||
'downloading_media'.tr(),
|
||||
'${'file_name'.tr()}: {filename}',
|
||||
|
@ -197,7 +197,7 @@ class ExpBackupPage extends HookConsumerWidget {
|
||||
const RemainderCard(),
|
||||
const Divider(),
|
||||
buildControlButtons(),
|
||||
const CurrentUploadingAssetInfoBox(),
|
||||
// const CurrentUploadingAssetInfoBox(),
|
||||
]
|
||||
: [
|
||||
const BackupAlbumSelectionCard(),
|
||||
|
@ -2,35 +2,99 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
|
||||
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||
|
||||
import 'package:immich_mobile/services/exp_backup.service.dart';
|
||||
import 'package:immich_mobile/services/upload.service.dart';
|
||||
|
||||
class ExpUploadStatus {
|
||||
final String taskId;
|
||||
final String filename;
|
||||
final double progress;
|
||||
ExpUploadStatus({
|
||||
required this.taskId,
|
||||
required this.filename,
|
||||
required this.progress,
|
||||
});
|
||||
|
||||
ExpUploadStatus copyWith({
|
||||
String? taskId,
|
||||
String? filename,
|
||||
double? progress,
|
||||
}) {
|
||||
return ExpUploadStatus(
|
||||
taskId: taskId ?? this.taskId,
|
||||
filename: filename ?? this.filename,
|
||||
progress: progress ?? this.progress,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'taskId': taskId,
|
||||
'filename': filename,
|
||||
'progress': progress,
|
||||
};
|
||||
}
|
||||
|
||||
factory ExpUploadStatus.fromMap(Map<String, dynamic> map) {
|
||||
return ExpUploadStatus(
|
||||
taskId: map['taskId'] as String,
|
||||
filename: map['filename'] as String,
|
||||
progress: map['progress'] as double,
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory ExpUploadStatus.fromJson(String source) =>
|
||||
ExpUploadStatus.fromMap(json.decode(source) as Map<String, dynamic>);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'ExpUploadStatus(taskId: $taskId, filename: $filename, progress: $progress)';
|
||||
|
||||
@override
|
||||
bool operator ==(covariant ExpUploadStatus other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.taskId == taskId &&
|
||||
other.filename == filename &&
|
||||
other.progress == progress;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => taskId.hashCode ^ filename.hashCode ^ progress.hashCode;
|
||||
}
|
||||
|
||||
class ExpBackupState {
|
||||
final int totalCount;
|
||||
final int backupCount;
|
||||
final int remainderCount;
|
||||
final Map<String, ExpUploadStatus> uploadItems;
|
||||
|
||||
ExpBackupState({
|
||||
required this.totalCount,
|
||||
required this.backupCount,
|
||||
required this.remainderCount,
|
||||
required this.uploadItems,
|
||||
});
|
||||
|
||||
ExpBackupState copyWith({
|
||||
int? totalCount,
|
||||
int? backupCount,
|
||||
int? remainderCount,
|
||||
Map<String, ExpUploadStatus>? uploadItems,
|
||||
}) {
|
||||
return ExpBackupState(
|
||||
totalCount: totalCount ?? this.totalCount,
|
||||
backupCount: backupCount ?? this.backupCount,
|
||||
remainderCount: remainderCount ?? this.remainderCount,
|
||||
uploadItems: uploadItems ?? this.uploadItems,
|
||||
);
|
||||
}
|
||||
|
||||
@ -39,6 +103,7 @@ class ExpBackupState {
|
||||
'totalCount': totalCount,
|
||||
'backupCount': backupCount,
|
||||
'remainderCount': remainderCount,
|
||||
'uploadItems': uploadItems,
|
||||
};
|
||||
}
|
||||
|
||||
@ -47,6 +112,14 @@ class ExpBackupState {
|
||||
totalCount: map['totalCount'] as int,
|
||||
backupCount: map['backupCount'] as int,
|
||||
remainderCount: map['remainderCount'] as int,
|
||||
uploadItems: Map<String, ExpUploadStatus>.from(
|
||||
(map['uploadItems'] as Map<String, dynamic>).map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
ExpUploadStatus.fromMap(value as Map<String, dynamic>),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -56,21 +129,28 @@ class ExpBackupState {
|
||||
ExpBackupState.fromMap(json.decode(source) as Map<String, dynamic>);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'ExpBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount)';
|
||||
String toString() {
|
||||
return 'ExpBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount, uploadItems: $uploadItems)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant ExpBackupState other) {
|
||||
if (identical(this, other)) return true;
|
||||
final mapEquals = const DeepCollectionEquality().equals;
|
||||
|
||||
return other.totalCount == totalCount &&
|
||||
other.backupCount == backupCount &&
|
||||
other.remainderCount == remainderCount;
|
||||
other.remainderCount == remainderCount &&
|
||||
mapEquals(other.uploadItems, uploadItems);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
totalCount.hashCode ^ backupCount.hashCode ^ remainderCount.hashCode;
|
||||
int get hashCode {
|
||||
return totalCount.hashCode ^
|
||||
backupCount.hashCode ^
|
||||
remainderCount.hashCode ^
|
||||
uploadItems.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
final expBackupProvider =
|
||||
@ -92,6 +172,7 @@ class ExpBackupNotifier extends StateNotifier<ExpBackupState> {
|
||||
totalCount: 0,
|
||||
backupCount: 0,
|
||||
remainderCount: 0,
|
||||
uploadItems: {},
|
||||
),
|
||||
) {
|
||||
{
|
||||
@ -120,8 +201,6 @@ class ExpBackupNotifier extends StateNotifier<ExpBackupState> {
|
||||
remainderCount: state.remainderCount - 1,
|
||||
);
|
||||
|
||||
// TODO: find a better place to call this.
|
||||
_backgroundSyncManager.syncRemote();
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -130,10 +209,30 @@ class ExpBackupNotifier extends StateNotifier<ExpBackupState> {
|
||||
}
|
||||
|
||||
void _taskProgressCallback(TaskProgressUpdate update) {
|
||||
debugPrint("[_taskProgressCallback] $update");
|
||||
final uploadStatus = ExpUploadStatus(
|
||||
taskId: update.task.taskId,
|
||||
filename: update.task.displayName,
|
||||
progress: update.progress,
|
||||
);
|
||||
|
||||
state = state.copyWith(
|
||||
uploadItems: {
|
||||
for (final entry in state.uploadItems.entries)
|
||||
if (entry.key == update.task.taskId)
|
||||
entry.key: uploadStatus
|
||||
else
|
||||
entry.key: entry.value,
|
||||
if (!state.uploadItems.containsKey(update.task.taskId))
|
||||
update.task.taskId: uploadStatus,
|
||||
},
|
||||
);
|
||||
|
||||
print(update.task.taskId);
|
||||
}
|
||||
|
||||
Future<void> getBackupStatus() async {
|
||||
await _backgroundSyncManager.syncRemote();
|
||||
|
||||
final [totalCount, backupCount, remainderCount] = await Future.wait([
|
||||
_backupService.getTotalCount(),
|
||||
_backupService.getBackupCount(),
|
||||
@ -152,7 +251,6 @@ class ExpBackupNotifier extends StateNotifier<ExpBackupState> {
|
||||
}
|
||||
|
||||
Future<void> cancel() async {
|
||||
await _uploadService.cancel();
|
||||
debugPrint("Cancel uploads");
|
||||
await _backupService.cancel();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ class ExpBackupService {
|
||||
final IBackupRepository _backupRepository;
|
||||
final IStorageRepository _storageRepository;
|
||||
final UploadService _uploadService;
|
||||
bool shouldCancel = false;
|
||||
|
||||
Future<int> getTotalCount() {
|
||||
return _backupRepository.getTotalCount();
|
||||
@ -40,6 +41,8 @@ class ExpBackupService {
|
||||
}
|
||||
|
||||
Future<void> backup() async {
|
||||
shouldCancel = false;
|
||||
|
||||
final candidates = await _backupRepository.getCandidates();
|
||||
if (candidates.isEmpty) {
|
||||
return;
|
||||
@ -47,6 +50,10 @@ class ExpBackupService {
|
||||
|
||||
const batchSize = 5;
|
||||
for (int i = 0; i < candidates.length; i += batchSize) {
|
||||
if (shouldCancel) {
|
||||
break;
|
||||
}
|
||||
|
||||
final batch = candidates.skip(i).take(batchSize).toList();
|
||||
|
||||
List<UploadTask> tasks = [];
|
||||
@ -57,7 +64,7 @@ class ExpBackupService {
|
||||
}
|
||||
}
|
||||
|
||||
if (tasks.isNotEmpty) {
|
||||
if (tasks.isNotEmpty && !shouldCancel) {
|
||||
_uploadService.enqueueTasks(tasks);
|
||||
}
|
||||
}
|
||||
@ -82,6 +89,7 @@ class ExpBackupService {
|
||||
}
|
||||
|
||||
Future<void> cancel() async {
|
||||
shouldCancel = true;
|
||||
await _uploadService.cancel();
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ class UploadService {
|
||||
|
||||
return UploadTask(
|
||||
taskId: id,
|
||||
displayName: filename,
|
||||
displayName: originalFileName ?? filename,
|
||||
httpRequestMethod: 'POST',
|
||||
url: url,
|
||||
headers: headers,
|
||||
|
@ -2,43 +2,53 @@ import 'dart:io';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/providers/backup/exp_backup.provider.dart';
|
||||
import 'package:immich_mobile/widgets/backup/asset_info_table.dart';
|
||||
import 'package:immich_mobile/widgets/backup/error_chip.dart';
|
||||
import 'package:immich_mobile/widgets/backup/icloud_download_progress_bar.dart';
|
||||
import 'package:immich_mobile/widgets/backup/upload_progress_bar.dart';
|
||||
import 'package:immich_mobile/widgets/backup/upload_stats.dart';
|
||||
|
||||
class CurrentUploadingAssetInfoBox extends StatelessWidget {
|
||||
class CurrentUploadingAssetInfoBox extends ConsumerWidget {
|
||||
const CurrentUploadingAssetInfoBox({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
isThreeLine: true,
|
||||
leading: Icon(
|
||||
Icons.image_outlined,
|
||||
color: context.primaryColor,
|
||||
size: 30,
|
||||
),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"backup_controller_page_uploading_file_info",
|
||||
style: context.textTheme.titleSmall,
|
||||
).tr(),
|
||||
const BackupErrorChip(),
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
children: [
|
||||
if (Platform.isIOS) const IcloudDownloadProgressBar(),
|
||||
const BackupUploadProgressBar(),
|
||||
const BackupUploadStats(),
|
||||
const BackupAssetInfoTable(),
|
||||
],
|
||||
),
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final uploadItems =
|
||||
ref.watch(expBackupProvider.select((state) => state.uploadItems));
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
if (uploadItems.isNotEmpty)
|
||||
Container(
|
||||
constraints: const BoxConstraints(maxHeight: 200),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: uploadItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final uploadItem = uploadItems.values.elementAt(index);
|
||||
return ListTile(
|
||||
dense: true,
|
||||
leading: CircularProgressIndicator(
|
||||
value: uploadItem.progress,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
title: Text(
|
||||
uploadItem.filename,
|
||||
style: context.textTheme.bodySmall,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailing: Text(
|
||||
'${(uploadItem.progress * 100).toInt()}%',
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -103,10 +103,6 @@ export class FileUploadInterceptor implements NestInterceptor {
|
||||
}
|
||||
|
||||
private filename(request: AuthRequest, file: Express.Multer.File, callback: DiskStorageCallback) {
|
||||
console.log(
|
||||
'FileUploadInterceptor.filename called with file:',
|
||||
this.assetService.getUploadFilename(asRequest(request, file)),
|
||||
);
|
||||
return callbackify(
|
||||
() => this.assetService.getUploadFilename(asRequest(request, file)),
|
||||
callback as Callback<string>,
|
||||
|
@ -191,8 +191,6 @@ export function mapToUploadFile(file: ImmichFile): UploadFile {
|
||||
}
|
||||
|
||||
export const asRequest = (request: AuthRequest, file: Express.Multer.File) => {
|
||||
file.originalname = request.body['filename'] ?? file.filename;
|
||||
|
||||
return {
|
||||
auth: request.user || null,
|
||||
fieldName: file.fieldname as UploadFieldName,
|
||||
|
Loading…
x
Reference in New Issue
Block a user