exiftoolr 0.0.9 → 0.1.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 +4 -4
- data/.travis.yml +3 -2
- data/README.md +29 -17
- data/Rakefile +14 -2
- data/exiftoolr.gemspec +24 -20
- data/lib/exiftoolr.rb +6 -6
- data/lib/exiftoolr/parser.rb +63 -0
- data/lib/exiftoolr/result.rb +2 -47
- data/lib/exiftoolr/version.rb +1 -1
- data/test/Canon 20D.jpg.yaml +201 -199
- data/test/Droid X.jpg.yaml +74 -72
- data/test/IMG_2452.jpg +0 -0
- data/test/IMG_2452.jpg.yaml +211 -0
- data/test/exiftoolr_test.rb +87 -0
- data/test/faces.jpg.yaml +281 -264
- data/test/iPhone 4S.jpg.yaml +68 -66
- data/test/parser_test.rb +58 -0
- data/test/test_helper.rb +14 -0
- metadata +84 -4
- data/test/test_exiftoolr.rb +0 -82
data/test/iPhone 4S.jpg.yaml
CHANGED
@@ -1,80 +1,82 @@
|
|
1
|
-
---
|
2
|
-
:
|
3
|
-
:
|
4
|
-
:
|
5
|
-
:
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
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
|
1
|
+
---
|
2
|
+
:source_file: /Users/mrm/Dropbox/code/exiftoolr/test/iPhone 4S.jpg
|
3
|
+
:exif_tool_version: 9.31
|
4
|
+
:file_name: iPhone 4S.jpg
|
5
|
+
:directory: /Users/mrm/Dropbox/code/exiftoolr/test
|
6
|
+
:file_size: 1954 kB
|
7
|
+
:file_modify_date: 2012-02-23 05:22:18.000000000 +00:00
|
8
|
+
:file_access_date: 2013-07-14 20:25:41.000000000 +00:00
|
9
|
+
:file_inode_change_date: 2013-02-16 19:21:57.000000000 +00:00
|
10
|
+
:file_permissions: rw-r--r--
|
11
|
+
:file_type: JPEG
|
18
12
|
:mime_type: image/jpeg
|
19
|
-
:
|
20
|
-
:
|
13
|
+
:exif_byte_order: Big-endian (Motorola, MM)
|
14
|
+
:make: Apple
|
15
|
+
:model: iPhone 4S
|
21
16
|
:orientation: Horizontal (normal)
|
17
|
+
:x_resolution: 72
|
18
|
+
:y_resolution: 72
|
19
|
+
:resolution_unit: inches
|
22
20
|
:software: 5.0.1
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
21
|
+
:modify_date: 2011-11-23 09:39:52.000000000 +00:00
|
22
|
+
:y_cb_cr_positioning: Centered
|
23
|
+
:exposure_time: !ruby/object:Rational
|
26
24
|
denominator: 6135
|
27
25
|
numerator: 1
|
28
|
-
:directory: test
|
29
|
-
:flashpix_version: "0100"
|
30
|
-
:y_cb_cr_positioning: Centered
|
31
|
-
:flash: Auto, Did not fire
|
32
26
|
:f_number: 2.4
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
39
|
-
:
|
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
|
27
|
+
:exposure_program: Program AE
|
28
|
+
:iso: 64
|
29
|
+
:exif_version: '0221'
|
30
|
+
:date_time_original: 2011-11-23 09:39:52.000000000 +00:00
|
31
|
+
:create_date: 2011-11-23 09:39:52.000000000 +00:00
|
32
|
+
:components_configuration: Y, Cb, Cr, -
|
33
|
+
:shutter_speed_value: !ruby/object:Rational
|
47
34
|
denominator: 6135
|
48
35
|
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
36
|
:aperture_value: 2.4
|
66
|
-
:
|
37
|
+
:brightness_value: 11.27442702
|
38
|
+
:metering_mode: Multi-segment
|
39
|
+
:flash: Auto, Did not fire
|
67
40
|
: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
41
|
:subject_area: 1631 1223 881 881
|
42
|
+
:flashpix_version: '0100'
|
43
|
+
:color_space: sRGB
|
44
|
+
:exif_image_width: 3264
|
45
|
+
:exif_image_height: 2448
|
46
|
+
:sensing_method: One-chip color area
|
47
|
+
:exposure_mode: Auto
|
48
|
+
:white_balance: Auto
|
76
49
|
:focal_length_in35mm_format: 35 mm
|
50
|
+
:scene_capture_type: Standard
|
51
|
+
:sharpness: Normal
|
52
|
+
:gps_latitude_ref: North
|
53
|
+
:gps_longitude_ref: West
|
54
|
+
:gps_altitude_ref: Above Sea Level
|
55
|
+
:gps_time_stamp: '19:03:32'
|
56
|
+
:gps_img_direction_ref: True North
|
57
|
+
:gps_img_direction: 121.9838275
|
58
|
+
:compression: JPEG (old-style)
|
59
|
+
:thumbnail_offset: 914
|
60
|
+
:thumbnail_length: 5987
|
77
61
|
:image_width: 3264
|
78
|
-
:
|
62
|
+
:image_height: 2448
|
63
|
+
:encoding_process: Baseline DCT, Huffman coding
|
64
|
+
:bits_per_sample: 8
|
65
|
+
:color_components: 3
|
66
|
+
:y_cb_cr_sub_sampling: YCbCr4:2:0 (2 2)
|
67
|
+
:aperture: 2.4
|
68
|
+
:gps_altitude: 14.2 m Above Sea Level
|
69
|
+
:gps_latitude: 37.50233333
|
70
|
+
:gps_longitude: -122.47566667
|
71
|
+
:gps_position: 37.50233333 N, 122.47566667 W
|
72
|
+
:image_size: 3264x2448
|
79
73
|
:scale_factor35efl: 8.2
|
80
|
-
:
|
74
|
+
:shutter_speed: !ruby/object:Rational
|
75
|
+
denominator: 6135
|
76
|
+
numerator: 1
|
77
|
+
:thumbnail_image: (Binary data 5987 bytes)
|
78
|
+
:circle_of_confusion: 0.004 mm
|
79
|
+
:fov: 54.4 deg
|
80
|
+
:focal_length35efl: '4.3 mm (35 mm equivalent: 35.0 mm)'
|
81
|
+
:hyperfocal_distance: 2.08 m
|
82
|
+
:light_value: 15.8
|
data/test/parser_test.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Exiftoolr::Parser do
|
4
|
+
it 'creates snake-case symbolic keys properly' do
|
5
|
+
p = Exiftoolr::Parser.new('HyperfocalDistance', '')
|
6
|
+
p.sym_key.must_equal :hyperfocal_distance
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'creates display keys properly' do
|
10
|
+
p = Exiftoolr::Parser.new('InternalSerialNumber', '')
|
11
|
+
p.display_key.must_equal 'Internal Serial Number'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'parses date flags without warnings' do
|
15
|
+
p = Exiftoolr::Parser.new('DateStampMode', 'Off')
|
16
|
+
p.value.must_equal 'Off'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'parses dates without timezones' do
|
20
|
+
p = Exiftoolr::Parser.new('CreateDate', '2004:09:19 12:25:20')
|
21
|
+
p.value.must_equal Time.parse('2004-09-19 12:25:20')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'parses sub-second times' do
|
25
|
+
p = Exiftoolr::Parser.new('SubSecDateTimeOriginal', '2011:09:25 20:08:09.00')
|
26
|
+
p.value.must_equal Time.parse('2011-09-25 20:08:09')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'parses dates with timezones' do
|
30
|
+
p = Exiftoolr::Parser.new('FileAccessDate', '2013:07:14 10:50:33-07:00')
|
31
|
+
p.value.must_equal Time.parse('2013-07-14 10:50:33-07:00')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'parses fractions properly' do
|
35
|
+
p = Exiftoolr::Parser.new('ShutterSpeedValue', '1/6135')
|
36
|
+
p.value.must_equal Rational(1, 6135)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'parses N GPS coords' do
|
40
|
+
p = Exiftoolr::Parser.new('GPSLatitude', '37.50233333 N')
|
41
|
+
p.value.must_be_close_to 37.50233333
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'parses S GPS coords' do
|
45
|
+
p = Exiftoolr::Parser.new('GPSLatitude', '37.50233333 S')
|
46
|
+
p.value.must_be_close_to -37.50233333
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'parses E GPS coords' do
|
50
|
+
p = Exiftoolr::Parser.new('GPSLongitude', '122.47566667 E')
|
51
|
+
p.value.must_be_close_to 122.47566667
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'parses W GPS coords' do
|
55
|
+
p = Exiftoolr::Parser.new('GPSLongitude', '122.47566667 W')
|
56
|
+
p.value.must_be_close_to -122.47566667
|
57
|
+
end
|
58
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'minitest/great_expectations'
|
3
|
+
require 'yaml'
|
4
|
+
require 'exiftoolr'
|
5
|
+
|
6
|
+
# We need a predictable timezone offset so non-tz-offset timestamps are comparable:
|
7
|
+
ENV['TZ'] = 'UTC'
|
8
|
+
|
9
|
+
unless ENV['CI']
|
10
|
+
require 'minitest/reporters'
|
11
|
+
MiniTest::Reporters.use!
|
12
|
+
end
|
13
|
+
|
14
|
+
puts "Exiftoolr.exiftool_version = #{Exiftoolr.exiftool_version}"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exiftoolr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew McEachen
|
@@ -38,6 +38,76 @@ dependencies:
|
|
38
38
|
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest-great_expectations
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: minitest-reporters
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
41
111
|
description: Multiget ExifTool wrapper for ruby
|
42
112
|
email:
|
43
113
|
- matthew-github@mceachen.org
|
@@ -52,17 +122,22 @@ files:
|
|
52
122
|
- Rakefile
|
53
123
|
- exiftoolr.gemspec
|
54
124
|
- lib/exiftoolr.rb
|
125
|
+
- lib/exiftoolr/parser.rb
|
55
126
|
- lib/exiftoolr/result.rb
|
56
127
|
- lib/exiftoolr/version.rb
|
57
128
|
- test/Canon 20D.jpg
|
58
129
|
- test/Canon 20D.jpg.yaml
|
59
130
|
- test/Droid X.jpg
|
60
131
|
- test/Droid X.jpg.yaml
|
132
|
+
- test/IMG_2452.jpg
|
133
|
+
- test/IMG_2452.jpg.yaml
|
134
|
+
- test/exiftoolr_test.rb
|
61
135
|
- test/faces.jpg
|
62
136
|
- test/faces.jpg.yaml
|
63
137
|
- test/iPhone 4S.jpg
|
64
138
|
- test/iPhone 4S.jpg.yaml
|
65
|
-
- test/
|
139
|
+
- test/parser_test.rb
|
140
|
+
- test/test_helper.rb
|
66
141
|
homepage: https://github.com/mceachen/exiftoolr
|
67
142
|
licenses:
|
68
143
|
- MIT
|
@@ -83,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
158
|
version: '0'
|
84
159
|
requirements:
|
85
160
|
- ExifTool (see http://www.sno.phy.queensu.ca/~phil/exiftool/)
|
86
|
-
rubyforge_project:
|
161
|
+
rubyforge_project:
|
87
162
|
rubygems_version: 2.0.2
|
88
163
|
signing_key:
|
89
164
|
specification_version: 4
|
@@ -93,8 +168,13 @@ test_files:
|
|
93
168
|
- test/Canon 20D.jpg.yaml
|
94
169
|
- test/Droid X.jpg
|
95
170
|
- test/Droid X.jpg.yaml
|
171
|
+
- test/IMG_2452.jpg
|
172
|
+
- test/IMG_2452.jpg.yaml
|
173
|
+
- test/exiftoolr_test.rb
|
96
174
|
- test/faces.jpg
|
97
175
|
- test/faces.jpg.yaml
|
98
176
|
- test/iPhone 4S.jpg
|
99
177
|
- test/iPhone 4S.jpg.yaml
|
100
|
-
- test/
|
178
|
+
- test/parser_test.rb
|
179
|
+
- test/test_helper.rb
|
180
|
+
has_rdoc:
|
data/test/test_exiftoolr.rb
DELETED
@@ -1,82 +0,0 @@
|
|
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_no_files
|
22
|
-
assert !Exiftoolr.new([]).errors?
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_invalid_exif
|
26
|
-
assert Exiftoolr.new("Gemfile").errors?
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_matches
|
30
|
-
Dir["**/*.jpg"].each do |filename|
|
31
|
-
e = Exiftoolr.new(filename)
|
32
|
-
validate_result(e, filename)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def validate_result(result, filename)
|
37
|
-
yaml_file = "#{filename}.yaml"
|
38
|
-
if File.exist?(yaml_file)
|
39
|
-
assert !result.errors?
|
40
|
-
else
|
41
|
-
assert result.errors?
|
42
|
-
return
|
43
|
-
end
|
44
|
-
exif = result.to_hash
|
45
|
-
File.open(yaml_file, 'w') { |out| YAML.dump(exif, out) } if DUMP_RESULTS
|
46
|
-
e = File.open(yaml_file) { |f| YAML::load(f) }
|
47
|
-
exif.keys.each do |k|
|
48
|
-
next if ignorable_key?(k)
|
49
|
-
expected = e[k]
|
50
|
-
actual = exif[k]
|
51
|
-
if actual.is_a?(String)
|
52
|
-
expected.downcase!
|
53
|
-
actual.downcase!
|
54
|
-
end
|
55
|
-
assert_equal expected, actual, "Key '#{k}' was incorrect for #{filename}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
TRANSLATED_KEY = /.*\-ml-\w\w-\w\w$/
|
60
|
-
|
61
|
-
def ignorable_key? key
|
62
|
-
@ignorable_keys ||= begin
|
63
|
-
ignorable = [:file_modify_date, :directory, :source_file, :exif_tool_version]
|
64
|
-
ignorable += [:modify_date, :create_date, :date_time_original, :nd_filter] if Exiftoolr.exiftool_version <= 8.2
|
65
|
-
ignorable
|
66
|
-
end.include?(key) || key.to_s =~ TRANSLATED_KEY
|
67
|
-
end
|
68
|
-
|
69
|
-
def test_multi_matches
|
70
|
-
filenames = Dir["**/*.jpg"].to_a
|
71
|
-
e = Exiftoolr.new(filenames)
|
72
|
-
filenames.each { |f| validate_result(e.result_for(f), f) }
|
73
|
-
end
|
74
|
-
|
75
|
-
def test_error_filtering
|
76
|
-
filenames = Dir["**/*.*"].to_a
|
77
|
-
e = Exiftoolr.new(filenames)
|
78
|
-
expected_files = Dir["**/*.jpg"].to_a.collect { |f| File.expand_path(f) }.sort
|
79
|
-
assert_equal expected_files, e.files_with_results.sort
|
80
|
-
filenames.each { |f| validate_result(e.result_for(f), f) }
|
81
|
-
end
|
82
|
-
end
|