1
0
mirror of https://github.com/arsenetar/dupeguru.git synced 2026-01-25 08:01:39 +00:00

Compare commits

...

8 Commits

25 changed files with 128 additions and 119 deletions

View File

@@ -48,3 +48,4 @@ ff43c6d9feb388f103b7857eaa6f7809185f78ec before-pluginbuilder
d274bcb98f2d02b86470a04cd62e718eff33b74f pe2.1.0
77e169f757195c11e9c1c5febeb2db8eb3589510 se3.0.2
97893f37d7d0767b5aedf1b4b40de57ee36d426b se3.1.0
e44d5127ed605daa7a17a01eee65d0a157de20c0 pe2.2.0

View File

@@ -24,17 +24,15 @@ def parse_args():
parser = OptionParser(usage=usage)
parser.add_option('--clean', action='store_true', dest='clean',
help="Clean build folder before building")
parser.add_option('--only-help', action='store_true', dest='only_help',
help="Build only help file")
parser.add_option('--doc', action='store_true', dest='doc',
help="Build only the help file")
parser.add_option('--loc', action='store_true', dest='loc',
help="Build only localization")
(options, args) = parser.parse_args()
return options
def build_cocoa(edition, dev):
from pluginbuilder import build_plugin
build_all_cocoa_locs('cocoalib')
build_all_cocoa_locs(op.join('cocoa', 'base'))
build_all_cocoa_locs(op.join('cocoa', edition))
print("Building dg_cocoa.plugin")
if not dev:
specific_packages = {
@@ -83,8 +81,6 @@ def build_cocoa(edition, dev):
open('run.py', 'wt').write(run_contents)
def build_qt(edition, dev):
print("Building .ts files")
build_all_qt_locs(op.join('qt', 'lang'), extradirs=[op.join('qtlib', 'lang')])
print("Building Qt stuff")
print_and_do("pyrcc4 -py3 {0} > {1}".format(op.join('qt', 'base', 'dg.qrc'), op.join('qt', 'base', 'dg_rc.py')))
print("Creating the run.py file")
@@ -104,6 +100,16 @@ def build_help(edition):
confrepl = {'edition': edition, 'appname': appname, 'homepage': homepage}
sphinxgen.gen(help_basepath, help_destpath, changelog_path, tixurl, confrepl)
def build_localizations(ui, edition):
print("Building localizations")
if ui == 'cocoa':
build_all_cocoa_locs('cocoalib')
build_all_cocoa_locs(op.join('cocoa', 'base'))
build_all_cocoa_locs(op.join('cocoa', edition))
elif ui == 'qt':
print("Building .ts files")
build_all_qt_locs(op.join('qt', 'lang'), extradirs=[op.join('qtlib', 'lang')])
def build_pe_modules(ui):
def move(src, dst):
if not op.exists(src):
@@ -144,6 +150,7 @@ def build_normal(edition, ui, dev):
print("Building dupeGuru {0} with UI {1}".format(edition.upper(), ui))
add_to_pythonpath('.')
build_help(edition)
build_localizations(ui, edition)
print("Building dupeGuru")
if edition == 'pe':
build_pe_modules(ui)
@@ -165,8 +172,10 @@ def main():
shutil.rmtree('build')
if not op.exists('build'):
os.mkdir('build')
if options.only_help:
if options.doc:
build_help(edition)
elif options.loc:
build_localizations(ui, edition)
else:
build_normal(edition, ui, dev)

View File

@@ -2,7 +2,7 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10J869</string>
<string key="IBDocument.SystemVersion">10J4138</string>
<string key="IBDocument.InterfaceBuilderVersion">851</string>
<string key="IBDocument.AppKitVersion">1038.35</string>
<string key="IBDocument.HIToolboxVersion">461.00</string>
@@ -34,10 +34,6 @@
<string key="NSClassName">NSApplication</string>
</object>
<object class="NSUserDefaultsController" id="455472712">
<object class="NSMutableArray" key="NSDeclaredKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>scanType</string>
</object>
<bool key="NSSharedInstance">YES</bool>
</object>
<object class="NSWindowTemplate" id="809668081">
@@ -232,7 +228,7 @@
<object class="NSButtonCell" key="NSCell" id="911281323">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Match scaled pictures together</string>
<string key="NSContents">Match pictures of different dimensions</string>
<reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="722670516"/>
<int key="NSButtonFlags">1211912703</int>
@@ -289,7 +285,7 @@
<object class="NSTextField" id="403531548">
<reference key="NSNextResponder" ref="1073354031"/>
<int key="NSvFlags">256</int>
<string key="NSFrame">{{301, 145}, {31, 14}}</string>
<string key="NSFrame">{{301, 112}, {31, 14}}</string>
<reference key="NSSuperview" ref="1073354031"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="983190380">
@@ -386,7 +382,7 @@
<object class="NSPopUpButton" id="337614813">
<reference key="NSNextResponder" ref="1073354031"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{113, 135}, {216, 26}}</string>
<string key="NSFrame">{{113, 135}, {219, 26}}</string>
<reference key="NSSuperview" ref="1073354031"/>
<bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="697629846">
@@ -988,26 +984,6 @@
</object>
<int key="connectionID">98</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">enabled: values.scanType</string>
<reference key="source" ref="722670516"/>
<reference key="destination" ref="455472712"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="722670516"/>
<reference key="NSDestination" ref="455472712"/>
<string key="NSLabel">enabled: values.scanType</string>
<string key="NSBinding">enabled</string>
<string key="NSKeyPath">values.scanType</string>
<object class="NSDictionary" key="NSOptions">
<string key="NS.key.0">NSValueTransformerName</string>
<string key="NS.object.0">vtScanTypeIsFuzzy</string>
</object>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">100</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -1124,7 +1100,6 @@
<reference key="object" ref="1073354031"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="403531548"/>
<reference ref="266372855"/>
<reference ref="869007847"/>
<reference ref="171701149"/>
@@ -1135,6 +1110,7 @@
<reference ref="472028782"/>
<reference ref="337614813"/>
<reference ref="536472926"/>
<reference ref="403531548"/>
</object>
<reference key="parent" ref="700068878"/>
</object>
@@ -1466,6 +1442,7 @@
<string>10.IBViewBoundsToFrameTransform</string>
<string>10.ImportedFromIB2</string>
<string>11.IBPluginDependency</string>
<string>11.IBViewBoundsToFrameTransform</string>
<string>11.ImportedFromIB2</string>
<string>12.IBPluginDependency</string>
<string>12.IBViewBoundsToFrameTransform</string>
@@ -1577,6 +1554,9 @@
</object>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAABDloAAwxAAAA</bytes>
</object>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<object class="NSAffineTransform">
@@ -1606,9 +1586,9 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{88, 591}, {392, 254}}</string>
<string>{{413, 591}, {392, 254}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{88, 591}, {392, 254}}</string>
<string>{{413, 591}, {392, 254}}</string>
<boolean value="YES"/>
<boolean value="YES"/>
<string>{213, 107}</string>

View File

@@ -29,7 +29,7 @@
/* Class = "NSMenuItem"; title = "Right in destination"; ObjectID = "30"; */
"30.title" = "Directement à la destination";
/* Class = "NSButtonCell"; title = "Match scaled pictures together"; ObjectID = "31"; */
/* Class = "NSButtonCell"; title = "Match pictures of different dimensions"; ObjectID = "31"; */
"31.title" = "Comparer les images de tailles différentes";
/* Class = "NSButtonCell"; title = "Automatically check for updates"; ObjectID = "32"; */

View File

@@ -2,7 +2,7 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10J869</string>
<string key="IBDocument.SystemVersion">10J4138</string>
<string key="IBDocument.InterfaceBuilderVersion">851</string>
<string key="IBDocument.AppKitVersion">1038.35</string>
<string key="IBDocument.HIToolboxVersion">461.00</string>
@@ -209,7 +209,7 @@
<reference key="NSControlView" ref="488256664"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<object class="NSCustomResource" key="NSNormalImage" id="596695557">
<object class="NSCustomResource" key="NSNormalImage" id="949163782">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSSwitch</string>
</object>
@@ -236,7 +236,7 @@
<reference key="NSControlView" ref="722670516"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSNormalImage" ref="596695557"/>
<reference key="NSNormalImage" ref="949163782"/>
<reference key="NSAlternateImage" ref="990345653"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
@@ -258,7 +258,7 @@
<reference key="NSControlView" ref="472028782"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSNormalImage" ref="596695557"/>
<reference key="NSNormalImage" ref="949163782"/>
<reference key="NSAlternateImage" ref="990345653"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
@@ -280,7 +280,7 @@
<reference key="NSControlView" ref="279087998"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSNormalImage" ref="596695557"/>
<reference key="NSNormalImage" ref="949163782"/>
<reference key="NSAlternateImage" ref="990345653"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
@@ -291,7 +291,7 @@
<object class="NSTextField" id="403531548">
<reference key="NSNextResponder" ref="1073354031"/>
<int key="NSvFlags">256</int>
<string key="NSFrame">{{301, 145}, {31, 14}}</string>
<string key="NSFrame">{{301, 112}, {31, 14}}</string>
<reference key="NSSuperview" ref="1073354031"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="983190380">
@@ -388,7 +388,7 @@
<object class="NSPopUpButton" id="337614813">
<reference key="NSNextResponder" ref="1073354031"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{113, 135}, {216, 26}}</string>
<string key="NSFrame">{{113, 135}, {219, 26}}</string>
<reference key="NSSuperview" ref="1073354031"/>
<bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="697629846">
@@ -479,7 +479,7 @@
<reference key="NSControlView" ref="1018598123"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSNormalImage" ref="596695557"/>
<reference key="NSNormalImage" ref="949163782"/>
<reference key="NSAlternateImage" ref="990345653"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
@@ -501,7 +501,7 @@
<reference key="NSControlView" ref="519470955"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSNormalImage" ref="596695557"/>
<reference key="NSNormalImage" ref="949163782"/>
<reference key="NSAlternateImage" ref="990345653"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
@@ -523,7 +523,7 @@
<reference key="NSControlView" ref="606836304"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSNormalImage" ref="596695557"/>
<reference key="NSNormalImage" ref="949163782"/>
<reference key="NSAlternateImage" ref="990345653"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
@@ -992,26 +992,6 @@
</object>
<int key="connectionID">98</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">enabled: values.scanType</string>
<reference key="source" ref="722670516"/>
<reference key="destination" ref="455472712"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="722670516"/>
<reference key="NSDestination" ref="455472712"/>
<string key="NSLabel">enabled: values.scanType</string>
<string key="NSBinding">enabled</string>
<string key="NSKeyPath">values.scanType</string>
<object class="NSDictionary" key="NSOptions">
<string key="NS.key.0">NSValueTransformerName</string>
<string key="NS.object.0">vtScanTypeIsFuzzy</string>
</object>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">100</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -1128,7 +1108,6 @@
<reference key="object" ref="1073354031"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="403531548"/>
<reference ref="266372855"/>
<reference ref="869007847"/>
<reference ref="171701149"/>
@@ -1139,6 +1118,7 @@
<reference ref="472028782"/>
<reference ref="337614813"/>
<reference ref="536472926"/>
<reference ref="403531548"/>
</object>
<reference key="parent" ref="700068878"/>
</object>
@@ -1470,6 +1450,7 @@
<string>10.IBViewBoundsToFrameTransform</string>
<string>10.ImportedFromIB2</string>
<string>11.IBPluginDependency</string>
<string>11.IBViewBoundsToFrameTransform</string>
<string>11.ImportedFromIB2</string>
<string>12.IBPluginDependency</string>
<string>12.IBViewBoundsToFrameTransform</string>
@@ -1582,6 +1563,9 @@
</object>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAABDloAAwxAAAA</bytes>
</object>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<object class="NSAffineTransform">
@@ -1611,9 +1595,9 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{88, 591}, {392, 254}}</string>
<string>{{413, 591}, {392, 254}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{88, 591}, {392, 254}}</string>
<string>{{413, 591}, {392, 254}}</string>
<boolean value="YES"/>
<boolean value="YES"/>
<string>{1.79769e+308, 1.79769e+308}</string>

View File

@@ -92,10 +92,13 @@ class DupeGuru(RegistrableApplication, Broadcaster):
logging.warning("Exception on GetDisplayInfo for %s: %s", str(dupe.path), str(e))
return ['---'] * len(self.data.COLUMNS)
def _create_file(self, path):
# We add fs.Folder to fileclasses in case the file we're loading contains folder paths.
return fs.get_file(path, self.directories.fileclasses + [fs.Folder])
def _get_file(self, str_path):
path = Path(str_path)
# We add fs.Folder to fileclasses in case the file we're loading contains folder paths.
f = fs.get_file(path, self.directories.fileclasses + [fs.Folder])
f = self._create_file(path)
if f is None:
return None
try:

View File

@@ -14,9 +14,6 @@ import time
Column = namedtuple('Column', 'attr display')
def format_path(p):
return str(p[:-1])
def format_timestamp(t, delta):
if delta:
return format_time_decimal(t)

View File

@@ -149,6 +149,10 @@ class File:
def name(self):
return self.path[-1]
@property
def folder_path(self):
return self.path[:-1]
class Folder(File):
"""A wrapper around a folder path.

View File

@@ -328,15 +328,13 @@ class Results(Markable):
def sort_dupes(self, key, asc=True, delta=False):
if not self.__dupes:
self.__get_dupe_list()
self.__dupes.sort(key=lambda d: self.data.GetDupeSortKey(d, lambda: self.get_group_of_duplicate(d), key, delta))
if not asc:
self.__dupes.reverse()
keyfunc = lambda d: self.data.GetDupeSortKey(d, lambda: self.get_group_of_duplicate(d), key, delta)
self.__dupes.sort(key=keyfunc, reverse=not asc)
self.__dupes_sort_descriptor = (key,asc,delta)
def sort_groups(self,key,asc=True):
self.groups.sort(key=lambda g: self.data.GetGroupSortKey(g, key))
if not asc:
self.groups.reverse()
keyfunc = lambda g: self.data.GetGroupSortKey(g, key)
self.groups.sort(key=keyfunc, reverse=not asc)
self.__groups_sort_descriptor = (key,asc)
#---Properties

View File

@@ -9,11 +9,11 @@
# data module for tests
from hscommon.util import format_size
from ..data import format_path, cmp_value, Column
from ..data import cmp_value, Column
COLUMNS = [
Column('name', 'Filename'),
Column('path', 'Directory'),
Column('folder_path', 'Directory'),
Column('size', 'Size (KB)'),
Column('extension', 'Kind'),
]
@@ -29,7 +29,7 @@ def GetDisplayInfo(dupe, group, delta):
size -= r.size
return [
dupe.name,
format_path(dupe.path),
str(dupe.folder_path),
format_size(size, 0, 1, False),
dupe.extension if hasattr(dupe, 'extension') else '---',
]

View File

@@ -26,6 +26,11 @@ class NamedObject(engine_test.NamedObject):
def __bool__(self):
return False #Make sure that operations are made correctly when the bool value of files is false.
@property
def folder_path(self):
return self.path[:-1]
# Returns a group set that looks like that:
# "foo bar" (1)

View File

@@ -8,14 +8,14 @@
from hscommon.util import format_time, format_size
from hscommon.trans import tr as trbase
from core.data import (format_path, format_timestamp, format_words, format_perc,
format_dupe_count, cmp_value, Column)
from core.data import (format_timestamp, format_words, format_perc, format_dupe_count, cmp_value,
Column)
tr = lambda s: trbase(s, 'columns')
COLUMNS = [
Column('name', tr("Filename")),
Column('path', tr("Folder")),
Column('folder_path', tr("Folder")),
Column('size', tr("Size (MB)")),
Column('duration', tr("Time")),
Column('bitrate', tr("Bitrate")),
@@ -63,7 +63,7 @@ def GetDisplayInfo(dupe, group, delta):
dupe_count = len(group.dupes)
return [
dupe.name,
format_path(dupe.path),
str(dupe.folder_path),
format_size(size, 2, 2, False),
format_time(duration, with_hours=False),
str(bitrate),

View File

@@ -1,2 +1,2 @@
__version__ = '2.2.0'
__version__ = '2.2.1'
__appname__ = 'dupeGuru Picture Edition'

View File

@@ -28,7 +28,7 @@ IPHOTO_PATH = Path('iPhoto Library')
class Photo(PhotoBase):
HANDLED_EXTS = PhotoBase.HANDLED_EXTS.copy()
HANDLED_EXTS.update({'psd', 'nef', 'cr2'})
HANDLED_EXTS.update({'psd', 'nef', 'cr2', 'orf'})
def _plat_get_dimensions(self):
return _block_osx.get_image_size(str(self.path))
@@ -45,8 +45,8 @@ class Photo(PhotoBase):
class IPhoto(Photo):
@property
def display_path(self):
return Path(('iPhoto Library', self.name))
def display_folder_path(self):
return IPHOTO_PATH
def get_iphoto_database_path():
ud = NSUserDefaults.standardUserDefaults()
@@ -166,11 +166,10 @@ class DupeGuruPE(app_cocoa.DupeGuru):
else:
app_cocoa.DupeGuru._do_delete_dupe(self, dupe, replace_with_hardlinks)
def _get_file(self, str_path):
p = Path(str_path)
if (self.directories.iphoto_libpath is not None) and (p in self.directories.iphoto_libpath[:-1]):
return IPhoto(p)
return app_cocoa.DupeGuru._get_file(self, str_path)
def _create_file(self, path):
if (self.directories.iphoto_libpath is not None) and (path in self.directories.iphoto_libpath[:-1]):
return IPhoto(path)
return app_cocoa.DupeGuru._create_file(self, path)
def copy_or_move(self, dupe, copy, destination, dest_type):
if isinstance(dupe, IPhoto):

View File

@@ -8,7 +8,7 @@
from hscommon.util import format_size
from hscommon.trans import tr as trbase
from core.data import format_path, format_timestamp, format_perc, format_dupe_count, cmp_value, Column
from core.data import format_timestamp, format_perc, format_dupe_count, cmp_value, Column
tr = lambda s: trbase(s, 'columns')
@@ -17,7 +17,7 @@ def format_dimensions(dimensions):
COLUMNS = [
Column('name', tr("Filename")),
Column('path', tr("Folder")),
Column('folder_path', tr("Folder")),
Column('size', tr("Size (KB)")),
Column('extension', tr("Kind")),
Column('dimensions', tr("Dimensions")),
@@ -26,6 +26,7 @@ COLUMNS = [
Column('dupe_count', tr("Dupe Count")),
]
FOLDER_COL = 1
MATCHPERC_COL = 6
DUPECOUNT_COL = 7
DELTA_COLUMNS = {2, 4, 5}
@@ -53,10 +54,10 @@ def GetDisplayInfo(dupe,group,delta=False):
else:
percentage = group.percentage
dupe_count = len(group.dupes)
dupe_path = getattr(dupe, 'display_path', dupe.path)
dupe_folder_path = getattr(dupe, 'display_folder_path', dupe.folder_path)
return [
dupe.name,
format_path(dupe_path),
str(dupe_folder_path),
format_size(size, 0, 1, False),
dupe.extension,
format_dimensions(dimensions),
@@ -71,6 +72,9 @@ def GetDupeSortKey(dupe, get_group, key, delta):
return m.percentage
if key == DUPECOUNT_COL:
return 0
if key == FOLDER_COL:
dupe_folder_path = getattr(dupe, 'display_folder_path', dupe.folder_path)
return cmp_value(str(dupe_folder_path))
r = cmp_value(getattr(dupe, COLUMNS[key].attr, ''))
if delta and (key in DELTA_COLUMNS):
ref_value = cmp_value(getattr(get_group().ref, COLUMNS[key].attr, ''))
@@ -85,5 +89,8 @@ def GetGroupSortKey(group, key):
return group.percentage
if key == DUPECOUNT_COL:
return len(group)
if key == FOLDER_COL:
dupe_folder_path = getattr(group.ref, 'display_folder_path', group.ref.folder_path)
return cmp_value(str(dupe_folder_path))
return cmp_value(getattr(group.ref, COLUMNS[key].attr, ''))

View File

@@ -154,6 +154,12 @@ GPS_TA0GS = {
INTEL_ENDIAN = ord('I')
MOTOROLA_ENDIAN = ord('M')
# About MAX_COUNT: It's possible to have corrupted exif tags where the entry count is way too high
# and thus makes us loop, not endlessly, but for heck of a long time for nothing. Therefore, we put
# an arbitrary limit on the entry count we'll allow ourselves to read and any IFD reporting more
# entries than that will be considered corrupt.
MAX_COUNT = 0xffff
def s2n_motorola(bytes):
x = 0
for c in bytes:
@@ -214,6 +220,9 @@ class TIFF_file:
def dump_IFD(self, ifd):
entries = self.s2n(ifd, 2)
logging.debug("Entries for IFD %d: %d", ifd, entries)
if entries > MAX_COUNT:
logging.debug("Probably corrupt. Aborting.")
return []
a = []
for i in range(entries):
entry = ifd + 2 + 12*i
@@ -223,6 +232,9 @@ class TIFF_file:
continue # not handled
typelen = [ 1, 1, 2, 4, 8, 1, 1, 2, 4, 8 ] [type-1]
count = self.s2n(entry+4, 4)
if count > MAX_COUNT:
logging.debug("Probably corrupt. Aborting.")
return []
offset = entry+8
if count*typelen > 4:
offset = self.s2n(offset, 4)

View File

@@ -16,7 +16,7 @@ from hscommon.trans import tr
from core.engine import Match
from . import exif
def getmatches(files, j):
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:
@@ -30,5 +30,8 @@ def getmatches(files, j):
del timestamp2pic['0000:00:00 00:00:00']
matches = []
for pictures in timestamp2pic.values():
matches += [Match(p1, p2, 100) for p1, p2 in combinations(pictures, 2)]
for p1, p2 in combinations(pictures, 2):
if (not match_scaled) and (p1.dimensions != p2.dimensions):
continue
matches.append(Match(p1, p2, 100))
return matches

View File

@@ -20,7 +20,7 @@ class ScannerPE(Scanner):
if self.scan_type == ScanType.FuzzyBlock:
return matchblock.getmatches(files, self.cache_path, self.threshold, self.match_scaled, j)
elif self.scan_type == ScanType.ExifTimestamp:
return matchexif.getmatches(files, j)
return matchexif.getmatches(files, self.match_scaled, j)
else:
raise Exception("Invalid scan type")

View File

@@ -8,14 +8,14 @@
from hscommon.util import format_size
from hscommon.trans import tr as trbase
from core.data import (format_path, format_timestamp, format_words, format_perc,
format_dupe_count, cmp_value, Column)
from core.data import (format_timestamp, format_words, format_perc, format_dupe_count, cmp_value,
Column)
tr = lambda s: trbase(s, 'columns')
COLUMNS = [
Column('name', tr("Filename")),
Column('path', tr("Folder")),
Column('folder_path', tr("Folder")),
Column('size', tr("Size (KB)")),
Column('extension', tr("Kind")),
Column('mtime', tr("Modification")),
@@ -46,7 +46,7 @@ def GetDisplayInfo(dupe, group, delta):
dupe_count = len(group.dupes)
return [
dupe.name,
format_path(dupe.path),
str(dupe.folder_path),
format_size(size, 0, 1, False),
dupe.extension,
format_timestamp(mtime, delta and m),

View File

@@ -1,3 +1,11 @@
=== 2.2.1 (2011-06-15)
* Brought back the filter hardness label which disappeared in 2.2.0. [Mac OS X] (#164)
* Added support for Olympus RAW files (.orf). [Mac OS X] (#159)
* Fixed a bug causing the picture analysis to stall on some corrupt EXIF data.
* Make the "Match scaled pictures" option apply to the new EXIF timestamp scan. (#162)
* Fixed glitch in the sorting feature of the Folder column. (#161)
=== 2.2.0 (2011-06-02)
* Added the EXIF Timestamp scan type.

View File

@@ -32,11 +32,11 @@ Preferences
.. only:: edition_pe
**Scan Type:** This option determines the type of scan that will be made on your pictures. The **Contents** scan type compares the actual contents of the pictures in a fuzzy way (making it possible to find not only exact duplicates, but also similar ones). The **EXIF Timestamp** scan type looks at the EXIF metadata of the picture (if it exists) and matches pictures that have the same one. It's much faster than the Contents scan.
**Scan Type:** This option determines the type of scan that will be made on your pictures. The **Contents** scan type compares the actual contents of the pictures in a fuzzy way (making it possible to find not only exact duplicates, but also similar ones). The **EXIF Timestamp** scan type looks at the EXIF metadata of the picture (if it exists) and matches pictures that have the same one. It's much faster than the Contents scan. **Warning:** Modified pictures often keep the same EXIF timestamp, so watch out for false positives when you use that scan type.
**Filter Hardness:** *Contents scan type only.* The higher is this setting, the "harder" is the filter (In other words, the less results you get). Most pictures of the same quality match at 100% even if the format is different (PNG and JPG for example.). However, if you want to make a PNG match with a lower quality JPG, you will have to set the filer hardness to lower than 100. The default, 95, is a sweet spot.
**Match scaled pictures together:** *Contents scan type only.* If you check this box, pictures of different dimensions will be allowed in the same duplicate group.
**Match pictures of different dimensions:** If you check this box, pictures of different dimensions will be allowed in the same duplicate group.
**Can mix file kind:** If you check this box, duplicate groups are allowed to have files with different extensions. If you don't check it, well, they aren't!

View File

@@ -32,11 +32,11 @@ Préférences
.. only:: edition_pe
**Type de scan:** Détermine le type de scan qui sera fait sur vos images. Le type **Contenu** compare le contenu des images de façon "fuzzy", rendant possible de trouver non seulement les doublons exactes, mais aussi les similaires. Le type **EXIF Timestamp** compare les métadonnées EXIF des images (si existantes) et détermine si le "timestamp" (moment de prise de la photo) est pareille. C'est beaucoup plus rapide que le scan par Contenu.
**Type de scan:** Détermine le type de scan qui sera fait sur vos images. Le type **Contenu** compare le contenu des images de façon "fuzzy", rendant possible de trouver non seulement les doublons exactes, mais aussi les similaires. Le type **EXIF Timestamp** compare les métadonnées EXIF des images (si existantes) et détermine si le "timestamp" (moment de prise de la photo) est pareille. C'est beaucoup plus rapide que le scan par Contenu. **Attention:** Les photos modifiées gardent souvent le même timestamp, donc faites attention aux faux doublons si vous utilisez cette méthode.
**Seuil du filtre:** *Scan par Contenu seulement.* Plus il est élevé, plus les images doivent être similaires pour être considérées comme des doublons. Le défaut de 95% permet quelques petites différence, comme par exemple une différence de qualité ou bien une légère modification des couleurs.
**Comparer les images de tailles différentes:** *Scan par Contenu seulement.* Le nom dit tout. Sans cette option, les images de tailles différentes ne sont pas comparées.
**Comparer les images de tailles différentes:** Le nom dit tout. Sans cette option, les images de tailles différentes ne sont pas comparées.
**Comparer les fichiers de différents types:** Sans cette option, seulement les fichiers du même type seront comparés.

View File

@@ -530,7 +530,7 @@
<translation>Année</translation>
</message>
<message>
<source>Match scaled pictures together</source>
<source>Match pictures of different dimensions</source>
<translation>Comparer les images de tailles différentes</translation>
</message>
<message>

View File

@@ -32,7 +32,7 @@ class PreferencesDialog(PreferencesDialogBase):
self._setupScanTypeBox(scanTypeLabels)
self._setupFilterHardnessBox()
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
self._setupAddCheckbox('matchScaledBox', tr("Match scaled pictures together"))
self._setupAddCheckbox('matchScaledBox', tr("Match pictures of different dimensions"))
self.widgetsVLayout.addWidget(self.matchScaledBox)
self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind"))
self.widgetsVLayout.addWidget(self.mixFileKindBox)
@@ -63,7 +63,6 @@ class PreferencesDialog(PreferencesDialogBase):
scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()]
fuzzy_scan = scan_type == ScanType.FuzzyBlock
self.filterHardnessSlider.setEnabled(fuzzy_scan)
self.matchScaledBox.setEnabled(fuzzy_scan)
if __name__ == '__main__':