ente/mobile/lib/services/deduplication_service.dart

95 lines
3.4 KiB
Dart
Raw Normal View History

import 'package:logging/logging.dart';
import "package:photos/core/configuration.dart";
2023-02-03 07:39:04 +00:00
import 'package:photos/core/network/network.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/models/duplicate_files.dart';
2023-08-25 04:39:30 +00:00
import 'package:photos/models/file/file.dart';
import "package:photos/services/collections_service.dart";
import "package:photos/services/files_service.dart";
class DeduplicationService {
final _logger = Logger("DeduplicationService");
2023-02-03 07:39:04 +00:00
final _enteDio = NetworkClient.instance.enteDio;
DeduplicationService._privateConstructor();
static final DeduplicationService instance =
DeduplicationService._privateConstructor();
Future<List<DuplicateFiles>> getDuplicateFiles() async {
try {
final List<DuplicateFiles> result = await _getDuplicateFiles();
return result;
} catch (e, s) {
_logger.severe("failed to get dedupeFile", e, s);
rethrow;
}
}
2023-12-14 04:10:50 +00:00
// Returns a list of DuplicateFiles, where each DuplicateFiles object contains
// a list of files that have the same size and hash
Future<List<DuplicateFiles>> _getDuplicateFiles() async {
Map<int, int> uploadIDToSize = {};
final bool hasFileSizes = await FilesService.instance.hasMigratedSizes();
if (!hasFileSizes) {
final DuplicateFilesResponse dupes = await _fetchDuplicateFileIDs();
uploadIDToSize = dupes.toUploadIDToSize();
}
final Set<int> allowedCollectionIDs =
CollectionsService.instance.nonHiddenOwnedCollections();
2023-08-24 16:56:24 +00:00
final List<EnteFile> allFiles = await FilesDB.instance.getAllFilesFromDB(
2023-08-14 05:54:28 +00:00
CollectionsService.instance.getHiddenCollectionIds(),
dedupeByUploadId: false,
);
final int ownerID = Configuration.instance.getUserID()!;
final List<EnteFile> filteredFiles = [];
for (final file in allFiles) {
if (!file.isUploaded ||
(file.hash ?? '').isEmpty ||
(file.ownerID ?? 0) != ownerID ||
(!allowedCollectionIDs.contains(file.collectionID!))) {
continue;
}
if ((file.fileSize ?? 0) <= 0) {
file.fileSize = uploadIDToSize[file.uploadedFileID!] ?? 0;
}
if ((file.fileSize ?? 0) <= 0) {
continue;
}
filteredFiles.add(file);
}
final Map<String, List<EnteFile>> sizeHashToFilesMap = {};
final Map<String, Set<int>> sizeHashToCollectionsSet = {};
final Set<int> processedFileIds = <int>{};
for (final file in filteredFiles) {
2023-12-14 04:10:50 +00:00
final key = '${file.fileSize}-${file.hash}';
if (!sizeHashToFilesMap.containsKey(key)) {
sizeHashToFilesMap[key] = <EnteFile>[];
sizeHashToCollectionsSet[key] = <int>{};
}
sizeHashToCollectionsSet[key]!.add(file.collectionID!);
if (!processedFileIds.contains(file.uploadedFileID)) {
sizeHashToFilesMap[key]!.add(file);
processedFileIds.add(file.uploadedFileID!);
}
}
2023-12-14 04:10:50 +00:00
final List<DuplicateFiles> dupesBySizeHash = [];
for (final key in sizeHashToFilesMap.keys) {
final List<EnteFile> files = sizeHashToFilesMap[key]!;
final Set<int> collectionIds = sizeHashToCollectionsSet[key]!;
if (files.length > 1) {
2023-12-14 04:10:50 +00:00
final size = files[0].fileSize!;
dupesBySizeHash.add(DuplicateFiles(files, size, collectionIds));
}
}
2023-12-14 04:10:50 +00:00
return dupesBySizeHash;
}
Future<DuplicateFilesResponse> _fetchDuplicateFileIDs() async {
2022-10-14 15:03:55 +00:00
final response = await _enteDio.get("/files/duplicates");
return DuplicateFilesResponse.fromMap(response.data);
}
}