leeh-mini_exiftool 1.6.0
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.
- 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
|