Selaa lähdekoodia

Add option to dedupe by file name (#1164)

Vishnu Mohandas 2 vuotta sitten
vanhempi
commit
25612e539d

+ 2 - 0
lib/generated/intl/messages_en.dart

@@ -370,6 +370,8 @@ class MessageLookup extends MessageLookupByLibrary {
         "close": MessageLookupByLibrary.simpleMessage("Close"),
         "clubByCaptureTime":
             MessageLookupByLibrary.simpleMessage("Club by capture time"),
+        "clubByFileName":
+            MessageLookupByLibrary.simpleMessage("Club by file name"),
         "codeAppliedPageTitle":
             MessageLookupByLibrary.simpleMessage("Code applied"),
         "codeCopiedToClipboard":

+ 10 - 0
lib/generated/l10n.dart

@@ -5753,6 +5753,16 @@ class S {
     );
   }
 
+  /// `Club by file name`
+  String get clubByFileName {
+    return Intl.message(
+      'Club by file name',
+      name: 'clubByFileName',
+      desc: '',
+      args: [],
+    );
+  }
+
   /// `Count`
   String get count {
     return Intl.message(

+ 1 - 0
lib/l10n/intl_en.arb

@@ -800,6 +800,7 @@
   "deselectAll": "Deselect all",
   "reviewDeduplicateItems": "Please review and delete the items you believe are duplicates.",
   "clubByCaptureTime": "Club by capture time",
+  "clubByFileName": "Club by file name",
   "count": "Count",
   "totalSize": "Total size",
   "time": "Time",

+ 38 - 0
lib/services/deduplication_service.dart

@@ -103,6 +103,44 @@ class DeduplicationService {
     return result;
   }
 
+  List<DuplicateFiles> clubDuplicatesByName(List<DuplicateFiles> dupes) {
+    final result = <DuplicateFiles>[];
+    for (final dupe in dupes) {
+      final files = <File>[];
+      final Map<String, int> fileNameCounter = {};
+      String mostFrequentFileName = "";
+      int mostFrequentFileNameCount = 0;
+      // Counts the frequency of creationTimes within the supposed duplicates
+      for (final file in dupe.files) {
+        if (fileNameCounter.containsKey(file.displayName)) {
+          fileNameCounter[file.displayName] =
+              fileNameCounter[file.displayName]! + 1;
+        } else {
+          fileNameCounter[file.displayName] = 0;
+        }
+        if (fileNameCounter[file.displayName]! >
+            mostFrequentFileNameCount) {
+          mostFrequentFileNameCount =
+              fileNameCounter[file.displayName]!;
+          mostFrequentFileName = file.displayName;
+        }
+        files.add(file);
+      }
+      // Ignores those files that were not created within the most common creationTime
+      final incorrectDuplicates = <File>{};
+      for (final file in files) {
+        if (file.displayName != mostFrequentFileName) {
+          incorrectDuplicates.add(file);
+        }
+      }
+      files.removeWhere((file) => incorrectDuplicates.contains(file));
+      if (files.length > 1) {
+        result.add(DuplicateFiles(files, dupe.size));
+      }
+    }
+    return result;
+  }
+
   Future<DuplicateFilesResponse> _fetchDuplicateFileIDs() async {
     final response = await _enteDio.get("/files/duplicates");
     return DuplicateFilesResponse.fromMap(response.data);

+ 38 - 20
lib/ui/tools/deduplicate_page.dart

@@ -48,7 +48,8 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
   final Set<File> _selectedFiles = <File>{};
   final Map<int?, int> _fileSizeMap = {};
   late List<DuplicateFiles> _duplicates;
-  bool? _shouldClubByCaptureTime = true;
+  bool _shouldClubByCaptureTime = true;
+  bool _shouldClubByFileName = false;
   bool toastShown = false;
 
   SortKey sortKey = SortKey.size;
@@ -203,14 +204,6 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
       child: Column(
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
-          Text(
-            "Following files were clubbed based on their sizes" +
-                (_shouldClubByCaptureTime! ? " and capture times." : "."),
-            style: Theme.of(context).textTheme.subtitle2,
-          ),
-          const Padding(
-            padding: EdgeInsets.all(2),
-          ),
           Text(
             S.of(context).reviewDeduplicateItems,
             style: Theme.of(context).textTheme.subtitle2,
@@ -229,24 +222,49 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
   Widget _getClubbingConfig() {
     return Padding(
       padding: const EdgeInsets.fromLTRB(12, 0, 12, 4),
-      child: CheckboxListTile(
-        value: _shouldClubByCaptureTime,
-        onChanged: (value) {
-          _shouldClubByCaptureTime = value;
-          _resetEntriesAndSelection();
-          setState(() {});
-        },
-        title: Text(S.of(context).clubByCaptureTime),
+      child: Column(
+        children: [
+          CheckboxListTile(
+            value: _shouldClubByFileName,
+            onChanged: (value) {
+              _shouldClubByFileName = value!;
+              _resetEntriesAndSelection();
+              setState(() {});
+            },
+            title: Text(S.of(context).clubByFileName),
+          ),
+          CheckboxListTile(
+            value: _shouldClubByCaptureTime,
+            onChanged: (value) {
+              _shouldClubByCaptureTime = value!;
+              _resetEntriesAndSelection();
+              setState(() {});
+            },
+            title: Text(S.of(context).clubByCaptureTime),
+          ),
+          const Padding(
+            padding: EdgeInsets.all(8),
+          ),
+          const Divider(
+            height: 0,
+          ),
+          const Padding(
+            padding: EdgeInsets.all(4),
+          ),
+        ],
       ),
     );
   }
 
   void _resetEntriesAndSelection() {
-    if (_shouldClubByCaptureTime!) {
+    _duplicates = widget.duplicates;
+    if (_shouldClubByCaptureTime) {
       _duplicates =
           DeduplicationService.instance.clubDuplicatesByTime(_duplicates);
-    } else {
-      _duplicates = widget.duplicates;
+    }
+    if (_shouldClubByFileName) {
+      _duplicates =
+          DeduplicationService.instance.clubDuplicatesByName(_duplicates);
     }
     _selectAllFilesButFirst();
   }