exiftoolr 0.0.4

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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in exiftoolr.gemspec
4
+ gemspec
@@ -0,0 +1,75 @@
1
+ # Ruby wrapper for ExifTool
2
+
3
+ This gem is the simplest thing that could possibly work that
4
+ reads the output of [exiftool](http://www.sno.phy.queensu.ca/~phil/exiftool)
5
+ and renders it into a ruby hash, with correctly typed values and symbolized keys.
6
+
7
+ ## Want constitutes "correct"?
8
+
9
+ * GPS latitude and longitude are rendered as signed floats,
10
+ where north and east are positive, and west and south are negative.
11
+ * Values like shutter speed and exposure time are rendered as Rationals,
12
+ which lets the caller show them as fractions (1/250) or as comparable.
13
+ * String values like "interop" and "serial number" are kept as strings
14
+ (which preserves zero prefixes)
15
+
16
+ ## Installation
17
+
18
+ You'll want to [install ExifTool](http://www.sno.phy.queensu.ca/~phil/exiftool/install.html), then
19
+
20
+ ```
21
+ gem install exiftoolr
22
+ ```
23
+
24
+ or add to your Gemfile:
25
+
26
+ ```
27
+ gem 'exiftoolr'
28
+ ```
29
+
30
+ and run ```bundle```.
31
+
32
+ ## Usage
33
+
34
+ ```ruby
35
+ require 'exiftoolr'
36
+ e = Exiftoolr.new("path/to/iPhone 4S.jpg")
37
+ e.to_hash
38
+ # => {:make => "Apple", :gps_longitude => -122.47566667, …
39
+ e.to_display_hash
40
+ # => {"Make" => "Apple", "GPS Longitude" => -122.47566667, …
41
+ ```
42
+
43
+ ### Multiple file support
44
+
45
+ Supply an array to the Exiftoolr initializer, then use ```.result_for```:
46
+
47
+ ```ruby
48
+ require 'exiftoolr'
49
+ e = Exiftoolr.new(Dir["**/*.jpg"])
50
+ result = e.result_for("path/to/iPhone 4S.jpg")
51
+ result.to_hash
52
+ # => {:make => "Apple", :gps_longitude => -122.47566667, …
53
+ result[:gps_longitude]
54
+ # => -122.47566667
55
+
56
+ e.files_with_results
57
+ # => ["path/to/iPhone 4S.jpg", "path/to/Droid X.jpg", …
58
+ ```
59
+
60
+ ### When things go wrong
61
+
62
+ * ```Exiftoolr::NoSuchFile``` is raised if the provided filename doesn't exist.
63
+ * ```Exiftoolr::ExiftoolNotInstalled``` is raised if ```exiftool``` isn't in your ```PATH```.
64
+ * If ExifTool has a problem reading EXIF data, no exception is raised, but ```#errors?``` will return true:
65
+
66
+ ```ruby
67
+ Exiftoolr.new("Gemfile").errors?
68
+ #=> true
69
+ ```
70
+
71
+ ## Change history
72
+
73
+ ### 0.0.4
74
+
75
+ Added support for multiple file fetching (which is *much* faster for large directories)
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new
5
+ task :default => :test
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "exiftoolr/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "exiftoolr"
7
+ s.version = Exiftoolr::VERSION
8
+ s.authors = ["Matthew McEachen"]
9
+ s.email = ["matthew-github@mceachen.org"]
10
+ s.homepage = "https://github.com/mceachen/exif_tooler"
11
+ s.summary = %q{Multiget ExifTool wrapper for ruby}
12
+ s.description = %q{Multiget ExifTool wrapper for ruby}
13
+
14
+ s.rubyforge_project = "exiftoolr"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.requirements << "ExifTool (see http://www.sno.phy.queensu.ca/~phil/exiftool/)"
22
+
23
+ s.add_dependency "json"
24
+ s.add_development_dependency "rake"
25
+ s.add_development_dependency "rspec"
26
+ end
@@ -0,0 +1,64 @@
1
+ require "exiftoolr/version"
2
+ require "exiftoolr/result"
3
+
4
+ require 'json'
5
+ require 'shellwords'
6
+
7
+ class Exiftoolr
8
+ class NoSuchFile < StandardError; end
9
+ class NotAFile < StandardError; end
10
+ class ExiftoolNotInstalled < StandardError; end
11
+
12
+ def self.exiftool_installed?
13
+ `exiftool -ver 2> /dev/null`.to_f > 0
14
+ end
15
+
16
+ def self.expand_path(filename)
17
+ raise NoSuchFile, filename unless File.exist?(filename)
18
+ raise NotAFile, filename unless File.file?(filename)
19
+ File.expand_path(filename)
20
+ end
21
+
22
+ def initialize(filenames, exiftool_opts = "")
23
+ escaped_filenames = filenames.to_a.collect do |f|
24
+ Shellwords.escape(self.class.expand_path(f))
25
+ end.join(" ")
26
+ json = `exiftool #{exiftool_opts} -j −coordFormat "%.8f" -dateFormat "%Y-%m-%d %H:%M:%S" #{escaped_filenames} 2> /dev/null`
27
+ raise ExiftoolNotInstalled if json == ""
28
+ @file2result = { }
29
+ JSON.parse(json).each do |raw|
30
+ @file2result[r.source_file] = Result.new(raw)
31
+ end
32
+ end
33
+
34
+ def result_for(filename)
35
+ @file2result[self.class.expand_path(filename)]
36
+ end
37
+
38
+ def files_with_results
39
+ @file2result.values.collect{|r|r.source_file unless r.errors?}.compact
40
+ end
41
+
42
+ def to_hash
43
+ first.to_hash
44
+ end
45
+
46
+ def to_display_hash
47
+ first.to_display_hash
48
+ end
49
+
50
+ def symbol_display_hash
51
+ first.symbol_display_hash
52
+ end
53
+
54
+ def errors?
55
+ @file2result.values.any? { |ea| ea.errors? }
56
+ end
57
+
58
+ private
59
+
60
+ def first
61
+ raise InvalidArgument, "use #result_for when multiple filenames are used" if @file2result.size > 1
62
+ @file2result.values.first
63
+ end
64
+ end
@@ -0,0 +1,47 @@
1
+ require 'time'
2
+ require 'rational'
3
+
4
+ class Exiftoolr
5
+ class Result
6
+ attr_reader :to_hash, :to_display_hash, :symbol_display_hash
7
+
8
+ WORD_BOUNDARY_RES = [/([A-Z\d]+)([A-Z][a-z])/, /([a-z\d])([A-Z])/]
9
+ FRACTION_RE = /\d+\/\d+/
10
+
11
+ def initialize(raw_hash)
12
+ @raw_hash = raw_hash
13
+ @to_hash = { }
14
+ @to_display_hash = { }
15
+ @symbol_display_hash = { }
16
+
17
+ @raw_hash.each do |k, v|
18
+ display_key = WORD_BOUNDARY_RES.inject(k) { |key, regex| key.gsub(regex, '\1 \2') }
19
+ sym_key = display_key.downcase.gsub(' ', '_').to_sym
20
+ if sym_key == :gps_latitude || sym_key == :gps_longitude
21
+ value, direction = v.split(" ")
22
+ v = value.to_f
23
+ v *= -1 if direction == 'S' || direction == 'W'
24
+ elsif display_key =~ /\bdate\b/i
25
+ v = Time.parse(v)
26
+ elsif v =~ FRACTION_RE
27
+ v = Rational(*v.split('/').collect { |ea| ea.to_i })
28
+ end
29
+ @to_hash[sym_key] = v
30
+ @to_display_hash[display_key] = v
31
+ @symbol_display_hash[sym_key] = display_key
32
+ end
33
+ end
34
+
35
+ def [](key)
36
+ @to_hash[key]
37
+ end
38
+
39
+ def source_file
40
+ self[:source_file]
41
+ end
42
+
43
+ def errors?
44
+ self[:error] == "Unknown file type" || self[:warning] == "Unsupported file type"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ class Exiftoolr
2
+ VERSION = "0.0.4"
3
+ end
Binary file
@@ -0,0 +1,225 @@
1
+ ---
2
+ :color_temp_custom2: 4792
3
+ :sensor_left_border: 84
4
+ :flash_activity: 0
5
+ :measured_rggb: 674 1024 1024 670
6
+ :photo_effect: "Off"
7
+ :digital_zoom: None
8
+ :af_points_in_focus: 4
9
+ :color_temp_shade: 7000
10
+ :y_cb_cr_sub_sampling: YCbCr4:2:2 (2 1)
11
+ :metering_mode: Evaluative
12
+ :image_size: 3504x2336
13
+ :canon_model_id: EOS 20D
14
+ :bracket_mode: "Off"
15
+ :camera_type: EOS Mid-range
16
+ :exposure_mode: Auto
17
+ :tone_curve: Standard
18
+ :af_area_x_positions: 8 -555 571 -931 8 947 -555 571 8
19
+ :color_tone: Normal
20
+ :af_image_width: 3504
21
+ :max_aperture: 5.6
22
+ :aeb_bracket_value: 0
23
+ :shutter_curtain_sync: 1st-curtain sync
24
+ :aeb_sequence_auto_cancel: 0,-,+/Enabled
25
+ :scale_factor35efl: 1.6
26
+ :lens_af_stop_button: AF stop
27
+ :add_original_decision_data: "Off"
28
+ :thumbnail_image: (Binary data 11259 bytes)
29
+ :flash_sync_speed_av: Auto
30
+ :black_mask_right_border: 0
31
+ :base_iso: 200
32
+ :image_width: 3504
33
+ :focal_plane_x_resolution: 2886.363636
34
+ :exposure_program: Program AE
35
+ :safety_shift_in_av_or_tv: Disable
36
+ :measured_ev: 13.25
37
+ :continuous_drive: Single
38
+ :red_balance: 1.943842
39
+ :file_name: Canon 20D.jpg
40
+ :white_balance_blue: 0
41
+ :lens: 17.0 - 85.0 mm
42
+ :digital_gain: 0
43
+ :macro_mode: Normal
44
+ :wb_rggb_levels_flash: 2187 1023 1023 1333
45
+ :menu_button_display_position: Previous (top if power off)
46
+ :sensor_height: 2360
47
+ :file_size: 2.9 MB
48
+ :black_mask_top_border: 0
49
+ :ettlii: Evaluative
50
+ :af_area_width: 78
51
+ :f_number: 9.0
52
+ :directory: test
53
+ :black_mask_bottom_border: 0
54
+ :focal_plane_y_size: 15.37 mm
55
+ :toning_effect: None
56
+ :af_area_y_positions: 504 270 270 4 4 4 -262 -262 -496
57
+ :focal_plane_y_resolution: 2885.805763
58
+ :target_aperture: 9
59
+ :focus_mode: One-shot AF
60
+ :mirror_lockup: Disable
61
+ :color_temp_flash: 6309
62
+ :make: Canon
63
+ :set_function_when_shooting: Image replay
64
+ :wb_bracket_value_gm: 0
65
+ :exposure_time: !ruby/object:Rational
66
+ denominator: 250
67
+ numerator: 1
68
+ :thumbnail_offset: 9728
69
+ :canon_flash_mode: "Off"
70
+ :serial_number: 0330113864
71
+ :fov: 15.4 deg
72
+ :thumbnail_image_valid_area: 0 159 7 112
73
+ :af_point_selection_method: Normal
74
+ :color_space: sRGB
75
+ :original_decision_data_offset: 0
76
+ :lens_id: Unknown 17-85mm
77
+ :shutter_speed: !ruby/object:Rational
78
+ denominator: 250
79
+ numerator: 1
80
+ :light_value: 13.3
81
+ :superimposed_display: "On"
82
+ :exposure_compensation: 0
83
+ :orientation: Horizontal (normal)
84
+ :components_configuration: Y, Cb, Cr, -
85
+ :"shutter-ae_lock": AF/AE lock
86
+ :focal_length: 85.0 mm
87
+ :focal_length35efl: "85.0 mm (35 mm equivalent: 132.8 mm)"
88
+ :bracket_shot_number: 0
89
+ :model: Canon EOS 20D
90
+ :aperture_value: 9.0
91
+ :flash_guide_number: 0
92
+ :long_exposure_noise_reduction2: "Off"
93
+ :focal_plane_x_size: 23.04 mm
94
+ :sequence_number: 0
95
+ :long_exposure_noise_reduction: "Off"
96
+ :white_balance: Auto
97
+ :file_type: JPEG
98
+ :canon_exposure_mode: Program AE
99
+ :interop_version: "0100"
100
+ :canon_image_width: 3504
101
+ :color_temp_fluorescent: 3952
102
+ :canon_firmware_version: Firmware 1.0.2
103
+ :sharpness: "+1"
104
+ :color_temp_auto: 4662
105
+ :af_assist_beam: Emits
106
+ :iso: 200
107
+ :black_mask_left_border: 0
108
+ :color_temp_custom1: 6163
109
+ :focal_plane_resolution_unit: inches
110
+ :wb_rggb_levels_custom1: 2095 1023 1023 1316
111
+ :flash_bits: (none)
112
+ :sensor_right_border: 3587
113
+ :bracket_value: 0
114
+ :canon_image_type: Canon EOS 20D
115
+ :exif_version: "0221"
116
+ :exif_image_width: 3504
117
+ :encoding_process: Baseline DCT, Huffman coding
118
+ :contrast: "+1"
119
+ :file_modify_date: 2012-02-19 21:49:48 -08:00
120
+ :white_balance_red: 0
121
+ :auto_rotate: None
122
+ :scene_capture_type: Standard
123
+ :compression: JPEG (old-style)
124
+ :sensor_red_level: 0
125
+ :control_mode: Camera Local Control
126
+ :sensor_width: 3596
127
+ :shooting_mode: Program AE
128
+ :lens_type: Unknown (-1)
129
+ :sensor_blue_level: 0
130
+ :file_permissions: rw-r--r--
131
+ :num_af_points: 9
132
+ :focus_continuous: Single
133
+ :color_temp_tungsten: 3196
134
+ :thumbnail_length: 11259
135
+ :target_exposure_time: !ruby/object:Rational
136
+ denominator: 251
137
+ numerator: 1
138
+ :valid_af_points: 9
139
+ :long_focal: 85 mm
140
+ :wb_rggb_levels_daylight: 1995 1023 1023 1509
141
+ :interop_index: R98 - DCF basic file (sRGB)
142
+ :canon_image_height: 2336
143
+ :create_date: 2004-09-19 12:25:20 -07:00
144
+ :wb_bracket_mode: "Off"
145
+ :focal_units: 1/mm
146
+ :serial_number_format: Format 2
147
+ :short_focal: 17 mm
148
+ :sensor_top_border: 19
149
+ :measured_ev2: 13.5
150
+ :image_height: 2336
151
+ :color_temp_as_shot: 4662
152
+ :flash: Off, Did not fire
153
+ :iso_expansion: "On"
154
+ :record_mode: JPEG
155
+ :flashpix_version: "0100"
156
+ :wb_rggb_levels_tungsten: 1410 1023 1023 2408
157
+ :blue_balance: 1.638424
158
+ :sensor_bottom_border: 2354
159
+ :circle_of_confusion: 0.019 mm
160
+ :exif_byte_order: Little-endian (Intel, II)
161
+ :custom_rendered: Normal
162
+ :drive_mode: Single-frame Shooting
163
+ :color_components: 3
164
+ :x_resolution: 72
165
+ :exif_image_height: 2336
166
+ :aperture: 9.0
167
+ :easy_mode: Manual
168
+ :nd_filter: n/a
169
+ :exposure_level_increments: !ruby/object:Rational
170
+ denominator: 3
171
+ numerator: 1
172
+ :auto_exposure_bracketing: "Off"
173
+ :af_area_height: 78
174
+ :flash_firing: Fires
175
+ :wb_rggb_levels_custom2: 1912 1023 1023 1627
176
+ :self_timer2: 0
177
+ :lens35efl: "17.0 - 85.0 mm (35 mm equivalent: 26.6 - 132.8 mm)"
178
+ :mime_type: image/jpeg
179
+ :wb_rggb_levels_shade: 2292 1023 1023 1267
180
+ :canon_image_size: Large
181
+ :hyperfocal_distance: 41.74 m
182
+ :wb_rggb_levels_auto: 1973 1023 1023 1663
183
+ :zoom_source_width: 0
184
+ :auto_iso: 100
185
+ :vrd_offset: 0
186
+ :wb_shift_gm: 0
187
+ :wb_rggb_levels_fluorescent: 1797 1023 1023 2058
188
+ :color_temperature: 4800
189
+ :wb_rggb_levels: 1973 1015 1015 1663
190
+ :wb_shift_ab: 0
191
+ :user_comment: ""
192
+ :zoom_target_width: 0
193
+ :quality: Fine
194
+ :color_temp_cloudy: 6000
195
+ :af_image_height: 2336
196
+ :manual_flash_output: n/a
197
+ :flash_exposure_comp: 0
198
+ :wb_rggb_levels_cloudy: 2160 1023 1023 1373
199
+ :source_file: test/Canon 20D.jpg
200
+ :color_temp_daylight: 5200
201
+ :sharpness_frequency: n/a
202
+ :slow_shutter: None
203
+ :modify_date: 2004-09-19 12:25:20 -07:00
204
+ :bulb_duration: 0
205
+ :bits_per_sample: 8
206
+ :focus_range: Not Known
207
+ :saturation: "+1"
208
+ :focal_type: Zoom
209
+ :wb_rggb_levels_as_shot: 1973 1015 1015 1663
210
+ :min_aperture: 32
211
+ :exif_tool_version: 8.59
212
+ :y_resolution: 72
213
+ :date_time_original: 2004-09-19 12:25:20 -07:00
214
+ :shutter_speed_value: !ruby/object:Rational
215
+ denominator: 250
216
+ numerator: 1
217
+ :filter_effect: None
218
+ :resolution_unit: inches
219
+ :y_cb_cr_positioning: Co-sited
220
+ :picture_style: None
221
+ :file_number: 793-9390
222
+ :self_timer: "Off"
223
+ :wb_bracket_value_ab: 0
224
+ :optical_zoom_code: n/a
225
+ :owner_name: unknown
Binary file
@@ -0,0 +1,87 @@
1
+ ---
2
+ :exposure_compensation: 0
3
+ :model: DROIDX 6d460001fff80000015d7d330e01c011
4
+ :color_space: sRGB
5
+ :components_configuration: Cb, Y, Cr, Y
6
+ :exif_byte_order: Little-endian (Intel, II)
7
+ :sensing_method: One-chip color area
8
+ :create_date: 2010-12-08 01:15:37 -08:00
9
+ :bits_per_sample: 8
10
+ :date_time_original: 2010-12-08 01:15:37 -08:00
11
+ :source_file: test/Droid X.jpg
12
+ :thumbnail_length: 32586
13
+ :exif_image_width: 3264
14
+ :scene_type: Directly photographed
15
+ :contrast: Normal
16
+ :exposure_program: Program AE
17
+ :compression: JPEG (old-style)
18
+ :image_size: 3264x1840
19
+ :max_aperture_value: 2.8
20
+ :mime_type: image/jpeg
21
+ :x_resolution: 300
22
+ :interop_version: "0100"
23
+ :orientation: Rotate 90 CW
24
+ :software: 2.2
25
+ :thumbnail_offset: 2150
26
+ :shutter_speed: !ruby/object:Rational
27
+ denominator: 15
28
+ numerator: 1
29
+ :directory: test
30
+ :flashpix_version: "0100"
31
+ :y_cb_cr_positioning: Centered
32
+ :flash: Auto, Did not fire
33
+ :f_number: 2.8
34
+ :saturation: Normal
35
+ :file_modify_date: 2012-02-19 21:49:48 -08:00
36
+ :y_resolution: 300
37
+ :encoding_process: Baseline DCT, Huffman coding
38
+ :subject_distance: 0 m
39
+ :thumbnail_image: (Binary data 32586 bytes)
40
+ :white_balance: Auto
41
+ :metering_mode: Center-weighted average
42
+ :exposure_mode: Auto
43
+ :compressed_bits_per_pixel: 4
44
+ :circle_of_confusion: 0.004 mm
45
+ :color_components: 3
46
+ :image_height: 1840
47
+ :exif_tool_version: 8.59
48
+ :subject_distance_range: Unknown
49
+ :exposure_time: !ruby/object:Rational
50
+ denominator: 15
51
+ numerator: 1
52
+ :resolution_unit: inches
53
+ :scene_capture_type: Standard
54
+ :file_name: Droid X.jpg
55
+ :image_unique_id: ""
56
+ :hyperfocal_distance: 2.24 m
57
+ :focal_length35efl: "5.0 mm (35 mm equivalent: 38.0 mm)"
58
+ :light_source: Standard Light A
59
+ :y_cb_cr_sub_sampling: YCbCr4:2:0 (2 2)
60
+ :aperture: 2.8
61
+ :interop_index: R98 - DCF basic file (sRGB)
62
+ :fov: 50.7 deg
63
+ :brightness_value: -0.8203125
64
+ :file_permissions: rwxr-xr-x
65
+ :exif_version: "0220"
66
+ :digital_zoom_ratio: 0
67
+ :sharpness: Normal
68
+ :custom_rendered: Custom
69
+ :aperture_value: 2.8
70
+ :exif_image_height: 1840
71
+ :focal_length: 5.0 mm
72
+ :gain_control: High gain up
73
+ :light_value: 4.2
74
+ :modify_date: 2010-12-08 01:15:37 -08:00
75
+ :shutter_speed_value: !ruby/object:Rational
76
+ denominator: 15
77
+ numerator: 1
78
+ :make: Motorola
79
+ :exposure_index: 637
80
+ :file_size: 2023 kB
81
+ :focal_length_in35mm_format: 38 mm
82
+ :image_width: 3264
83
+ :user_comment: ""
84
+ :file_type: JPEG
85
+ :scale_factor35efl: 7.6
86
+ :iso: 637
87
+ :file_source: Digital Camera
Binary file
@@ -0,0 +1,327 @@
1
+ ---
2
+ :black_mask_bottom_border: 0
3
+ :red_trc: (Binary data 14 bytes)
4
+ :wb_rggb_levels_custom2: 1734 1023 1023 1901
5
+ :serial_number: 0720413239
6
+ :fov: 68.3 deg
7
+ :self_timer2: 0
8
+ :file_size: 3.8 MB
9
+ :wb_rggb_levels: 1822 1015 1015 1709
10
+ :wb_shift_ab: 0
11
+ :lens35efl: "17.0 - 40.0 mm (35 mm equivalent: 26.6 - 62.5 mm)"
12
+ :thumbnail_image_valid_area: 0 159 7 112
13
+ :"profile_description_ml-zh-cn": !binary |
14
+ 55u45py6IFJHQiDmj4/ov7Dmlofku7Y=
15
+
16
+ :current_iptc_digest: b443520a10119da99c2550175e6d0efb
17
+ :model: Canon EOS 20D
18
+ :af_point_selection_method: Normal
19
+ :aperture_value: 4.0
20
+ :coded_character_set: UTF8
21
+ :user_comment: ""
22
+ :color_space: sRGB
23
+ :focus_range: Not Known
24
+ :"profile_description_ml-ja-jp": !binary |
25
+ 44Kr44Oh44OpIFJHQiDjg5fjg63jg5XjgqHjgqTjg6s=
26
+
27
+ :zoom_target_width: 0
28
+ :device_model: ""
29
+ :flash_guide_number: 0
30
+ :focal_plane_x_size: 23.04 mm
31
+ :saturation: Normal
32
+ :quality: Fine
33
+ :"profile_description_ml-it-it": Profilo RGB Fotocamera
34
+ :focal_type: Zoom
35
+ :iso: 400
36
+ :sequence_number: 0
37
+ :profile_file_signature: acsp
38
+ :wb_rggb_levels_as_shot: 1822 1015 1015 1709
39
+ :black_mask_left_border: 0
40
+ :long_exposure_noise_reduction2: "Off"
41
+ :color_temp_custom2: 4301
42
+ :blue_trc: (Binary data 14 bytes)
43
+ :color_temp_custom1: 2694
44
+ :compression: JPEG (old-style)
45
+ :exif_tool_version: 8.59
46
+ :focal_plane_resolution_unit: inches
47
+ :sensor_left_border: 84
48
+ :sensor_red_level: 0
49
+ :wb_rggb_levels_custom1: 1096 1023 1023 2793
50
+ :"profile_description_ml-ko-kr": !binary |
51
+ 7Lm066mU6528IFJHQiDtlITroZztjIzsnbw=
52
+
53
+ :photo_effect: "Off"
54
+ :af_area_x_positions: 8 -555 571 -931 8 947 -555 571 8
55
+ :profile_version: 2.2.0
56
+ :control_mode: Camera Local Control
57
+ :flash_activity: 0
58
+ :connection_space_illuminant: 0.9642 1 0.82491
59
+ :sensor_width: 3596
60
+ :color_tone: Normal
61
+ :measured_rggb: 641 1024 1024 660
62
+ :create_date: 2005-11-26 14:20:34 -08:00
63
+ :shooting_mode: Depth-of-field AE
64
+ :application_record_version: 4
65
+ :af_image_width: 3504
66
+ :wb_bracket_mode: "Off"
67
+ :lens_type: Unknown (-1)
68
+ :focal_units: 1/mm
69
+ :make: Canon
70
+ :safety_shift_in_av_or_tv: Enable
71
+ :aeb_bracket_value: 0
72
+ :profile_id: 0
73
+ :software: Picasa
74
+ :serial_number_format: Format 2
75
+ :measured_ev: 14.12
76
+ :shutter_curtain_sync: 1st-curtain sync
77
+ :white_balance_blue: 0
78
+ :exif_byte_order: Little-endian (Intel, II)
79
+ :short_focal: 17 mm
80
+ :device_attributes: Reflective, Glossy, Positive, Color
81
+ :region_area_y:
82
+ - 0.248716
83
+ - 0.486301
84
+ - 0.243365
85
+ - 0.360873
86
+ - 0.466182
87
+ :custom_rendered: Normal
88
+ :sensor_top_border: 19
89
+ :focal_plane_y_size: 15.37 mm
90
+ :continuous_drive: Single
91
+ :profile_cmm_type: appl
92
+ :drive_mode: Single-frame Shooting
93
+ :toning_effect: None
94
+ :red_balance: 1.795074
95
+ :af_area_y_positions: 504 270 270 4 4 4 -262 -262 -496
96
+ :mime_type: image/jpeg
97
+ :color_components: 3
98
+ :region_area_x:
99
+ - 0.549658
100
+ - 0.858162
101
+ - 0.730736
102
+ - 0.453196
103
+ - 0.118579
104
+ :wb_rggb_levels_shade: 2215 1023 1023 1341
105
+ :x_resolution: 72
106
+ :cmm_flags: Not Embedded, Independent
107
+ :focus_mode: One-shot AF
108
+ :"profile_description_ml-fi-fi": Kameran RGB-profiili
109
+ :blue_matrix_column: 0.15662 0.08336 0.71953
110
+ :canon_image_size: Large
111
+ :original_decision_data_offset: 3968887
112
+ :focal_plane_y_resolution: 3959.322034
113
+ :hyperfocal_distance: 3.76 m
114
+ :lens_id: Unknown 17-40mm
115
+ :target_aperture: 4
116
+ :color_temp_cloudy: 6000
117
+ :wb_rggb_levels_auto: 1822 1023 1023 1709
118
+ :envelope_record_version: 4
119
+ :shutter_speed: !ruby/object:Rational
120
+ denominator: 4000
121
+ numerator: 1
122
+ :color_space_data: "RGB "
123
+ :af_image_height: 2336
124
+ :zoom_source_width: 0
125
+ :exposure_compensation: 0
126
+ :long_exposure_noise_reduction: "On"
127
+ :light_value: 14.0
128
+ :region_applied_to_dimensions_unit: pixel
129
+ :manual_flash_output: n/a
130
+ :y_resolution: 72
131
+ :white_balance: Auto
132
+ :flash_exposure_comp: !ruby/object:Rational
133
+ denominator: 3
134
+ numerator: 2
135
+ :superimposed_display: "On"
136
+ :date_time_original: 2005-11-26 14:20:34 -08:00
137
+ :wb_rggb_levels_cloudy: 2087 1023 1023 1459
138
+ :region_applied_to_dimensions_h: 2336
139
+ :interop_version: "0100"
140
+ :flash_bits: (none)
141
+ :shutter_speed_value: !ruby/object:Rational
142
+ denominator: 4000
143
+ numerator: 1
144
+ :source_file: test/faces.jpg
145
+ :file_type: JPEG
146
+ :file_name: faces.jpg
147
+ :iptc_digest: b443520a10119da99c2550175e6d0efb
148
+ :filter_effect: None
149
+ :sensor_right_border: 3587
150
+ :canon_exposure_mode: Depth-of-field AE
151
+ :"profile_description_ml-es-es": "Perfil RGB para C\xC3\xA1mara"
152
+ :profile_description_ml: Camera RGB Profile
153
+ :resolution_unit: inches
154
+ :region_area_w:
155
+ - 0.0667808
156
+ - 0.103881
157
+ - 0.0687785
158
+ - 0.0753424
159
+ - 0.11387
160
+ :bracket_value: 0
161
+ :digital_zoom: None
162
+ :y_cb_cr_positioning: Co-sited
163
+ :exif_image_width: 3504
164
+ :af_points_in_focus: 0,1,2,3,4,5,6,7,8
165
+ :color_temp_shade: 7000
166
+ :sensor_blue_level: 0
167
+ :canon_image_type: Canon EOS 20D
168
+ :region_applied_to_dimensions_w: 3504
169
+ :num_af_points: 9
170
+ :exif_version: "0221"
171
+ :y_cb_cr_sub_sampling: YCbCr4:2:2 (2 1)
172
+ :color_temp_tungsten: 3203
173
+ :aeb_sequence_auto_cancel: 0,-,+/Enabled
174
+ :metering_mode: Center-weighted average
175
+ :thumbnail_length: 12170
176
+ :green_matrix_column: 0.35332 0.67441 0.09042
177
+ :scale_factor35efl: 1.6
178
+ :image_size: 3504x2336
179
+ :measured_ev2: 14.125
180
+ :focus_continuous: Single
181
+ :primary_platform: Apple Computer Inc.
182
+ :lens_af_stop_button: ONE SHOT <-> AI SERVO
183
+ :media_white_point: 0.95047 1 1.0891
184
+ :image_height: 2336
185
+ :file_permissions: rw-r--r--
186
+ :lens: 17.0 - 40.0 mm
187
+ :add_original_decision_data: "On"
188
+ :"profile_description_ml-nl-nl": RGB-profiel Camera
189
+ :profile_class: Input Device Profile
190
+ :flash: Off, Did not fire
191
+ :digital_gain: 0
192
+ :thumbnail_image: (Binary data 12170 bytes)
193
+ :exif_image_height: 2336
194
+ :iso_expansion: "Off"
195
+ :red_matrix_column: 0.45427 0.24263 0.01482
196
+ :macro_mode: Normal
197
+ :aperture: 4.0
198
+ :color_temp_as_shot: 4599
199
+ :rendering_intent: Perceptual
200
+ :directory: test
201
+ :wb_rggb_levels_flash: 2112 1023 1023 1414
202
+ :mirror_lockup: Disable
203
+ :menu_button_display_position: Previous (top if power off)
204
+ :profile_copyright: Copyright 2003 Apple Computer Inc., all rights reserved.
205
+ :easy_mode: Manual
206
+ :auto_exposure_bracketing: "Off"
207
+ :color_temp_flash: 6303
208
+ :sensor_height: 2360
209
+ :auto_iso: 100
210
+ :nd_filter: n/a
211
+ :set_function_when_shooting: Change quality
212
+ :green_trc: (Binary data 14 bytes)
213
+ :vrd_offset: 0
214
+ :exposure_level_increments: !ruby/object:Rational
215
+ denominator: 3
216
+ numerator: 1
217
+ :wb_bracket_value_gm: 0
218
+ :region_area_h:
219
+ - 0.119007
220
+ - 0.1875
221
+ - 0.12286
222
+ - 0.101884
223
+ - 0.189212
224
+ :wb_rggb_levels_fluorescent: 1743 1023 1023 2205
225
+ :orientation: Horizontal (normal)
226
+ :exposure_time: !ruby/object:Rational
227
+ denominator: 4000
228
+ numerator: 1
229
+ :components_configuration: Y, Cb, Cr, -
230
+ :color_temp_daylight: 5200
231
+ :region_area_unit:
232
+ - normalized
233
+ - normalized
234
+ - normalized
235
+ - normalized
236
+ - normalized
237
+ :color_temperature: 4300
238
+ :thumbnail_offset: 4036
239
+ :sharpness_frequency: n/a
240
+ :wb_shift_gm: 0
241
+ :"profile_description_ml-da-dk": RGB-beskrivelse til Kamera
242
+ :region_name:
243
+ - Matthew McEachen
244
+ - Karen McEachen
245
+ - Ruth McEachen
246
+ - James McEachen
247
+ - Jamie McEachen
248
+ :focal_length: 17.0 mm
249
+ :device_manufacturer: appl
250
+ :slow_shutter: None
251
+ :canon_image_width: 3504
252
+ :focal_length35efl: "17.0 mm (35 mm equivalent: 26.6 mm)"
253
+ :warning: Invalid original decision data
254
+ :bulb_duration: 0
255
+ :color_temp_fluorescent: 3961
256
+ :bracket_shot_number: 0
257
+ :"profile_description_ml-pt-br": "Perfil RGB de C\xC3\xA2mera"
258
+ :picture_style: None
259
+ :bits_per_sample: 8
260
+ :region_type:
261
+ - Face
262
+ - Face
263
+ - Face
264
+ - Face
265
+ - Face
266
+ :canon_firmware_version: Firmware 2.0.3
267
+ :sharpness: 0
268
+ :file_number: 326-2647
269
+ :modify_date: 2012-02-15 22:58:07 -08:00
270
+ :"profile_description_ml-zh-tw": !binary |
271
+ 5pW45L2N55u45qmfIFJHQiDoibLlvanmj4/ov7A=
272
+
273
+ :self_timer: "Off"
274
+ :encoding_process: Baseline DCT, Huffman coding
275
+ :color_temp_auto: 4599
276
+ :profile_creator: appl
277
+ :af_assist_beam: Emits
278
+ :optical_zoom_code: n/a
279
+ :contrast: Normal
280
+ :white_balance_red: 0
281
+ :owner_name: unknown
282
+ :xmp_toolkit: XMP Core 5.1.2
283
+ :canon_model_id: EOS 20D
284
+ :wb_bracket_value_ab: 0
285
+ :target_exposure_time: !ruby/object:Rational
286
+ denominator: 4008
287
+ numerator: 1
288
+ :auto_rotate: None
289
+ :bracket_mode: "Off"
290
+ :valid_af_points: 9
291
+ :scene_capture_type: Standard
292
+ :exposure_mode: Auto
293
+ :tone_curve: Standard
294
+ :chromatic_adaptation: 1.04788 0.02292 -0.0502 0.02957 0.99049 -0.01706 -0.00923 0.01508 0.75165
295
+ :long_focal: 40 mm
296
+ :file_modify_date: 2012-02-19 21:49:48 -08:00
297
+ :flash_sync_speed_av: Auto
298
+ :"profile_description_ml-sv-se": "RGB-profil f\xC3\xB6r Kamera"
299
+ :profile_connection_space: "XYZ "
300
+ :wb_rggb_levels_daylight: 1933 1023 1023 1607
301
+ :black_mask_right_border: 0
302
+ :camera_type: EOS Mid-range
303
+ :"profile_description_ml-de-de": "RGB-Profil f\xC3\xBCr Kameras"
304
+ :record_mode: JPEG
305
+ :interop_index: R98 - DCF basic file (sRGB)
306
+ :profile_date_time: 2003-07-01 00:00:00 -07:00
307
+ :base_iso: 400
308
+ :focal_plane_x_resolution: 3959.322034
309
+ :flashpix_version: "0100"
310
+ :canon_image_height: 2336
311
+ :"profile_description_ml-no-no": RGB-kameraprofil
312
+ :profile_description: Camera RGB Profile
313
+ :wb_rggb_levels_tungsten: 1382 1023 1023 2574
314
+ :black_mask_top_border: 0
315
+ :exposure_program: Not Defined
316
+ :"profile_description_ml-fr-fu": "Profil RVB de l\xE2\x80\x99appareil-photo"
317
+ :blue_balance: 1.683744
318
+ :ettlii: Evaluative
319
+ :image_width: 3504
320
+ :"shutter-ae_lock": AF/AE lock
321
+ :af_area_height: 78
322
+ :sensor_bottom_border: 2354
323
+ :af_area_width: 78
324
+ :flash_firing: Fires
325
+ :circle_of_confusion: 0.019 mm
326
+ :canon_flash_mode: "Off"
327
+ :f_number: 4.0
Binary file
@@ -0,0 +1,80 @@
1
+ ---
2
+ :gps_latitude_ref: North
3
+ :model: iPhone 4S
4
+ :color_space: sRGB
5
+ :components_configuration: Y, Cb, Cr, -
6
+ :exif_byte_order: Big-endian (Motorola, MM)
7
+ :sensing_method: One-chip color area
8
+ :create_date: 2011-11-23 09:39:52 -08:00
9
+ :bits_per_sample: 8
10
+ :date_time_original: 2011-11-23 09:39:52 -08:00
11
+ :source_file: test/iPhone 4S.jpg
12
+ :gps_img_direction: 121.9838275
13
+ :thumbnail_length: 5987
14
+ :exif_image_width: 3264
15
+ :exposure_program: Program AE
16
+ :compression: JPEG (old-style)
17
+ :image_size: 3264x2448
18
+ :mime_type: image/jpeg
19
+ :gps_longitude: -122.47566667
20
+ :x_resolution: 72
21
+ :orientation: Horizontal (normal)
22
+ :software: 5.0.1
23
+ :thumbnail_offset: 914
24
+ :gps_altitude_ref: Above Sea Level
25
+ :shutter_speed: !ruby/object:Rational
26
+ denominator: 6135
27
+ numerator: 1
28
+ :directory: test
29
+ :flashpix_version: "0100"
30
+ :y_cb_cr_positioning: Centered
31
+ :flash: Auto, Did not fire
32
+ :f_number: 2.4
33
+ :gps_altitude: 14.2 m Above Sea Level
34
+ :file_modify_date: 2012-02-19 21:49:48 -08:00
35
+ :y_resolution: 72
36
+ :encoding_process: Baseline DCT, Huffman coding
37
+ :thumbnail_image: (Binary data 5987 bytes)
38
+ :exposure_mode: Auto
39
+ :white_balance: Auto
40
+ :circle_of_confusion: 0.004 mm
41
+ :metering_mode: Multi-segment
42
+ :gps_latitude: 37.50233333
43
+ :color_components: 3
44
+ :image_height: 2448
45
+ :exif_tool_version: 8.59
46
+ :exposure_time: !ruby/object:Rational
47
+ denominator: 6135
48
+ numerator: 1
49
+ :gps_longitude_ref: West
50
+ :resolution_unit: inches
51
+ :scene_capture_type: Standard
52
+ :gps_position: 37.50233333 N, 122.47566667 W
53
+ :file_name: iPhone 4S.jpg
54
+ :hyperfocal_distance: 2.08 m
55
+ :focal_length35efl: "4.3 mm (35 mm equivalent: 35.0 mm)"
56
+ :y_cb_cr_sub_sampling: YCbCr4:2:0 (2 2)
57
+ :aperture: 2.4
58
+ :fov: 54.4 deg
59
+ :brightness_value: 11.27442702
60
+ :file_permissions: rw-r--r--
61
+ :gps_img_direction_ref: True North
62
+ :exif_version: "0221"
63
+ :sharpness: Normal
64
+ :gps_time_stamp: "19:03:32"
65
+ :aperture_value: 2.4
66
+ :exif_image_height: 2448
67
+ :focal_length: 4.3 mm
68
+ :light_value: 15.8
69
+ :modify_date: 2011-11-23 09:39:52 -08:00
70
+ :shutter_speed_value: !ruby/object:Rational
71
+ denominator: 6135
72
+ numerator: 1
73
+ :make: Apple
74
+ :file_size: 1954 kB
75
+ :subject_area: 1631 1223 881 881
76
+ :focal_length_in35mm_format: 35 mm
77
+ :image_width: 3264
78
+ :file_type: JPEG
79
+ :scale_factor35efl: 8.2
80
+ :iso: 64
@@ -0,0 +1,62 @@
1
+ require "test/unit"
2
+ require "exiftoolr"
3
+ require "yaml"
4
+
5
+ class TestExiftoolr < Test::Unit::TestCase
6
+
7
+ DUMP_RESULTS = false
8
+
9
+ def test_missing
10
+ assert_raise Exiftoolr::NoSuchFile do
11
+ Exiftoolr.new("no/such/file")
12
+ end
13
+ end
14
+
15
+ def test_directory
16
+ assert_raise Exiftoolr::NotAFile do
17
+ Exiftoolr.new("lib")
18
+ end
19
+ end
20
+
21
+ def test_invalid_exif
22
+ assert Exiftoolr.new("Gemfile").errors?
23
+ end
24
+
25
+ def test_matches
26
+ Dir["**/*.jpg"].each do |filename|
27
+ e = Exiftoolr.new(filename)
28
+ validate_result(e, filename)
29
+ end
30
+ end
31
+
32
+ def validate_result(result, filename)
33
+ yaml_file = "#{filename}.yaml"
34
+ if File.exist?(yaml_file)
35
+ assert !result.errors?
36
+ else
37
+ assert result.errors?
38
+ return
39
+ end
40
+ exif = result.to_hash
41
+ File.open(yaml_file, 'w') { |out| YAML.dump(exif, out) } if DUMP_RESULTS
42
+ e = File.open(yaml_file) { |f| YAML::load(f) }
43
+ exif.keys.each do |k|
44
+ next if [:file_modify_date, :directory, :source_file].include? k
45
+ assert_equal e[k], exif[k], "Key '#{k}' was incorrect for #{filename}"
46
+ end
47
+ end
48
+
49
+ def test_multi_matches
50
+ filenames = Dir["**/*.jpg"].to_a
51
+ e = Exiftoolr.new(filenames)
52
+ filenames.each { |f| validate_result(e.result_for(f), f) }
53
+ end
54
+
55
+ def test_error_filtering
56
+ filenames = Dir["**/*.*"].to_a
57
+ e = Exiftoolr.new(filenames)
58
+ expected_files = Dir["**/*.jpg"].to_a.collect{|f|File.expand_path(f)}.sort
59
+ assert_equal expected_files, e.files_with_results.sort
60
+ filenames.each { |f| validate_result(e.result_for(f), f) }
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exiftoolr
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 4
10
+ version: 0.0.4
11
+ platform: ruby
12
+ authors:
13
+ - Matthew McEachen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-02-23 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ name: json
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ hash: 3
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ type: :development
46
+ name: rake
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ type: :development
60
+ name: rspec
61
+ version_requirements: *id003
62
+ description: Multiget ExifTool wrapper for ruby
63
+ email:
64
+ - matthew-github@mceachen.org
65
+ executables: []
66
+
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - .gitignore
73
+ - Gemfile
74
+ - README.md
75
+ - Rakefile
76
+ - exiftoolr.gemspec
77
+ - lib/exiftoolr.rb
78
+ - lib/exiftoolr/result.rb
79
+ - lib/exiftoolr/version.rb
80
+ - test/Canon 20D.jpg
81
+ - test/Canon 20D.jpg.yaml
82
+ - test/Droid X.jpg
83
+ - test/Droid X.jpg.yaml
84
+ - test/faces.jpg
85
+ - test/faces.jpg.yaml
86
+ - test/iPhone 4S.jpg
87
+ - test/iPhone 4S.jpg.yaml
88
+ - test/test_exiftoolr.rb
89
+ homepage: https://github.com/mceachen/exif_tooler
90
+ licenses: []
91
+
92
+ post_install_message:
93
+ rdoc_options: []
94
+
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ hash: 3
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ requirements:
116
+ - ExifTool (see http://www.sno.phy.queensu.ca/~phil/exiftool/)
117
+ rubyforge_project: exiftoolr
118
+ rubygems_version: 1.8.17
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: Multiget ExifTool wrapper for ruby
122
+ test_files:
123
+ - test/Canon 20D.jpg
124
+ - test/Canon 20D.jpg.yaml
125
+ - test/Droid X.jpg
126
+ - test/Droid X.jpg.yaml
127
+ - test/faces.jpg
128
+ - test/faces.jpg.yaml
129
+ - test/iPhone 4S.jpg
130
+ - test/iPhone 4S.jpg.yaml
131
+ - test/test_exiftoolr.rb