From 75b08125c0d9a30e7c6e322780e8796c5b9af43e Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 10 Aug 2012 16:34:27 -0400 Subject: [PATCH] [#201 state:fixed] Added an EXIF Timestamp column in PE. --- cocoa/pe/ResultWindow.m | 1 + core_pe/app.py | 3 ++- core_pe/matchexif.py | 12 ++---------- core_pe/photo.py | 12 ++++++++++-- core_pe/result_table.py | 1 + qt/pe/results_model.py | 1 + 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/cocoa/pe/ResultWindow.m b/cocoa/pe/ResultWindow.m index f82f34a5..4cae60b3 100644 --- a/cocoa/pe/ResultWindow.m +++ b/cocoa/pe/ResultWindow.m @@ -22,6 +22,7 @@ http://www.hardcoded.net/licenses/bsd_license {@"size", 63, 16, 0, YES, nil}, {@"extension", 40, 16, 0, YES, nil}, {@"dimensions", 73, 16, 0, YES, nil}, + {@"exif_timestamp", 120, 16, 0, YES, nil}, {@"mtime", 120, 16, 0, YES, nil}, {@"percentage", 58, 16, 0, YES, nil}, {@"dupe_count", 80, 16, 0, YES, nil}, diff --git a/core_pe/app.py b/core_pe/app.py index 44053337..2279b483 100644 --- a/core_pe/app.py +++ b/core_pe/app.py @@ -24,7 +24,7 @@ def get_delta_dimensions(value, ref_value): class DupeGuru(DupeGuruBase): NAME = __appname__ - METADATA_TO_READ = ['size', 'mtime', 'dimensions'] + METADATA_TO_READ = ['size', 'mtime', 'dimensions', 'exif_timestamp'] def __init__(self, view, appdata): DupeGuruBase.__init__(self, view, appdata) @@ -54,6 +54,7 @@ class DupeGuru(DupeGuruBase): 'size': format_size(size, 0, 1, False), 'extension': dupe.extension, 'dimensions': format_dimensions(dimensions), + 'exif_timestamp': dupe.exif_timestamp, 'mtime': format_timestamp(mtime, delta and m), 'percentage': format_perc(percentage), 'dupe_count': format_dupe_count(dupe_count), diff --git a/core_pe/matchexif.py b/core_pe/matchexif.py index dab3b55e..442415c1 100644 --- a/core_pe/matchexif.py +++ b/core_pe/matchexif.py @@ -6,26 +6,18 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license -import logging from collections import defaultdict from itertools import combinations -from hscommon import io from hscommon.trans import tr from core.engine import Match -from . import exif def getmatches(files, match_scaled, j): timestamp2pic = defaultdict(set) for picture in j.iter_with_progress(files, tr("Read EXIF of %d/%d pictures")): - try: - with io.open(picture.path, 'rb') as fp: - exifdata = exif.get_fields(fp) - timestamp = exifdata['DateTimeOriginal'] - timestamp2pic[timestamp].add(picture) - except Exception: - logging.info("Couldn't read EXIF of picture: %s", picture.path) + timestamp = picture.exif_timestamp + timestamp2pic[timestamp].add(picture) if '0000:00:00 00:00:00' in timestamp2pic: # very likely false matches del timestamp2pic['0000:00:00 00:00:00'] matches = [] diff --git a/core_pe/photo.py b/core_pe/photo.py index cd398a7e..9dfd09a2 100644 --- a/core_pe/photo.py +++ b/core_pe/photo.py @@ -6,7 +6,7 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license -from hscommon import io +import logging from hscommon.util import get_file_ext from core import fs from . import exif @@ -15,6 +15,7 @@ class Photo(fs.File): INITIAL_INFO = fs.File.INITIAL_INFO.copy() INITIAL_INFO.update({ 'dimensions': (0,0), + 'exif_timestamp': '', }) __slots__ = fs.File.__slots__ + tuple(INITIAL_INFO.keys()) @@ -30,7 +31,7 @@ class Photo(fs.File): def _get_orientation(self): if not hasattr(self, '_cached_orientation'): try: - with io.open(self.path, 'rb') as fp: + with self.path.open('rb') as fp: exifdata = exif.get_fields(fp) # the value is a list (probably one-sized) of ints orientations = exifdata['Orientation'] @@ -49,6 +50,13 @@ class Photo(fs.File): self.dimensions = self._plat_get_dimensions() if self._get_orientation() in {5, 6, 7, 8}: self.dimensions = (self.dimensions[1], self.dimensions[0]) + elif field == 'exif_timestamp': + try: + with self.path.open('rb') as fp: + exifdata = exif.get_fields(fp) + self.exif_timestamp = exifdata['DateTimeOriginal'] + except Exception: + logging.info("Couldn't read EXIF of picture: %s", self.path) def get_blocks(self, block_count_per_side): return self._plat_get_blocks(block_count_per_side, self._get_orientation()) diff --git a/core_pe/result_table.py b/core_pe/result_table.py index 4dcc0f09..c36b0772 100644 --- a/core_pe/result_table.py +++ b/core_pe/result_table.py @@ -20,6 +20,7 @@ class ResultTable(ResultTableBase): Column('size', coltr("Size (KB)"), optional=True), Column('extension', coltr("Kind"), visible=False, optional=True), Column('dimensions', coltr("Dimensions"), optional=True), + Column('exif_timestamp', coltr("EXIF Timestamp"), visible=False, optional=True), Column('mtime', coltr("Modification"), visible=False, optional=True), Column('percentage', coltr("Match %"), optional=True), Column('dupe_count', coltr("Dupe Count"), visible=False, optional=True), diff --git a/qt/pe/results_model.py b/qt/pe/results_model.py index 6e0733f3..70786071 100644 --- a/qt/pe/results_model.py +++ b/qt/pe/results_model.py @@ -16,6 +16,7 @@ class ResultsModel(ResultsModelBase): Column('size', defaultWidth=60), Column('extension', defaultWidth=40), Column('dimensions', defaultWidth=100), + Column('exif_timestamp', defaultWidth=120), Column('mtime', defaultWidth=120), Column('percentage', defaultWidth=60), Column('dupe_count', defaultWidth=80),