Class: Exiftool

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/exiftool.rb,
lib/exiftool/result.rb,
lib/exiftool/version.rb,
lib/exiftool/field_parser.rb

Overview

Exiftool Class

Defined Under Namespace

Classes: ExiftoolNotInstalled, FieldParser, NoDefaultResultWithMultiget, NoSuchFile, NotAFile, Result

Constant Summary collapse

VERSION =
Gem::Version.new('1.3.1')

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filenames, exiftool_opts = '') ⇒ Exiftool

Returns a new instance of Exiftool.

Raises:



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/exiftool.rb', line 63

def initialize(filenames, exiftool_opts = '')
  @file2result = {}
  io_input = nil
  if filenames.is_a?(IO) || filenames.is_a?(StringIO)
    io_input = filenames
    filenames = ['-']
  end

  filenames = [filenames] if filenames.is_a?(String) || filenames.is_a?(Pathname)
  return if filenames.empty?

  expanded_filenames = filenames.map do |f|
    f == '-' ? '-' : self.class.expand_path(f.to_s)
  end
  args = [
    self.class.command,
    *Shellwords.split(exiftool_opts),
    '-j',
    '-coordFormat', '%.8f',
    *expanded_filenames
  ]

  json = ''
  begin
    Open3.popen3(*args) do |stdin, stdout, _stderr, wait_thr|
      if io_input
        IO.copy_stream(io_input, stdin)
        stdin.close
      end
      json = stdout.read.to_s.chomp
      wait_thr.value
    end
  rescue Errno::ENOENT
    json = ''
  end

  raise ExiftoolNotInstalled if json == ''

  JSON.parse(json).each do |raw|
    result = Result.new(raw)
    @file2result[result.source_file] = result
  end
end

Class Attribute Details

.commandObject



25
26
27
# File 'lib/exiftool.rb', line 25

def self.command
  @command ||= 'exiftool'
end

Class Method Details

.exiftool_installed?Boolean

Returns:



29
30
31
# File 'lib/exiftool.rb', line 29

def self.exiftool_installed?
  exiftool_version.to_f.positive?
end

.exiftool_versionObject

This is a string, not a float, to handle versions like “9.40” properly.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/exiftool.rb', line 34

def self.exiftool_version
  return @exiftool_version if defined?(@exiftool_version) && @exiftool_version

  stdout_str = ''
  begin
    Open3.popen3(command, '-ver') do |_stdin, stdout, _stderr, wait_thr|
      stdout_str = stdout.read.to_s.chomp
      # Ensure the process is reaped
      wait_thr.value
    end
  rescue Errno::ENOENT
    stdout_str = ''
  end

  @exiftool_version = stdout_str
end

.expand_path(filename) ⇒ Object

Raises:



51
52
53
54
55
56
57
# File 'lib/exiftool.rb', line 51

def self.expand_path(filename)
  raise(NoSuchFile, filename) unless File.exist?(filename)

  raise(NotAFile, filename) unless File.file?(filename)

  File.expand_path(filename)
end

Instance Method Details

#errors?Boolean

Returns:



123
124
125
# File 'lib/exiftool.rb', line 123

def errors?
  @file2result.values.any?(&:errors?)
end

#files_with_resultsObject



119
120
121
# File 'lib/exiftool.rb', line 119

def files_with_results
  results.map(&:source_file)
end

#result_for(filename) ⇒ Object



115
116
117
# File 'lib/exiftool.rb', line 115

def result_for(filename)
  @file2result[self.class.expand_path(filename)]
end

#results(include_results_with_errors: false) ⇒ Object



107
108
109
110
111
112
113
# File 'lib/exiftool.rb', line 107

def results(include_results_with_errors: false)
  if include_results_with_errors
    @file2result.values
  else
    @file2result.values.reject(&:errors?)
  end
end