leeh-mini_exiftool 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,159 @@
1
+ 1.6.0
2
+ - Type conversion in MiniExiftool.from_hash.
3
+ Thanks to Ethan Soutar-Rau for the merge request.
4
+ - Switching to rim. (No longer troubles with echoe.)
5
+ - Exiftool version detection delayed.
6
+ Thanks to Sebastian Skałacki for the merge request.
7
+ - New method MiniExiftool#save!
8
+ Cherry-picked commit from Wil Gieseler.
9
+
10
+ 1.5.1
11
+ - Make rational values work on Ruby 1.8.7.
12
+
13
+ 1.5.0
14
+ - Supporting exiftool command-line option -m.
15
+ rubyforge request [#29587]
16
+ Thanks to Michael Grove for reporting.
17
+ - Supporting rational values.
18
+
19
+ 1.4.4
20
+ - Fix escaping of values for older versions of Shellwords.
21
+
22
+ 1.4.3
23
+ - Fixing rubyforge bug [#29596] (Quotes in values)
24
+ Thanks to Michael Grove for reporting
25
+
26
+ 1.4.2
27
+ - Add .yardopts file to gem.
28
+
29
+ 1.4.1
30
+ - Update documentation for using yard.
31
+
32
+ 1.4.0
33
+ - Allow symbols for tag access with [].
34
+ - Refactoring tests.
35
+
36
+ 1.3.1
37
+ - Remove TestEscapeFilename test and releating test photo
38
+ because the latter produces errors on windows systems.
39
+ - Version check in prerelease task.
40
+
41
+ 1.3.0
42
+ - MiniExiftool is now ready for Ruby 1.9
43
+ All tests in the test suite pass. :)
44
+
45
+ 1.2.2
46
+ - Fixing ptore directory naming convention for darwin.
47
+ Thanks to Denis Barushev for the hint.
48
+
49
+ 1.2.1
50
+ - Switching to echoe.
51
+ - Update e-mail address.
52
+
53
+ 1.2.0
54
+ - Fixing time zone handling.
55
+ Thanks to ccoenen for the hint.
56
+
57
+ 1.1.0
58
+ - Escaping filenames in shell commands
59
+ Thanks to Michael Hoy for the hint and implementing a patch which was
60
+ the base for this fix.
61
+
62
+ 1.0.2
63
+ - Fixing warings
64
+ Thanks to Peter-Hinrich Krogmann for the hint.
65
+
66
+ 1.0.1
67
+ - Fixing bug [#22726]
68
+ Making MiniExiftool::Error public.
69
+ Thanks to Mathias Stjernstrom for sending a patch.
70
+
71
+ 1.0.0
72
+ - Be aware changing in the interface:
73
+ - List tags (e.g. Keywords, SupplementalCategories) are now handled as
74
+ arrays.
75
+ - Tag SubjectLocation is not longer an array value but a string value!
76
+
77
+ 0.7.0
78
+ - Changing composite behaviour: Composite tags are now included as standard!
79
+ - New method MiniExiftool.opts which returns a hash of the standard
80
+ options used for MiniExiftool.new
81
+ - New option :convert_encoding for MiniExiftool.new which uses the -L-option
82
+ of the exiftool command-line application (see online documentation for it)
83
+ Thanks to Henning Kulander for the causing of this change.
84
+
85
+ 0.6.0
86
+ - New methods for serialization:
87
+ - MiniExiftool.from_hash
88
+ - MiniExiftool.from_yaml
89
+ - MiniExiftool#to_hash
90
+ - MiniExiftool#to_yaml
91
+ Thanks to Andrew Bennett for the initial idea of YAML-serialization
92
+ - Refactoring of tests
93
+ - Small documetation update
94
+
95
+ 0.5.1
96
+ - Warning "parenthesize argument(s) for future version" removed
97
+ Thanks to Greg from knobby.ws
98
+
99
+ 0.5.0
100
+ - New option :timestamps to create DateTime objects instead of Time objects
101
+ for timestamps (Fixing bug #16328)
102
+ - Invalid values of timestamps (i.e. 0000:00:00 00:00:00) are now mapped
103
+ to false
104
+
105
+ 0.4.1
106
+ - Compatibility for Ruby 1.9
107
+
108
+ 0.4.0
109
+ - MiniExiftool::Error inherits now from StandardError
110
+ - Alternative installation via setup.rb
111
+ - Bugfix
112
+ Saving of non-read tags doesn't work with tags with hyphen
113
+ Thanks to Robin Romahn for reporting the bug
114
+ - New methods: MiniExiftool.all_tags and MiniExiftool.original_tag
115
+ - Interna: Original tag names (all and writable) are now saved via pstore in
116
+ a file for better performance
117
+
118
+ 0.3.1
119
+ - Typos fixed
120
+
121
+ 0.3.0
122
+ - Documentation completed and a Mini Tutorial added
123
+ - Interface changes:
124
+ - Test if a value for a tag can be saved is now done in
125
+ MiniExiftool#save
126
+ => There is no check at the moment you set a value:
127
+ the tag occurs in MiniExiftool#changed_values
128
+ => While calling MiniExiftool#save errors can occur (see next point)
129
+ - MiniExiftool#save is a transaction: if one or more error occurs the file is
130
+ not changed! In such a case the errors can be found in MiniExiftool#errors
131
+ - Parameter opts of MiniExiftool.initialize is now a Hash with two options:
132
+ - :numerical => read metadata as numerical values
133
+ - :composite => read also composite tags
134
+ - Tests added
135
+
136
+ 0.2.0
137
+ - Better error handling (i.e. error messages)
138
+ - Checking if the exiftool command can be executed at loading the lib
139
+ - New class method exiftool_version
140
+ - Added tests
141
+ - Documentation completed
142
+
143
+ 0.1.2
144
+ - Bugfix for Windows (Tempfile)
145
+ Thanks to Jérome Soika for testing
146
+ - Regexes optimized (a little bit)
147
+ - New class-method MiniExiftool.writable_tags
148
+
149
+ 0.1.1
150
+ - Fixing bug [#8073]
151
+ Handling the '-' in tag Self-timer
152
+ Thanks to Eric Young
153
+
154
+ 0.1.0
155
+ - New method "revert"
156
+ - More tests
157
+
158
+ 0.0.1
159
+ - Initial release
@@ -0,0 +1,50 @@
1
+ = MiniExiftool
2
+
3
+ This library is wrapper for the Exiftool command-line application
4
+ (http://www.sno.phy.queensu.ca/~phil/exiftool) written by Phil Harvey.
5
+ Read and write access is done in a clean OO manner.
6
+
7
+ == Requirements
8
+
9
+ An installation of the Exiftool command-line application.
10
+ Instructions for installation you can find under
11
+ http://www.sno.phy.queensu.ca/~phil/exiftool/install.html .
12
+
13
+ Alternatively Wil Gieseler has bundled a meta-gem that eliminates the
14
+ need for a seperate Exiftool installation. Have a look at
15
+ http://github.com/wilg/mini_exiftool_vendored or
16
+ http://rubygems.org/gems/mini_exiftool_vendored .
17
+
18
+ == Installation
19
+
20
+ First you need Exiftool (see under Requirements above). Then you can simply
21
+ install the gem with
22
+ gem install mini_exiftool
23
+ respectively (on *nix sytems)
24
+ sudo gem install mini_exiftool
25
+
26
+ Alternative you can download the tarball or zip file and run
27
+ ruby setup.rb config
28
+ ruby setup.rb setup
29
+ sudo ruby setup.rb install
30
+
31
+ == Contribution
32
+
33
+ The code is also hostet in a git repository on Gitorious at
34
+ http://gitorious.org/projects/mini_exiftool
35
+ feel free to contribute!
36
+
37
+ == Author
38
+ Jan Friedrich <janfri26 AT gmail DOT com>
39
+
40
+ == Copyright / License
41
+ Copyright (c) 2007-2011 by Jan Friedrich
42
+
43
+ Licensed under terms of the GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1,
44
+ February 1999 (see file COPYING for more details)
45
+
46
+ == Usage
47
+
48
+ For further information about using MiniExiftool read the
49
+ Tutorial[link://file.Tutorial.html] and have a look at the examples in
50
+ directory examples.
@@ -0,0 +1,23 @@
1
+ require 'rim'
2
+ require 'rim/check_version'
3
+ require 'rim/gem'
4
+ require 'rim/test'
5
+
6
+ $:.unshift 'lib'
7
+ require 'mini_exiftool'
8
+
9
+ Rim.setup do |p|
10
+ p.name = 'leeh-mini_exiftool'
11
+ p.version = MiniExiftool::VERSION
12
+ p.authors = 'Jan Friedrich'
13
+ p.email = 'janfri26@gmail.com'
14
+ p.summary = 'This library is wrapper for the Exiftool command-line application (http://www.sno.phy.queensu.ca/~phil/exiftool).'
15
+ p.homepage = 'http://gitorious.org/mini_exiftool'
16
+ p.install_message = %q{
17
+ +-----------------------------------------------------------------------+
18
+ | Please ensure you have installed exiftool and it's found in your PATH |
19
+ | (Try "exiftool -ver" on your commandline). For more details see |
20
+ | http://www.sno.phy.queensu.ca/~phil/exiftool/install.html |
21
+ +-----------------------------------------------------------------------+
22
+ }
23
+ end
@@ -0,0 +1,464 @@
1
+ # -- encoding: utf-8 --
2
+ #
3
+ # MiniExiftool
4
+ #
5
+ # This library is wrapper for the Exiftool command-line
6
+ # application (http://www.sno.phy.queensu.ca/~phil/exiftool/)
7
+ # written by Phil Harvey.
8
+ # Read and write access is done in a clean OO manner.
9
+ #
10
+ # Author: Jan Friedrich
11
+ # Copyright (c) 2007-2012 by Jan Friedrich
12
+ # Licensed under the GNU LESSER GENERAL PUBLIC LICENSE,
13
+ # Version 2.1, February 1999
14
+ #
15
+
16
+ require 'fileutils'
17
+ require 'tempfile'
18
+ require 'pstore'
19
+ require 'rational'
20
+ require 'set'
21
+ require 'shellwords'
22
+ require 'time'
23
+
24
+ # Simple OO access to the Exiftool command-line application.
25
+ class MiniExiftool
26
+
27
+ # Name of the Exiftool command-line application
28
+ @@cmd = 'exiftool'
29
+
30
+ # Hash of the standard options used when call MiniExiftool.new
31
+ @@opts = { :numerical => false, :composite => true, :convert_encoding => false, :ignore_minor_errors => false, :timestamps => Time }
32
+
33
+ attr_reader :filename
34
+ attr_accessor :numerical, :composite, :convert_encoding, :ignore_minor_errors, :errors, :timestamps
35
+
36
+ VERSION = '1.6.0'
37
+
38
+ # +opts+ support at the moment
39
+ # * <code>:numerical</code> for numerical values, default is +false+
40
+ # * <code>:composite</code> for including composite tags while loading,
41
+ # default is +true+
42
+ # * <code>:convert_encoding</code> convert encoding (See -L-option of
43
+ # the exiftool command-line application, default is +false+)
44
+ # * <code>:ignore_minor_errors</code> ignore minor errors (See -m-option
45
+ # of the exiftool command-line application, default is +false+)
46
+ # * <code>:timestamps</code> generating DateTime objects instead of
47
+ # Time objects if set to <code>DateTime</code>, default is +Time+
48
+ #
49
+ # <b>ATTENTION:</b> Time objects are created using <code>Time.local</code>
50
+ # therefore they use <em>your local timezone</em>, DateTime objects instead
51
+ # are created <em>without timezone</em>!
52
+ def initialize filename=nil, opts={}
53
+ opts = @@opts.merge opts
54
+ @numerical = opts[:numerical]
55
+ @composite = opts[:composite]
56
+ @convert_encoding = opts[:convert_encoding]
57
+ @ignore_minor_errors = opts[:ignore_minor_errors]
58
+ @timestamps = opts[:timestamps]
59
+ @coord_format = opts[:coord_format]
60
+ @values = TagHash.new
61
+ @tag_names = TagHash.new
62
+ @changed_values = TagHash.new
63
+ @errors = TagHash.new
64
+ load filename unless filename.nil?
65
+ end
66
+
67
+ def initialize_from_hash hash # :nodoc:
68
+ hash.each_pair do |tag,value|
69
+ set_value tag, perform_conversions(value)
70
+ end
71
+ set_attributes_by_heuristic
72
+ self
73
+ end
74
+
75
+ # Load the tags of filename.
76
+ def load filename
77
+ MiniExiftool.setup
78
+ unless filename && File.exist?(filename)
79
+ raise MiniExiftool::Error.new("File '#{filename}' does not exist.")
80
+ end
81
+ if File.directory?(filename)
82
+ raise MiniExiftool::Error.new("'#{filename}' is a directory.")
83
+ end
84
+ @filename = filename
85
+ @values.clear
86
+ @tag_names.clear
87
+ @changed_values.clear
88
+ opt_params = ''
89
+ opt_params << (@numerical ? '-n ' : '')
90
+ opt_params << (@composite ? '' : '-e ')
91
+ opt_params << (@convert_encoding ? '-L ' : '')
92
+ opt_params << (@coord_format ? "-c \"#{@coord_format}\"" : '')
93
+ cmd = %Q(#@@cmd -q -q -s -t #{opt_params} #{@@sep_op} #{Shellwords.escape(filename)})
94
+ if run(cmd)
95
+ parse_output
96
+ else
97
+ raise MiniExiftool::Error.new(@error_text)
98
+ end
99
+ self
100
+ end
101
+
102
+ # Reload the tags of an already read file.
103
+ def reload
104
+ load @filename
105
+ end
106
+
107
+ # Returns the value of a tag.
108
+ def [] tag
109
+ @changed_values[tag] || @values[tag]
110
+ end
111
+
112
+ # Set the value of a tag.
113
+ def []=(tag, val)
114
+ @changed_values[tag] = val
115
+ end
116
+
117
+ # Returns true if any tag value is changed or if the value of a
118
+ # given tag is changed.
119
+ def changed? tag=false
120
+ if tag
121
+ @changed_values.include? tag
122
+ else
123
+ !@changed_values.empty?
124
+ end
125
+ end
126
+
127
+ # Revert all changes or the change of a given tag.
128
+ def revert tag=nil
129
+ if tag
130
+ val = @changed_values.delete(tag)
131
+ res = val != nil
132
+ else
133
+ res = @changed_values.size > 0
134
+ @changed_values.clear
135
+ end
136
+ res
137
+ end
138
+
139
+ # Returns an array of the tags (original tag names) of the read file.
140
+ def tags
141
+ @values.keys.map { |key| @tag_names[key] }
142
+ end
143
+
144
+ # Returns an array of all changed tags.
145
+ def changed_tags
146
+ @changed_values.keys.map { |key| MiniExiftool.original_tag(key) }
147
+ end
148
+
149
+ # Save the changes to the file.
150
+ def save
151
+ MiniExiftool.setup
152
+ return false if @changed_values.empty?
153
+ @errors.clear
154
+ temp_file = Tempfile.new('mini_exiftool')
155
+ temp_file.close
156
+ temp_filename = temp_file.path
157
+ FileUtils.cp filename, temp_filename
158
+ all_ok = true
159
+ @changed_values.each do |tag, val|
160
+ original_tag = MiniExiftool.original_tag(tag)
161
+ arr_val = val.kind_of?(Array) ? val : [val]
162
+ arr_val.map! {|e| convert e}
163
+ tag_params = ''
164
+ arr_val.each do |v|
165
+ tag_params << %Q(-#{original_tag}=#{Shellwords.escape(v.to_s)} )
166
+ end
167
+ opt_params = ''
168
+ opt_params << (arr_val.detect {|x| x.kind_of?(Numeric)} ? '-n ' : '')
169
+ opt_params << (@convert_encoding ? '-L ' : '')
170
+ opt_params << (@ignore_minor_errors ? '-m' : '')
171
+ cmd = %Q(#@@cmd -q -P -overwrite_original #{opt_params} #{tag_params} #{temp_filename})
172
+ if convert_encoding && cmd.respond_to?(:encode)
173
+ cmd.encode('ISO-8859-1')
174
+ end
175
+ result = run(cmd)
176
+ unless result
177
+ all_ok = false
178
+ @errors[tag] = @error_text.gsub(/Nothing to do.\n\z/, '').chomp
179
+ end
180
+ end
181
+ if all_ok
182
+ FileUtils.cp temp_filename, filename
183
+ reload
184
+ end
185
+ temp_file.delete
186
+ all_ok
187
+ end
188
+
189
+ def save!
190
+ unless save
191
+ err = []
192
+ self.errors.each do |key, value|
193
+ err << "(#{key}) #{value}"
194
+ end
195
+ raise MiniExiftool::Error.new("MiniExiftool couldn't save. The following errors occurred: #{err.empty? ? "None" : err.join(", ")}")
196
+ end
197
+ end
198
+
199
+ # Returns a hash of the original loaded values of the MiniExiftool
200
+ # instance.
201
+ def to_hash
202
+ result = {}
203
+ @values.each do |k,v|
204
+ result[@tag_names[k]] = v
205
+ end
206
+ result
207
+ end
208
+
209
+ # Returns a YAML representation of the original loaded values of the
210
+ # MiniExiftool instance.
211
+ def to_yaml
212
+ to_hash.to_yaml
213
+ end
214
+
215
+ # Create a MiniExiftool instance from a hash. Default value conversions will be applied if neccesary.
216
+ def self.from_hash hash
217
+ instance = MiniExiftool.new
218
+ instance.initialize_from_hash hash
219
+ instance
220
+ end
221
+
222
+ # Create a MiniExiftool instance from YAML data created with
223
+ # MiniExiftool#to_yaml
224
+ def self.from_yaml yaml
225
+ MiniExiftool.from_hash YAML.load(yaml)
226
+ end
227
+
228
+ # Returns the command name of the called Exiftool application.
229
+ def self.command
230
+ @@cmd
231
+ end
232
+
233
+ # Setting the command name of the called Exiftool application.
234
+ def self.command= cmd
235
+ @@cmd = cmd
236
+ end
237
+
238
+ # Returns the options hash.
239
+ def self.opts
240
+ @@opts
241
+ end
242
+
243
+ # Returns a set of all known tags of Exiftool.
244
+ def self.all_tags
245
+ unless defined? @@all_tags
246
+ @@all_tags = pstore_get :all_tags
247
+ end
248
+ @@all_tags
249
+ end
250
+
251
+ # Returns a set of all possible writable tags of Exiftool.
252
+ def self.writable_tags
253
+ unless defined? @@writable_tags
254
+ @@writable_tags = pstore_get :writable_tags
255
+ end
256
+ @@writable_tags
257
+ end
258
+
259
+ # Returns the original Exiftool name of the given tag
260
+ def self.original_tag tag
261
+ unless defined? @@all_tags_map
262
+ @@all_tags_map = pstore_get :all_tags_map
263
+ end
264
+ @@all_tags_map[tag]
265
+ end
266
+
267
+ # Returns the version of the Exiftool command-line application.
268
+ def self.exiftool_version
269
+ output = `#{MiniExiftool.command} -ver 2>&1`
270
+ unless $?.exitstatus == 0
271
+ raise MiniExiftool::Error.new("Command '#{MiniExiftool.command}' not found")
272
+ end
273
+ output.chomp!
274
+ end
275
+
276
+ def self.unify tag
277
+ tag.to_s.gsub(/[-_]/,'').downcase
278
+ end
279
+
280
+ # Exception class
281
+ class MiniExiftool::Error < StandardError; end
282
+
283
+ ############################################################################
284
+ private
285
+ ############################################################################
286
+
287
+ @@setup_done = false
288
+ def self.setup
289
+ return if @@setup_done
290
+ @@error_file = Tempfile.new 'errors'
291
+ @@error_file.close
292
+
293
+ if Float(exiftool_version) < 7.41
294
+ @@separator = ', '
295
+ @@sep_op = ''
296
+ else
297
+ @@separator = '@@'
298
+ @@sep_op = '-sep @@'
299
+ end
300
+ @@setup_done = true
301
+ end
302
+
303
+ def run cmd
304
+ if $DEBUG
305
+ $stderr.puts cmd
306
+ end
307
+ @output = `#{cmd} 2>#{@@error_file.path}`
308
+ if convert_encoding && @output.respond_to?(:force_encoding)
309
+ @output.force_encoding('ISO-8859-1')
310
+ end
311
+ @status = $?
312
+ unless @status.exitstatus == 0
313
+ @error_text = File.readlines(@@error_file.path).join
314
+ return false
315
+ else
316
+ @error_text = ''
317
+ return true
318
+ end
319
+ end
320
+
321
+ def convert val
322
+ case val
323
+ when Time
324
+ val = val.strftime('%Y:%m:%d %H:%M:%S')
325
+ end
326
+ val
327
+ end
328
+
329
+ def method_missing symbol, *args
330
+ tag_name = symbol.id2name
331
+ if tag_name.sub!(/=$/, '')
332
+ self[tag_name] = args.first
333
+ else
334
+ self[tag_name]
335
+ end
336
+ end
337
+
338
+ def parse_output
339
+ @output.each_line do |line|
340
+ tag, value = parse_line line
341
+ set_value tag, value
342
+ end
343
+ end
344
+
345
+ def parse_line line
346
+ if line =~ /^([^\t]+)\t(.*)$/
347
+ tag, value = $1, perform_conversions($2)
348
+ else
349
+ raise MiniExiftool::Error.new("Malformed line #{line.inspect} of exiftool output.")
350
+ end
351
+ return [tag, value]
352
+ end
353
+
354
+ def perform_conversions(value)
355
+ case value
356
+ when /^\d{4}:\d\d:\d\d \d\d:\d\d:\d\d/
357
+ s = value.sub(/^(\d+):(\d+):/, '\1-\2-')
358
+ begin
359
+ if @timestamps == Time
360
+ value = Time.parse(s)
361
+ elsif @timestamps == DateTime
362
+ value = DateTime.parse(s)
363
+ else
364
+ raise MiniExiftool::Error.new("Value #@timestamps not allowed for option timestamps.")
365
+ end
366
+ rescue ArgumentError
367
+ value = false
368
+ end
369
+ when /^\d+\.\d+$/
370
+ value = value.to_f
371
+ when /^0+[1-9]+$/
372
+ # nothing => String
373
+ when /^-?\d+$/
374
+ value = value.to_i
375
+ when %r(^(\d+)/(\d+)$)
376
+ value = Rational($1.to_i, $2.to_i)
377
+ when /^[\d ]+$/
378
+ # nothing => String
379
+ when /#{@@separator}/
380
+ value = value.split @@separator
381
+ end
382
+ value
383
+ end
384
+
385
+ def set_value tag, value
386
+ @tag_names[tag] = tag
387
+ @values[tag] = value
388
+ end
389
+
390
+ def set_attributes_by_heuristic
391
+ self.composite = tags.include?('ImageSize') ? true : false
392
+ self.numerical = self.file_size.kind_of?(Integer) ? true : false
393
+ # TODO: Is there a heuristic to determine @convert_encoding?
394
+ self.timestamps = self.FileModifyDate.kind_of?(DateTime) ? DateTime : Time
395
+ end
396
+
397
+ def temp_filename
398
+ unless @temp_filename
399
+ temp_file = Tempfile.new('mini-exiftool')
400
+ temp_file.close
401
+ FileUtils.cp(@filename, temp_file.path)
402
+ @temp_filename = temp_file.path
403
+ end
404
+ @temp_filename
405
+ end
406
+
407
+ def self.pstore_get attribute
408
+ load_or_create_pstore unless defined? @@pstore
409
+ result = nil
410
+ @@pstore.transaction(true) do |ps|
411
+ result = ps[attribute]
412
+ end
413
+ result
414
+ end
415
+
416
+ def self.load_or_create_pstore
417
+ # This will hopefully work on *NIX and Windows systems
418
+ home = ENV['HOME'] || ENV['HOMEDRIVE'] + ENV['HOMEPATH'] || ENV['USERPROFILE']
419
+ subdir = RUBY_PLATFORM =~ /\bmswin/i ? '_mini_exiftool' : '.mini_exiftool'
420
+ FileUtils.mkdir_p(File.join(home, subdir))
421
+ filename = File.join(home, subdir, 'exiftool_tags_' << exiftool_version.gsub('.', '_') << '.pstore')
422
+ @@pstore = PStore.new filename
423
+ if !File.exist?(filename) || File.size(filename) == 0
424
+ @@pstore.transaction do |ps|
425
+ ps[:all_tags] = all_tags = determine_tags('list')
426
+ ps[:writable_tags] = determine_tags('listw')
427
+ map = {}
428
+ all_tags.each { |k| map[unify(k)] = k }
429
+ ps[:all_tags_map] = map
430
+ end
431
+ end
432
+ end
433
+
434
+ def self.determine_tags arg
435
+ output = `#{@@cmd} -#{arg}`
436
+ lines = output.split(/\n/)
437
+ tags = Set.new
438
+ lines.each do |line|
439
+ next unless line =~ /^\s/
440
+ tags |= line.chomp.split
441
+ end
442
+ tags
443
+ end
444
+
445
+
446
+ # Hash with indifferent access:
447
+ # DateTimeOriginal == datetimeoriginal == date_time_original
448
+ class TagHash < Hash # :nodoc:
449
+ def[] k
450
+ super(unify(k))
451
+ end
452
+ def []= k, v
453
+ super(unify(k), v)
454
+ end
455
+ def delete k
456
+ super(unify(k))
457
+ end
458
+
459
+ def unify tag
460
+ MiniExiftool.unify tag
461
+ end
462
+ end
463
+
464
+ end