mirror of
https://github.com/arsenetar/dupeguru.git
synced 2025-03-10 05:34:36 +00:00
[#201 state:fixed] Added an EXIF Timestamp column in PE.
This commit is contained in:
parent
20320f539f
commit
75b08125c0
@ -22,6 +22,7 @@ http://www.hardcoded.net/licenses/bsd_license
|
|||||||
{@"size", 63, 16, 0, YES, nil},
|
{@"size", 63, 16, 0, YES, nil},
|
||||||
{@"extension", 40, 16, 0, YES, nil},
|
{@"extension", 40, 16, 0, YES, nil},
|
||||||
{@"dimensions", 73, 16, 0, YES, nil},
|
{@"dimensions", 73, 16, 0, YES, nil},
|
||||||
|
{@"exif_timestamp", 120, 16, 0, YES, nil},
|
||||||
{@"mtime", 120, 16, 0, YES, nil},
|
{@"mtime", 120, 16, 0, YES, nil},
|
||||||
{@"percentage", 58, 16, 0, YES, nil},
|
{@"percentage", 58, 16, 0, YES, nil},
|
||||||
{@"dupe_count", 80, 16, 0, YES, nil},
|
{@"dupe_count", 80, 16, 0, YES, nil},
|
||||||
|
@ -24,7 +24,7 @@ def get_delta_dimensions(value, ref_value):
|
|||||||
|
|
||||||
class DupeGuru(DupeGuruBase):
|
class DupeGuru(DupeGuruBase):
|
||||||
NAME = __appname__
|
NAME = __appname__
|
||||||
METADATA_TO_READ = ['size', 'mtime', 'dimensions']
|
METADATA_TO_READ = ['size', 'mtime', 'dimensions', 'exif_timestamp']
|
||||||
|
|
||||||
def __init__(self, view, appdata):
|
def __init__(self, view, appdata):
|
||||||
DupeGuruBase.__init__(self, view, appdata)
|
DupeGuruBase.__init__(self, view, appdata)
|
||||||
@ -54,6 +54,7 @@ class DupeGuru(DupeGuruBase):
|
|||||||
'size': format_size(size, 0, 1, False),
|
'size': format_size(size, 0, 1, False),
|
||||||
'extension': dupe.extension,
|
'extension': dupe.extension,
|
||||||
'dimensions': format_dimensions(dimensions),
|
'dimensions': format_dimensions(dimensions),
|
||||||
|
'exif_timestamp': dupe.exif_timestamp,
|
||||||
'mtime': format_timestamp(mtime, delta and m),
|
'mtime': format_timestamp(mtime, delta and m),
|
||||||
'percentage': format_perc(percentage),
|
'percentage': format_perc(percentage),
|
||||||
'dupe_count': format_dupe_count(dupe_count),
|
'dupe_count': format_dupe_count(dupe_count),
|
||||||
|
@ -6,26 +6,18 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/bsd_license
|
# http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
|
||||||
import logging
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from itertools import combinations
|
from itertools import combinations
|
||||||
|
|
||||||
from hscommon import io
|
|
||||||
from hscommon.trans import tr
|
from hscommon.trans import tr
|
||||||
|
|
||||||
from core.engine import Match
|
from core.engine import Match
|
||||||
from . import exif
|
|
||||||
|
|
||||||
def getmatches(files, match_scaled, j):
|
def getmatches(files, match_scaled, j):
|
||||||
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")):
|
||||||
try:
|
timestamp = picture.exif_timestamp
|
||||||
with io.open(picture.path, 'rb') as fp:
|
|
||||||
exifdata = exif.get_fields(fp)
|
|
||||||
timestamp = exifdata['DateTimeOriginal']
|
|
||||||
timestamp2pic[timestamp].add(picture)
|
timestamp2pic[timestamp].add(picture)
|
||||||
except Exception:
|
|
||||||
logging.info("Couldn't read EXIF of picture: %s", picture.path)
|
|
||||||
if '0000:00:00 00:00:00' in timestamp2pic: # very likely false matches
|
if '0000:00:00 00:00:00' in timestamp2pic: # very likely false matches
|
||||||
del timestamp2pic['0000:00:00 00:00:00']
|
del timestamp2pic['0000:00:00 00:00:00']
|
||||||
matches = []
|
matches = []
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/bsd_license
|
# http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
|
||||||
from hscommon import io
|
import logging
|
||||||
from hscommon.util import get_file_ext
|
from hscommon.util import get_file_ext
|
||||||
from core import fs
|
from core import fs
|
||||||
from . import exif
|
from . import exif
|
||||||
@ -15,6 +15,7 @@ class Photo(fs.File):
|
|||||||
INITIAL_INFO = fs.File.INITIAL_INFO.copy()
|
INITIAL_INFO = fs.File.INITIAL_INFO.copy()
|
||||||
INITIAL_INFO.update({
|
INITIAL_INFO.update({
|
||||||
'dimensions': (0,0),
|
'dimensions': (0,0),
|
||||||
|
'exif_timestamp': '',
|
||||||
})
|
})
|
||||||
__slots__ = fs.File.__slots__ + tuple(INITIAL_INFO.keys())
|
__slots__ = fs.File.__slots__ + tuple(INITIAL_INFO.keys())
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ class Photo(fs.File):
|
|||||||
def _get_orientation(self):
|
def _get_orientation(self):
|
||||||
if not hasattr(self, '_cached_orientation'):
|
if not hasattr(self, '_cached_orientation'):
|
||||||
try:
|
try:
|
||||||
with io.open(self.path, 'rb') as fp:
|
with self.path.open('rb') as fp:
|
||||||
exifdata = exif.get_fields(fp)
|
exifdata = exif.get_fields(fp)
|
||||||
# the value is a list (probably one-sized) of ints
|
# the value is a list (probably one-sized) of ints
|
||||||
orientations = exifdata['Orientation']
|
orientations = exifdata['Orientation']
|
||||||
@ -49,6 +50,13 @@ class Photo(fs.File):
|
|||||||
self.dimensions = self._plat_get_dimensions()
|
self.dimensions = self._plat_get_dimensions()
|
||||||
if self._get_orientation() in {5, 6, 7, 8}:
|
if self._get_orientation() in {5, 6, 7, 8}:
|
||||||
self.dimensions = (self.dimensions[1], self.dimensions[0])
|
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):
|
def get_blocks(self, block_count_per_side):
|
||||||
return self._plat_get_blocks(block_count_per_side, self._get_orientation())
|
return self._plat_get_blocks(block_count_per_side, self._get_orientation())
|
||||||
|
@ -20,6 +20,7 @@ class ResultTable(ResultTableBase):
|
|||||||
Column('size', coltr("Size (KB)"), optional=True),
|
Column('size', coltr("Size (KB)"), optional=True),
|
||||||
Column('extension', coltr("Kind"), visible=False, optional=True),
|
Column('extension', coltr("Kind"), visible=False, optional=True),
|
||||||
Column('dimensions', coltr("Dimensions"), 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('mtime', coltr("Modification"), visible=False, optional=True),
|
||||||
Column('percentage', coltr("Match %"), optional=True),
|
Column('percentage', coltr("Match %"), optional=True),
|
||||||
Column('dupe_count', coltr("Dupe Count"), visible=False, optional=True),
|
Column('dupe_count', coltr("Dupe Count"), visible=False, optional=True),
|
||||||
|
@ -16,6 +16,7 @@ class ResultsModel(ResultsModelBase):
|
|||||||
Column('size', defaultWidth=60),
|
Column('size', defaultWidth=60),
|
||||||
Column('extension', defaultWidth=40),
|
Column('extension', defaultWidth=40),
|
||||||
Column('dimensions', defaultWidth=100),
|
Column('dimensions', defaultWidth=100),
|
||||||
|
Column('exif_timestamp', defaultWidth=120),
|
||||||
Column('mtime', defaultWidth=120),
|
Column('mtime', defaultWidth=120),
|
||||||
Column('percentage', defaultWidth=60),
|
Column('percentage', defaultWidth=60),
|
||||||
Column('dupe_count', defaultWidth=80),
|
Column('dupe_count', defaultWidth=80),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user