leeh-mini_exiftool 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/COPYING +502 -0
- data/Changelog +159 -0
- data/README.rdoc +50 -0
- data/Rakefile +23 -0
- data/lib/mini_exiftool.rb +464 -0
- data/test/data/Bad_PreviewIFD.jpg +0 -0
- data/test/data/Canon.jpg +0 -0
- data/test/data/INFORMATION +3 -0
- data/test/data/test.jpg +0 -0
- data/test/data/test.jpg.json +106 -0
- data/test/data/test_coordinates.jpg +0 -0
- data/test/data/test_special_dates.jpg +0 -0
- data/test/helpers_for_test.rb +28 -0
- data/test/test_bad_preview_ifd.rb +29 -0
- data/test/test_class_methods.rb +80 -0
- data/test/test_composite.rb +19 -0
- data/test/test_dumping.rb +80 -0
- data/test/test_from_hash.rb +22 -0
- data/test/test_read.rb +53 -0
- data/test/test_read_coordinates.rb +18 -0
- data/test/test_read_numerical.rb +33 -0
- data/test/test_save.rb +85 -0
- data/test/test_special.rb +42 -0
- data/test/test_special_dates.rb +52 -0
- data/test/test_write.rb +161 -0
- metadata +85 -0
data/Changelog
ADDED
@@ -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
|
data/README.rdoc
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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
|