mirror of
https://github.com/arsenetar/dupeguru.git
synced 2025-05-07 01:19:48 +00:00
parent
ba13b700b0
commit
9bd0ec8875
@ -12,7 +12,7 @@ dialogHeights = {
|
|||||||
scanTypeNames = {
|
scanTypeNames = {
|
||||||
'se': ["Filename", "Content", "Folders"],
|
'se': ["Filename", "Content", "Folders"],
|
||||||
'me': ["Filename", "Filename - Fields", "Filename - Fields (No Order)", "Tags", "Content", "Audio Content"],
|
'me': ["Filename", "Filename - Fields", "Filename - Fields (No Order)", "Tags", "Content", "Audio Content"],
|
||||||
'pe': ["Contents", "EXIF Timestamp"],
|
'pe': ["Contents", "EXIF Timestamp", "Trigger-happy mode"],
|
||||||
}
|
}
|
||||||
|
|
||||||
result = Window(410, dialogHeights[edition], dialogTitles[edition])
|
result = Window(410, dialogHeights[edition], dialogTitles[edition])
|
||||||
|
@ -331,6 +331,7 @@ class PyDupeGuru(PyDupeGuruBase):
|
|||||||
self.model.scanner.scan_type = [
|
self.model.scanner.scan_type = [
|
||||||
ScanType.FuzzyBlock,
|
ScanType.FuzzyBlock,
|
||||||
ScanType.ExifTimestamp,
|
ScanType.ExifTimestamp,
|
||||||
|
ScanType.TriggerHappyMode,
|
||||||
][scan_type]
|
][scan_type]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
@ -33,6 +33,7 @@ class ScanType:
|
|||||||
#PE
|
#PE
|
||||||
FuzzyBlock = 10
|
FuzzyBlock = 10
|
||||||
ExifTimestamp = 11
|
ExifTimestamp = 11
|
||||||
|
TriggerHappyMode = 12
|
||||||
|
|
||||||
SCANNABLE_TAGS = ['track', 'artist', 'album', 'title', 'genre', 'year']
|
SCANNABLE_TAGS = ['track', 'artist', 'album', 'title', 'genre', 'year']
|
||||||
|
|
||||||
|
@ -10,21 +10,43 @@ from collections import defaultdict
|
|||||||
from itertools import combinations
|
from itertools import combinations
|
||||||
|
|
||||||
from hscommon.trans import tr
|
from hscommon.trans import tr
|
||||||
|
from jobprogress import job
|
||||||
|
|
||||||
from core.engine import Match
|
from core.engine import Match
|
||||||
|
|
||||||
def getmatches(files, match_scaled, j):
|
def group_by_timestamp(files, date_only=False, j=job.nulljob):
|
||||||
|
"""Returns a mapping timestamp --> set(files).
|
||||||
|
|
||||||
|
If ``date_only`` is ``True``, ignore the "time" part of the timestamp and consider files as
|
||||||
|
matching as soon as their date part match.
|
||||||
|
"""
|
||||||
timestamp2pic = defaultdict(set)
|
timestamp2pic = defaultdict(set)
|
||||||
for picture in j.iter_with_progress(files, tr("Read EXIF of %d/%d pictures")):
|
for picture in j.iter_with_progress(files, tr("Read EXIF of %d/%d pictures")):
|
||||||
timestamp = picture.exif_timestamp
|
timestamp = picture.exif_timestamp
|
||||||
if timestamp:
|
if timestamp:
|
||||||
|
if date_only:
|
||||||
|
timestamp = timestamp[:10]
|
||||||
timestamp2pic[timestamp].add(picture)
|
timestamp2pic[timestamp].add(picture)
|
||||||
if '0000:00:00 00:00:00' in timestamp2pic: # very likely false matches
|
NULL_TS = '0000:00:00 00:00:00'
|
||||||
del timestamp2pic['0000:00:00 00:00:00']
|
if date_only:
|
||||||
|
NULL_TS = NULL_TS[:10]
|
||||||
|
if NULL_TS in timestamp2pic: # very likely false matches
|
||||||
|
del timestamp2pic[NULL_TS]
|
||||||
|
return timestamp2pic
|
||||||
|
|
||||||
|
def getmatches(files, match_scaled=True, date_only=False, j=job.nulljob):
|
||||||
|
"""Returns a list of files with the same EXIF date.
|
||||||
|
|
||||||
|
Reads the EXIF tag of all ``files`` and return a :class:`Match` for every pair of files having
|
||||||
|
the exact same EXIF timestamp (DateTimeOriginal).
|
||||||
|
|
||||||
|
If ``match_scaled`` if ``False``, ignore files that don't have the same dimensions.
|
||||||
|
"""
|
||||||
|
timestamp2pic = group_by_timestamp(files, j=j)
|
||||||
matches = []
|
matches = []
|
||||||
for pictures in timestamp2pic.values():
|
for pictures in timestamp2pic.values():
|
||||||
for p1, p2 in combinations(pictures, 2):
|
for p1, p2 in combinations(pictures, 2):
|
||||||
if (not match_scaled) and (p1.dimensions != p2.dimensions):
|
if (not match_scaled) and (p1.dimensions != p2.dimensions):
|
||||||
continue
|
continue
|
||||||
matches.append(Match(p1, p2, 100))
|
matches.append(Match(p1, p2, 100))
|
||||||
return matches
|
return matches
|
||||||
|
@ -20,7 +20,17 @@ class ScannerPE(Scanner):
|
|||||||
if self.scan_type == ScanType.FuzzyBlock:
|
if self.scan_type == ScanType.FuzzyBlock:
|
||||||
return matchblock.getmatches(files, self.cache_path, self.threshold, self.match_scaled, j)
|
return matchblock.getmatches(files, self.cache_path, self.threshold, self.match_scaled, j)
|
||||||
elif self.scan_type == ScanType.ExifTimestamp:
|
elif self.scan_type == ScanType.ExifTimestamp:
|
||||||
return matchexif.getmatches(files, self.match_scaled, j)
|
return matchexif.getmatches(files, match_scaled=self.match_scaled, j=j)
|
||||||
|
elif self.scan_type == ScanType.TriggerHappyMode:
|
||||||
|
j = j.start_subjob([1, 9])
|
||||||
|
groups = matchexif.group_by_timestamp(files, date_only=True, j=j)
|
||||||
|
j = j.start_subjob(len(groups))
|
||||||
|
matches = []
|
||||||
|
for subfiles in groups.values():
|
||||||
|
matches += matchblock.getmatches(
|
||||||
|
list(subfiles), self.cache_path, self.threshold, self.match_scaled, j
|
||||||
|
)
|
||||||
|
return matches
|
||||||
else:
|
else:
|
||||||
raise Exception("Invalid scan type")
|
raise Exception("Invalid scan type")
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ tr = trget('ui')
|
|||||||
SCAN_TYPE_ORDER = [
|
SCAN_TYPE_ORDER = [
|
||||||
ScanType.FuzzyBlock,
|
ScanType.FuzzyBlock,
|
||||||
ScanType.ExifTimestamp,
|
ScanType.ExifTimestamp,
|
||||||
|
ScanType.TriggerHappyMode,
|
||||||
]
|
]
|
||||||
|
|
||||||
class PreferencesDialog(PreferencesDialogBase):
|
class PreferencesDialog(PreferencesDialogBase):
|
||||||
@ -32,6 +33,7 @@ class PreferencesDialog(PreferencesDialogBase):
|
|||||||
scanTypeLabels = [
|
scanTypeLabels = [
|
||||||
tr("Contents"),
|
tr("Contents"),
|
||||||
tr("EXIF Timestamp"),
|
tr("EXIF Timestamp"),
|
||||||
|
tr("Trigger-happy mode"),
|
||||||
]
|
]
|
||||||
self._setupScanTypeBox(scanTypeLabels)
|
self._setupScanTypeBox(scanTypeLabels)
|
||||||
self._setupFilterHardnessBox()
|
self._setupFilterHardnessBox()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user