exiftoolr 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ecd656c3093b0eb6f3f94cc402bf5ee14a9b13ba
4
- data.tar.gz: a8129d314f15fe908720f07393581b251ae95bfa
3
+ metadata.gz: 86c98c6b3dee0ab28c3ca8f2d5a5707abceb771a
4
+ data.tar.gz: 0e3a10c593d43ad8a1eac4cad6e2771f3adb82d8
5
5
  SHA512:
6
- metadata.gz: d8bcb6cc7a2dce7917f64c00e616f2493662562ff11ecfbc1bc49665311fd372c6ce057db7e343d45e7cd09e815cdf1ea3dd1f0bed395e086e57c17bbb62c685
7
- data.tar.gz: 051740e233bd226e7293e4dd332bd22f1de4cc785f670bd3f43b324481d958c75f7ae515199bb8e654f58bd7c002ac3e8a2487b46dbc8f32068909b5d892d9e4
6
+ metadata.gz: 9bfcc98ac2e68a9d958d1879170ab6adae73e026239f5978c1db7d0fd887f25796dda5b30cb3d94f270deb4b1f056aee62166295978eebeee6672fe8588da130
7
+ data.tar.gz: 232d3ac4d5d4f0993d6801a7ce16ff6681185be71bc2ca6a9306cfe35e95666e419e667da6b7c95c2264e8e30f2152b7a53be1ac9d9f4b88066fa44add6846dc
data/README.md CHANGED
@@ -8,77 +8,19 @@ This gem is the simplest thing that could possibly work that
8
8
  reads the output of [exiftool](http://www.sno.phy.queensu.ca/~phil/exiftool)
9
9
  and renders it into a ruby hash, with correctly typed values and symbolized keys.
10
10
 
11
- Rubies 1.9 and later are supported.
11
+ Note that this gem was renamed from 'exiftoolr' to the less-ungainly 'exiftool'
12
+ as of version 0.2.0. This gem simply adds a "class alias" from Exiftoolr to Exiftool,
13
+ and is dependant on that new gem.
12
14
 
13
- ## What constitutes "correct values"?
15
+ Consumers should switch to the new gem's namespace as soon as possible.
14
16
 
15
- * GPS latitude and longitude are rendered as signed floats,
16
- where north and east are positive, and west and south are negative.
17
- * Values like shutter speed and exposure time are rendered as Rationals,
18
- which lets the caller show them as fractions (1/250) or as comparable numeric instances.
19
- * String values like "interop" and "serial number" are kept as strings
20
- (which preserves zero prefixes)
21
- * Timestamps are attempted to be interpreted with correct timezones and sub-second resolution, if
22
- the header contains that data.
23
- Please note that EXIF headers don't always include a timezone offset, so we just adopt the system
24
- timezone, which may, of course, be wrong.
17
+ Go to [exiftool](https://github.com/mceachen/exiftool) for more information!
25
18
 
26
- ## Usage
27
-
28
- ```ruby
29
- require 'exiftoolr'
30
- e = Exiftoolr.new("path/to/iPhone 4S.jpg")
31
- e.to_hash
32
- # => {:make => "Apple", :gps_longitude => -122.47566667, …
33
- e.to_display_hash
34
- # => {"Make" => "Apple", "GPS Longitude" => -122.47566667, …
35
- ```
36
-
37
- ### Multiple file support
38
-
39
- This gem supports Exiftool's multiget, which lets you fetch metadata for many files at once.
40
-
41
- This can be dramatically more efficient (like, 60x faster) than spinning up the ```exiftool```
42
- process for each file.
43
-
44
- Supply an array to the Exiftoolr initializer, then use ```.result_for```:
45
-
46
- ```ruby
47
- require 'exiftoolr'
48
- e = Exiftoolr.new(Dir["**/*.jpg"])
49
- result = e.result_for("path/to/iPhone 4S.jpg")
50
- result.to_hash
51
- # => {:make => "Apple", :gps_longitude => -122.47566667, …
52
- result[:gps_longitude]
53
- # => -122.47566667
54
-
55
- e.files_with_results
56
- # => ["path/to/iPhone 4S.jpg", "path/to/Droid X.jpg", …
57
- ```
58
-
59
- ### When things go wrong
60
-
61
- * ```Exiftoolr::NoSuchFile``` is raised if the provided filename doesn't exist.
62
- * ```Exiftoolr::ExiftoolNotInstalled``` is raised if ```exiftool``` isn't in your ```PATH```.
63
- * If ExifTool has a problem reading EXIF data, no exception is raised, but ```#errors?``` will return true:
64
-
65
- ```ruby
66
- Exiftoolr.new("Gemfile").errors?
67
- #=> true
68
- ```
69
-
70
-
71
- ## Installation
72
-
73
- First [install ExifTool](http://www.sno.phy.queensu.ca/~phil/exiftool/install.html).
74
-
75
- Then, add this your Gemfile:
76
-
77
- gem 'exiftoolr'
19
+ ## Change history
78
20
 
79
- and then run ```bundle```.
21
+ ### 0.2.0
80
22
 
81
- ## Change history
23
+ * Renamed to 'exiftool' (but kept backward compatibility)
82
24
 
83
25
  ### 0.1.0
84
26
 
@@ -1,11 +1,10 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'exiftoolr/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
6
  spec.name = 'exiftoolr'
8
- spec.version = Exiftoolr::VERSION
7
+ spec.version = Gem::Version.new('0.2.0')
9
8
  spec.authors = ['Matthew McEachen']
10
9
  spec.email = %w(matthew-github@mceachen.org)
11
10
  spec.homepage = 'https://github.com/mceachen/exiftoolr'
@@ -21,6 +20,7 @@ Gem::Specification.new do |spec|
21
20
  spec.requirements << 'ExifTool (see http://www.sno.phy.queensu.ca/~phil/exiftool/)'
22
21
 
23
22
  spec.add_dependency 'json'
23
+ spec.add_dependency 'exiftool'
24
24
  spec.add_development_dependency 'rake'
25
25
  spec.add_development_dependency 'bundler'
26
26
  spec.add_development_dependency 'yard'
@@ -1,73 +1,3 @@
1
- require 'json'
2
- require 'shellwords'
3
- require 'exiftoolr/result'
1
+ require 'exiftool'
4
2
 
5
- class Exiftoolr
6
- class NoSuchFile < StandardError ; end
7
- class NotAFile < StandardError ; end
8
- class ExiftoolNotInstalled < StandardError ; end
9
-
10
- def self.exiftool_installed?
11
- exiftool_version > 0
12
- end
13
-
14
- def self.exiftool_version
15
- @@exiftool_version ||= `exiftool -ver 2> /dev/null`.to_f
16
- end
17
-
18
- def self.expand_path(filename)
19
- raise(NoSuchFile, filename) unless File.exist?(filename)
20
- raise(NotAFile, filename) unless File.file?(filename)
21
- File.expand_path(filename)
22
- end
23
-
24
- def initialize(filenames, exiftool_opts = '')
25
- @file2result = {}
26
- filenames = [filenames] if filenames.is_a?(String)
27
- unless filenames.empty?
28
- escaped_filenames = filenames.collect do |f|
29
- Shellwords.escape(self.class.expand_path(f.to_s))
30
- end.join(" ")
31
- # I'd like to use -dateformat, but it doesn't support timezone offsets properly,
32
- # nor sub-second timestamps.
33
- cmd = "exiftool #{exiftool_opts} -j -coordFormat \"%.8f\" #{escaped_filenames} 2> /dev/null"
34
- json = `#{cmd}`
35
- raise ExiftoolNotInstalled if json == ""
36
- JSON.parse(json).each do |raw|
37
- result = Result.new(raw)
38
- @file2result[result.source_file] = result
39
- end
40
- end
41
- end
42
-
43
- def result_for(filename)
44
- @file2result[self.class.expand_path(filename)]
45
- end
46
-
47
- def files_with_results
48
- @file2result.values.collect { |r| r.source_file unless r.errors? }.compact
49
- end
50
-
51
- def to_hash
52
- first.to_hash
53
- end
54
-
55
- def to_display_hash
56
- first.to_display_hash
57
- end
58
-
59
- def symbol_display_hash
60
- first.symbol_display_hash
61
- end
62
-
63
- def errors?
64
- @file2result.values.any? { |ea| ea.errors? }
65
- end
66
-
67
- private
68
-
69
- def first
70
- raise InvalidArgument, 'use #result_for when multiple filenames are used' if @file2result.size > 1
71
- @file2result.values.first
72
- end
73
- end
3
+ Exiftoolr = Exiftool
@@ -18,7 +18,7 @@ describe Exiftoolr do
18
18
  end
19
19
 
20
20
  it 'has errors with files without EXIF headers' do
21
- e = Exiftoolr.new("Gemfile")
21
+ e = Exiftoolr.new('Gemfile')
22
22
  e.errors?.must_be_true
23
23
  end
24
24
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exiftoolr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew McEachen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-14 00:00:00.000000000 Z
11
+ date: 2013-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: exiftool
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -122,9 +136,6 @@ files:
122
136
  - Rakefile
123
137
  - exiftoolr.gemspec
124
138
  - lib/exiftoolr.rb
125
- - lib/exiftoolr/parser.rb
126
- - lib/exiftoolr/result.rb
127
- - lib/exiftoolr/version.rb
128
139
  - test/Canon 20D.jpg
129
140
  - test/Canon 20D.jpg.yaml
130
141
  - test/Droid X.jpg
@@ -1,63 +0,0 @@
1
- require 'time'
2
- require 'date'
3
- require 'rational'
4
-
5
- class Exiftoolr
6
- class Parser
7
-
8
- WORD_BOUNDARY_RES = [/([A-Z\d]+)([A-Z][a-z])/, /([a-z\d])([A-Z])/]
9
- FRACTION_RE = /^(\d+)\/(\d+)$/
10
-
11
- attr_reader :key, :display_key, :sym_key, :raw_value
12
-
13
- def initialize(key, raw_value)
14
- @key = key
15
- @display_key = WORD_BOUNDARY_RES.inject(key) { |k, regex| k.gsub(regex, '\1 \2') }
16
- @sym_key = display_key.downcase.gsub(' ', '_').to_sym
17
- @raw_value = raw_value
18
- end
19
-
20
- def value
21
- for_lat_long ||
22
- for_date ||
23
- for_fraction ||
24
- raw_value
25
- rescue StandardError => e
26
- "Warning: Parsing '#{raw_value}' for attribute '#{key}' raised #{e.message}"
27
- end
28
-
29
- private
30
-
31
- def for_lat_long
32
- if sym_key == :gps_latitude || sym_key == :gps_longitude
33
- value, direction = raw_value.split(" ")
34
- if value =~ /\A\d+\.?\d*\z/
35
- value.to_f * (['S', 'W'].include?(direction) ? -1 : 1)
36
- end
37
- end
38
- end
39
-
40
- def for_date
41
- if raw_value.is_a?(String) && display_key =~ /\bdate\b/i
42
- try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S%z') } ||
43
- try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S') } ||
44
- try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S.%L%z') } ||
45
- try_parse { Time.strptime(raw_value, '%Y:%m:%d %H:%M:%S.%L') } ||
46
- try_parse { Time.parse(raw_value) }
47
- end
48
- end
49
-
50
- def try_parse
51
- yield
52
- rescue ArgumentError
53
- nil
54
- end
55
-
56
- def for_fraction
57
- if raw_value.is_a?(String)
58
- scan = raw_value.scan(FRACTION_RE).first
59
- v = Rational(*scan.map { |ea| ea.to_i }) unless scan.nil?
60
- end
61
- end
62
- end
63
- end
@@ -1,32 +0,0 @@
1
- require 'exiftoolr/parser'
2
-
3
- class Exiftoolr
4
- class Result
5
- attr_reader :to_hash, :to_display_hash, :symbol_display_hash
6
-
7
- def initialize(raw_hash)
8
- @raw_hash = raw_hash
9
- @to_hash = {}
10
- @to_display_hash = {}
11
- @symbol_display_hash = {}
12
- @raw_hash.each do |key, raw_value|
13
- p = Parser.new(key, raw_value)
14
- @to_hash[p.sym_key] = p.value
15
- @to_display_hash[p.display_key] = p.value
16
- @symbol_display_hash[p.sym_key] = p.display_key
17
- end
18
- end
19
-
20
- def [](key)
21
- @to_hash[key]
22
- end
23
-
24
- def source_file
25
- self[:source_file]
26
- end
27
-
28
- def errors?
29
- self[:error] == 'Unknown file type' || self[:warning] == 'Unsupported file type'
30
- end
31
- end
32
- end
@@ -1,3 +0,0 @@
1
- class Exiftoolr
2
- VERSION = Gem::Version.new('0.1.0')
3
- end