Added PE's "trigger happy" scan type

ref #242
This commit is contained in:
Virgil Dupras 2014-03-30 16:01:56 -04:00
parent ba13b700b0
commit 9bd0ec8875
6 changed files with 42 additions and 6 deletions

View File

@ -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])

View File

@ -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

View File

@ -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']

View File

@ -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

View File

@ -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")

View File

@ -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()