mini_exiftool 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Changelog CHANGED
@@ -1,3 +1,6 @@
1
+ Version 1.0.2
2
+ - Fixing warings
3
+ thanks to Peter-Hinrich Krogmann for the hint.
1
4
  Version 1.0.1
2
5
  - Fixing bug [#22726]
3
6
  Making MiniExiftool::Error public.
@@ -0,0 +1,445 @@
1
+ #
2
+ # MiniExiftool
3
+ #
4
+ # This library is wrapper for the Exiftool command-line
5
+ # application (http://www.sno.phy.queensu.ca/~phil/exiftool/)
6
+ # written by Phil Harvey.
7
+ # Read and write access is done in a clean OO manner.
8
+ #
9
+ # Author: Jan Friedrich
10
+ # Copyright (c) 2007, 2008 by Jan Friedrich
11
+ # Licensed under the GNU LESSER GENERAL PUBLIC LICENSE,
12
+ # Version 2.1, February 1999
13
+ #
14
+
15
+ require 'fileutils'
16
+ require 'tempfile'
17
+ require 'pstore'
18
+ require 'set'
19
+
20
+ # Simple OO access to the Exiftool command-line application.
21
+ class MiniExiftool
22
+
23
+ # Name of the Exiftool command-line application
24
+ @@cmd = 'exiftool'
25
+
26
+ # Hash of the standard options used when call MiniExiftool.new
27
+ @@opts = { :numerical => false, :composite => true, :convert_encoding => false, :timestamps => Time }
28
+
29
+ attr_reader :filename
30
+ attr_accessor :numerical, :composite, :convert_encoding, :errors, :timestamps
31
+
32
+ VERSION = '1.0.1'
33
+
34
+ # +opts+ support at the moment
35
+ # * <code>:numerical</code> for numerical values, default is +false+
36
+ # * <code>:composite</code> for including composite tags while loading,
37
+ # default is +true+
38
+ # * <code>:convert_encoding</code> convert encoding (See -L-option of
39
+ # the exiftool command-line application, default is +false+
40
+ # * <code>:timestamps</code> generating DateTime objects instead of
41
+ # Time objects if set to <code>DateTime</code>, default is +Time+
42
+ #
43
+ # <b>ATTENTION:</b> Time objects are created using <code>Time.local</code>
44
+ # therefore they use <em>your local timezone</em>, DateTime objects instead
45
+ # are created <em>without timezone</em>!
46
+ def initialize filename=nil, opts={}
47
+ opts = @@opts.merge opts
48
+ @numerical = opts[:numerical]
49
+ @composite = opts[:composite]
50
+ @convert_encoding = opts[:convert_encoding]
51
+ @timestamps = opts[:timestamps]
52
+ @values = TagHash.new
53
+ @tag_names = TagHash.new
54
+ @changed_values = TagHash.new
55
+ @errors = TagHash.new
56
+ load filename unless filename.nil?
57
+ end
58
+
59
+ def initialize_from_hash hash # :nodoc:
60
+ hash.each_pair do |tag,value|
61
+ set_value tag, value
62
+ end
63
+ set_attributes_by_heuristic
64
+ self
65
+ end
66
+
67
+ # Load the tags of filename.
68
+ def load filename
69
+ unless filename && File.exist?(filename)
70
+ raise MiniExiftool::Error.new("File '#{filename}' does not exist.")
71
+ end
72
+ if File.directory?(filename)
73
+ raise MiniExiftool::Error.new("'#{filename}' is a directory.")
74
+ end
75
+ @filename = filename
76
+ @values.clear
77
+ @tag_names.clear
78
+ @changed_values.clear
79
+ opt_params = ''
80
+ opt_params << (@numerical ? '-n ' : '')
81
+ opt_params << (@composite ? '' : '-e ')
82
+ opt_params << (@convert_encoding ? '-L ' : '')
83
+ cmd = %Q(#@@cmd -q -q -s -t #{opt_params} #{@@sep_op} "#{filename}")
84
+ if run(cmd)
85
+ parse_output
86
+ else
87
+ raise MiniExiftool::Error.new(@error_text)
88
+ end
89
+ self
90
+ end
91
+
92
+ # Reload the tags of an already readed file.
93
+ def reload
94
+ load @filename
95
+ end
96
+
97
+ # Returns the value of a tag.
98
+ def [] tag
99
+ @changed_values[tag] || @values[tag]
100
+ end
101
+
102
+ # Set the value of a tag.
103
+ def []=(tag, val)
104
+ @changed_values[tag] = val
105
+ end
106
+
107
+ # Returns true if any tag value is changed or if the value of a
108
+ # given tag is changed.
109
+ def changed? tag=false
110
+ if tag
111
+ @changed_values.include? tag
112
+ else
113
+ !@changed_values.empty?
114
+ end
115
+ end
116
+
117
+ # Revert all changes or the change of a given tag.
118
+ def revert tag=nil
119
+ if tag
120
+ val = @changed_values.delete(tag)
121
+ res = val != nil
122
+ else
123
+ res = @changed_values.size > 0
124
+ @changed_values.clear
125
+ end
126
+ res
127
+ end
128
+
129
+ # Returns an array of the tags (original tag names) of the readed file.
130
+ def tags
131
+ @values.keys.map { |key| @tag_names[key] }
132
+ end
133
+
134
+ # Returns an array of all changed tags.
135
+ def changed_tags
136
+ @changed_values.keys.map { |key| MiniExiftool.original_tag(key) }
137
+ end
138
+
139
+ # Save the changes to the file.
140
+ def save
141
+ return false if @changed_values.empty?
142
+ @errors.clear
143
+ temp_file = Tempfile.new('mini_exiftool')
144
+ temp_file.close
145
+ temp_filename = temp_file.path
146
+ FileUtils.cp filename, temp_filename
147
+ all_ok = true
148
+ @changed_values.each do |tag, val|
149
+ original_tag = MiniExiftool.original_tag(tag)
150
+ arr_val = val.kind_of?(Array) ? val : [val]
151
+ arr_val.map! {|e| convert e}
152
+ tag_params = ''
153
+ arr_val.each do |v|
154
+ tag_params << %Q(-#{original_tag}="#{v}" )
155
+ end
156
+ opt_params = ''
157
+ opt_params << (arr_val.detect {|x| x.kind_of?(Numeric)} ? '-n ' : '')
158
+ opt_params << (@convert_encoding ? '-L ' : '')
159
+ cmd = %Q(#@@cmd -q -P -overwrite_original #{opt_params} #{tag_params} "#{temp_filename}")
160
+ result = run(cmd)
161
+ unless result
162
+ all_ok = false
163
+ @errors[tag] = @error_text.gsub(/Nothing to do.\n\z/, '').chomp
164
+ end
165
+ end
166
+ if all_ok
167
+ FileUtils.cp temp_filename, filename
168
+ reload
169
+ end
170
+ temp_file.delete
171
+ all_ok
172
+ end
173
+
174
+ # Returns a hash of the original loaded values of the MiniExiftool
175
+ # instance.
176
+ def to_hash
177
+ result = {}
178
+ @values.each do |k,v|
179
+ result[@tag_names[k]] = v
180
+ end
181
+ result
182
+ end
183
+
184
+ # Returns a YAML representation of the original loaded values of the
185
+ # MiniExiftool instance.
186
+ def to_yaml
187
+ to_hash.to_yaml
188
+ end
189
+
190
+ # Create a MiniExiftool instance from a hash
191
+ def self.from_hash hash
192
+ instance = MiniExiftool.new
193
+ instance.initialize_from_hash hash
194
+ instance
195
+ end
196
+
197
+ # Create a MiniExiftool instance from YAML data created with
198
+ # MiniExiftool#to_yaml
199
+ def self.from_yaml yaml
200
+ MiniExiftool.from_hash YAML.load(yaml)
201
+ end
202
+
203
+ # Returns the command name of the called Exiftool application.
204
+ def self.command
205
+ @@cmd
206
+ end
207
+
208
+ # Setting the command name of the called Exiftool application.
209
+ def self.command= cmd
210
+ @@cmd = cmd
211
+ end
212
+
213
+ # Returns the options hash.
214
+ def self.opts
215
+ @@opts
216
+ end
217
+
218
+ # Returns a set of all known tags of Exiftool.
219
+ def self.all_tags
220
+ unless defined? @@all_tags
221
+ @@all_tags = pstore_get :all_tags
222
+ end
223
+ @@all_tags
224
+ end
225
+
226
+ # Returns a set of all possible writable tags of Exiftool.
227
+ def self.writable_tags
228
+ unless defined? @@writable_tags
229
+ @@writable_tags = pstore_get :writable_tags
230
+ end
231
+ @@writable_tags
232
+ end
233
+
234
+ # Returns the original Exiftool name of the given tag
235
+ def self.original_tag tag
236
+ unless defined? @@all_tags_map
237
+ @@all_tags_map = pstore_get :all_tags_map
238
+ end
239
+ @@all_tags_map[tag]
240
+ end
241
+
242
+ # Returns the version of the Exiftool command-line application.
243
+ def self.exiftool_version
244
+ output = `#{MiniExiftool.command} -ver 2>&1`
245
+ unless $?.exitstatus == 0
246
+ raise MiniExiftool::Error.new("Command '#{MiniExiftool.command}' not found")
247
+ end
248
+ output.chomp!
249
+ end
250
+
251
+ def self.unify tag
252
+ tag.gsub(/[-_]/,'').downcase
253
+ end
254
+
255
+ # Exception class
256
+ class MiniExiftool::Error < StandardError; end
257
+
258
+ ############################################################################
259
+ private
260
+ ############################################################################
261
+
262
+ @@error_file = Tempfile.new 'errors'
263
+ @@error_file.close
264
+
265
+ if Float(exiftool_version) < 7.41
266
+ @@separator = ', '
267
+ @@sep_op = ''
268
+ else
269
+ @@separator = '@@'
270
+ @@sep_op = '-sep @@'
271
+ end
272
+
273
+ def run cmd
274
+ if $DEBUG
275
+ $stderr.puts cmd
276
+ end
277
+ @output = `#{cmd} 2>#{@@error_file.path}`
278
+ @status = $?
279
+ unless @status.exitstatus == 0
280
+ @error_text = File.readlines(@@error_file.path).join
281
+ return false
282
+ else
283
+ @error_text = ''
284
+ return true
285
+ end
286
+ end
287
+
288
+ def convert val
289
+ case val
290
+ when Time
291
+ val = val.strftime('%Y:%m:%d %H:%M:%S')
292
+ end
293
+ val
294
+ end
295
+
296
+ def method_missing symbol, *args
297
+ tag_name = symbol.id2name
298
+ if tag_name.sub!(/=$/, '')
299
+ self[tag_name] = args.first
300
+ else
301
+ self[tag_name]
302
+ end
303
+ end
304
+
305
+ def parse_output
306
+ @output.each_line do |line|
307
+ tag, value = parse_line line
308
+ set_value tag, value
309
+ end
310
+ end
311
+
312
+ def parse_line line
313
+ if line =~ /^([^\t]+)\t(.*)$/
314
+ tag, value = $1, $2
315
+ case value
316
+ when /^\d{4}:\d\d:\d\d \d\d:\d\d:\d\d$/
317
+ arr = value.split(/[: ]/) #Fixed by kro44 - add parentheses
318
+ arr.map! {|elem| elem.to_i}
319
+ begin
320
+ if @timestamps == Time
321
+ value = Time.local(*arr) #Fixed by kro44 - add parentheses
322
+ elsif @timestamps == DateTime
323
+ value = DateTime.strptime(value,'%Y:%m:%d %H:%M:%S')
324
+ else
325
+ raise MiniExiftool::Error.new("Value #@timestamps not allowed for option timestamps.")
326
+ end
327
+ rescue ArgumentError
328
+ value = false
329
+ end
330
+ when /^\d+\.\d+$/
331
+ value = value.to_f
332
+ when /^0+[1-9]+$/
333
+ # nothing => String
334
+ when /^-?\d+$/
335
+ value = value.to_i
336
+ when /^[\d ]+$/
337
+ # nothing => String
338
+ when /#{@@separator}/
339
+ value = value.split @@separator
340
+ end
341
+ else
342
+ raise MiniExiftool::Error.new("Malformed line #{line.inspect} of exiftool output.")
343
+ end
344
+ unless value.respond_to?('to_a')
345
+ class << value
346
+ def to_a
347
+ [self]
348
+ end
349
+ end
350
+ end
351
+ return [tag, value]
352
+ end
353
+
354
+ def set_value tag, value
355
+ @tag_names[tag] = tag
356
+ @values[tag] = value
357
+ end
358
+
359
+ def set_attributes_by_heuristic
360
+ self.composite = tags.include?('ImageSize') ? true : false
361
+ self.numerical = self.file_size.kind_of?(Integer) ? true : false
362
+ # TODO: Is there a heuristic to determine @convert_encoding?
363
+ self.timestamps = self.FileModifyDate.kind_of?(DateTime) ? DateTime : Time
364
+ end
365
+
366
+ def temp_filename
367
+ unless @temp_filename
368
+ temp_file = Tempfile.new('mini-exiftool')
369
+ temp_file.close
370
+ FileUtils.cp(@filename, temp_file.path)
371
+ @temp_filename = temp_file.path
372
+ end
373
+ @temp_filename
374
+ end
375
+
376
+ def self.pstore_get attribute
377
+ load_or_create_pstore unless defined? @@pstore
378
+ result = nil
379
+ @@pstore.transaction(true) do |ps|
380
+ result = ps[attribute]
381
+ end
382
+ result
383
+ end
384
+
385
+ def self.load_or_create_pstore
386
+ # This will hopefully work on *NIX and Windows systems
387
+ home = ENV['HOME'] || ENV['HOMEDRIVE'] + ENV['HOMEPATH'] || ENV['USERPROFILE']
388
+ subdir = RUBY_PLATFORM =~ /win/i ? '_mini_exiftool' : '.mini_exiftool'
389
+ FileUtils.mkdir_p(File.join(home, subdir))
390
+ filename = File.join(home, subdir, 'exiftool_tags_' << exiftool_version.gsub('.', '_') << '.pstore')
391
+ @@pstore = PStore.new filename
392
+ if !File.exist?(filename) || File.size(filename) == 0
393
+ @@pstore.transaction do |ps|
394
+ ps[:all_tags] = all_tags = determine_tags('list')
395
+ ps[:writable_tags] = determine_tags('listw')
396
+ map = {}
397
+ all_tags.each { |k| map[unify(k)] = k }
398
+ ps[:all_tags_map] = map
399
+ end
400
+ end
401
+ end
402
+
403
+ def self.determine_tags arg
404
+ output = `#{@@cmd} -#{arg}`
405
+ lines = output.split(/\n/) #Fixed by kro44 - add parentheses
406
+ tags = Set.new
407
+ lines.each do |line|
408
+ next unless line =~ /^\s/
409
+ tags |= line.chomp.split
410
+ end
411
+ tags
412
+ end
413
+
414
+
415
+ # Hash with indifferent access:
416
+ # DateTimeOriginal == datetimeoriginal == date_time_original
417
+ class TagHash < Hash # :nodoc:
418
+ def[] k
419
+ super(unify(k))
420
+ end
421
+ def []= k, v
422
+ super(unify(k), v)
423
+ end
424
+ def delete k
425
+ super(unify(k))
426
+ end
427
+
428
+ def unify tag
429
+ MiniExiftool.unify tag
430
+ end
431
+ end
432
+
433
+ end
434
+
435
+ # Add to_a to Numerical if it's not yet defined
436
+ unless Numeric.instance_methods.include? 'to_a'
437
+ class Numeric
438
+ def to_a
439
+ [self]
440
+ end
441
+ end
442
+ end
443
+
444
+ # Test if we can run the Exiftool command
445
+ MiniExiftool.exiftool_version
@@ -29,7 +29,7 @@ class MiniExiftool
29
29
  attr_reader :filename
30
30
  attr_accessor :numerical, :composite, :convert_encoding, :errors, :timestamps
31
31
 
32
- VERSION = '1.0.1'
32
+ VERSION = '1.0.2'
33
33
 
34
34
  # +opts+ support at the moment
35
35
  # * <code>:numerical</code> for numerical values, default is +false+
@@ -314,11 +314,11 @@ class MiniExiftool
314
314
  tag, value = $1, $2
315
315
  case value
316
316
  when /^\d{4}:\d\d:\d\d \d\d:\d\d:\d\d$/
317
- arr = value.split /[: ]/
317
+ arr = value.split(/[: ]/)
318
318
  arr.map! {|elem| elem.to_i}
319
319
  begin
320
320
  if @timestamps == Time
321
- value = Time.local *arr
321
+ value = Time.local(*arr)
322
322
  elsif @timestamps == DateTime
323
323
  value = DateTime.strptime(value,'%Y:%m:%d %H:%M:%S')
324
324
  else
@@ -402,7 +402,7 @@ class MiniExiftool
402
402
 
403
403
  def self.determine_tags arg
404
404
  output = `#{@@cmd} -#{arg}`
405
- lines = output.split /\n/
405
+ lines = output.split(/\n/)
406
406
  tags = Set.new
407
407
  lines.each do |line|
408
408
  next unless line =~ /^\s/
data/x.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'mini_exiftool'
2
+
3
+ puts MiniExiftool.exiftool_version
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_exiftool
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ hash: 19
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 2
10
+ version: 1.0.2
5
11
  platform: ruby
6
12
  authors:
7
13
  - Jan Friedrich
@@ -9,7 +15,7 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2008-11-10 00:00:00 +01:00
18
+ date: 2010-12-30 00:00:00 +01:00
13
19
  default_executable:
14
20
  dependencies: []
15
21
 
@@ -23,32 +29,36 @@ extra_rdoc_files:
23
29
  - README
24
30
  - Tutorial
25
31
  files:
32
+ - examples/external_photo.rb
33
+ - examples/print_portraits.rb
34
+ - examples/shift_time.rb
35
+ - lib/mini_exiftool.new.rb
26
36
  - lib/mini_exiftool.rb
27
- - test/test_write.rb
28
- - test/test_read.rb
29
- - test/test_special.rb
37
+ - setup.rb
38
+ - test/helpers_for_test.rb
30
39
  - test/test_class_methods.rb
31
- - test/test_save.rb
32
40
  - test/test_composite.rb
41
+ - test/test_dumping.rb
42
+ - test/test_read.rb
33
43
  - test/test_read_numerical.rb
34
- - test/helpers_for_test.rb
44
+ - test/test_save.rb
45
+ - test/test_special.rb
35
46
  - test/test_special_dates.rb
36
- - test/test_dumping.rb
37
- - examples/external_photo.rb
38
- - examples/print_portraits.rb
39
- - examples/shift_time.rb
40
- - setup.rb
47
+ - test/test_write.rb
48
+ - x.rb
41
49
  - Rakefile
42
50
  - COPYING
43
51
  - Changelog
44
- - test/data/test.jpg
45
52
  - test/data/Canon.jpg
46
53
  - test/data/INFORMATION
54
+ - test/data/test.jpg
47
55
  - test/data/test_special_dates.jpg
48
56
  - README
49
57
  - Tutorial
50
58
  has_rdoc: true
51
59
  homepage: http://miniexiftool.rubyforge.org/
60
+ licenses: []
61
+
52
62
  post_install_message:
53
63
  rdoc_options:
54
64
  - --title
@@ -58,31 +68,37 @@ rdoc_options:
58
68
  require_paths:
59
69
  - lib
60
70
  required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
61
72
  requirements:
62
73
  - - ">="
63
74
  - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
64
78
  version: "0"
65
- version:
66
79
  required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
67
81
  requirements:
68
82
  - - ">="
69
83
  - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
70
87
  version: "0"
71
- version:
72
88
  requirements: []
73
89
 
74
90
  rubyforge_project: miniexiftool
75
- rubygems_version: 1.2.0
91
+ rubygems_version: 1.3.7
76
92
  signing_key:
77
- specification_version: 2
93
+ specification_version: 3
78
94
  summary: A library for nice OO access to the Exiftool command-line application written by Phil Harvey.
79
95
  test_files:
80
- - test/test_write.rb
81
- - test/test_read.rb
82
- - test/test_special.rb
83
96
  - test/test_class_methods.rb
84
- - test/test_save.rb
85
97
  - test/test_composite.rb
98
+ - test/test_dumping.rb
99
+ - test/test_read.rb
86
100
  - test/test_read_numerical.rb
101
+ - test/test_save.rb
102
+ - test/test_special.rb
87
103
  - test/test_special_dates.rb
88
- - test/test_dumping.rb
104
+ - test/test_write.rb