multi_exiftool 0.12.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 317f91ac28f2791341c7c6354e996545c80f08fb7100607460295ae55c38d463
4
- data.tar.gz: d98f7b7a4a0cca8e0ce2e18f1b718ef7227412a31ff5e9b8f3d4685077d97221
3
+ metadata.gz: 43d4f9d91d7d4c6321b5e9698e3aaecc38fd1044e140110f5e4ff22fdf15ee96
4
+ data.tar.gz: a9ff99dd7943f29ce6219f75a3dad31ce06c3e4abf458e50aa6c9e8903660022
5
5
  SHA512:
6
- metadata.gz: 292b732d0940abb55c9dccebadfb8976767891ad33fb865e982689fd0b4a617590b95b195d1144831d073374357f68823dd51a4875962ef0374b0803753adf46
7
- data.tar.gz: b1d1c48bfdf082f7f79ebf5f5b2feefd0e661fcfb2aebc23b73cfec25606e195b2c989c5e36b05e57257e56ddbb0b0b5fef452ed3a07cb7479508062bc302fbe
6
+ metadata.gz: 648a9ba62793a9c0215c06cc701935bd123612d7491be111e0e96398e9f2bcc42399f2bf42591ce70c2722579e04d113394832c337ca04674036c2b93f014f2b
7
+ data.tar.gz: 17d5d839ba13f1a07f2ec586c78b68bb950e54f2e6f67f2288fcdb01dda5f5ff341db462b8d103fa510eb5eb190080cf883c8d85de16fdcda89aee994414cd4a
@@ -1,4 +1,4 @@
1
- personal_ws-1.1 en 33
1
+ personal_ws-1.1 en 34
2
2
  Coenen
3
3
  ExifTool
4
4
  Friedrich
@@ -13,6 +13,7 @@ README
13
13
  SemVer
14
14
  SemVerTag
15
15
  api
16
+ config
16
17
  dir
17
18
  exiftool
18
19
  filenames
data/Changelog CHANGED
@@ -1,3 +1,20 @@
1
+ 0.16.0
2
+ Add option to use config files.
3
+
4
+ 0.15.0
5
+ Make multi_exiftool work with Ruby 1.9 again.
6
+
7
+ 0.14.1
8
+ Fix two typos.
9
+
10
+ 0.14.0
11
+ Improve api for batch processing.
12
+
13
+ 0.13.0
14
+ Implement batch processing to allow to write different values to multiple file
15
+ with only one call of the ExifTool command-line application.
16
+ Update documentation.
17
+
1
18
  0.12.0
2
19
  Implement Values#respond_to_missing?.
3
20
  Improve documentation and fix example code.
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ## Description
4
4
 
5
5
  This library is a wrapper for the ExifTool command-line application
6
- (http://www.sno.phy.queensu.ca/~phil/exiftool) written by Phil Harvey.
6
+ (https://exiftool.org) written by Phil Harvey.
7
7
  It is designed for dealing with multiple files at once by creating
8
8
  commands to call exiftool with various arguments, call it and parsing
9
9
  the results.
@@ -60,6 +60,54 @@ else
60
60
  end
61
61
  ```
62
62
 
63
+ If it is necessary to write different values to multiple files there is batch processing
64
+
65
+ ```ruby
66
+ require 'multi_exiftool'
67
+
68
+ # Object oriented approach
69
+
70
+ batch = MultiExiftool::Batch.new
71
+ Dir['*.jpg'].each_with_index do |filename, i|
72
+ values = {creator: 'Jan Friedrich', copyright: 'Public Domain', comment: "This is file number #{i+1}."}
73
+ batch.write filename, values
74
+ end
75
+ if batch.execute
76
+ puts 'ok'
77
+ else
78
+ puts batch.errors
79
+ end
80
+
81
+ # Functional approach
82
+
83
+ errors = MultiExiftool.batch do
84
+ Dir['*.jpg'].each_with_index do |filename, i|
85
+ values = {creator: 'Jan Friedrich', copyright: 'Public Domain', comment: "This is file number #{i+1}."}
86
+ write filename, values
87
+ end
88
+ end
89
+ if errors.empty?
90
+ puts 'ok'
91
+ else
92
+ puts errors
93
+ end
94
+
95
+ # or alternative with block parameter as yielded Batch instance
96
+
97
+ errors = MultiExiftool.batch do |batch|
98
+ Dir['*.jpg'].each_with_index do |filename, i|
99
+ values = {creator: 'Jan Friedrich', copyright: 'Public Domain', comment: "This is file number #{i+1}."}
100
+ batch.write filename, values
101
+ end
102
+ end
103
+ if errors.empty?
104
+ puts 'ok'
105
+ else
106
+ puts errors
107
+ end
108
+ ```
109
+
110
+
63
111
  ### Deleting
64
112
 
65
113
  ```ruby
@@ -153,7 +201,7 @@ The method Values#convert is called each time a value is fetched.
153
201
  - Ruby 1.9.1 or higher
154
202
  - An installation of the ExifTool command-line application (version 7.65 or
155
203
  higher). Instructions for installation you can find under
156
- http://www.sno.phy.queensu.ca/~phil/exiftool/install.html .
204
+ https://exiftool.org/install.html .
157
205
  - If you have problems with special characters (like German umlauts) in
158
206
  filenames on windows system it is recommended to use exiftool version 9.79
159
207
  or higher.
@@ -185,7 +233,7 @@ SemVerTag.
185
233
 
186
234
  ## Author
187
235
 
188
- Jan Friedrich <janfri26gmail.com>
236
+ Jan Friedrich <janfri26@gmail.com>
189
237
 
190
238
  ## MIT License
191
239
 
data/Rakefile CHANGED
@@ -11,8 +11,8 @@ Rim.setup do |p|
11
11
  p.version = MultiExiftool::VERSION
12
12
  p.authors = 'Jan Friedrich'
13
13
  p.email = 'janfri26@gmail.com'
14
- p.summary = 'This library is a wrapper for the ExifTool command-line application (http://www.sno.phy.queensu.ca/~phil/exiftool).'
15
- p.description = 'This library a is wrapper for the ExifTool command-line application (http://www.sno.phy.queensu.ca/~phil/exiftool) written by Phil Harvey. It is designed for dealing with multiple files at once by creating commands to call exiftool with various arguments, call it and parsing the results.'
14
+ p.summary = 'This library is a wrapper for the ExifTool command-line application (https://exiftool.org).'
15
+ p.description = 'This library is a wrapper for the ExifTool command-line application (https://exiftool.org) written by Phil Harvey. It is designed for dealing with multiple files at once by creating commands to call exiftool with various arguments, call it and parsing the results.'
16
16
  p.ruby_version = '>=1.9.1'
17
17
  p.license = 'MIT'
18
18
  p.homepage = 'https://github.com/janfri/multi_exiftool'
@@ -21,7 +21,7 @@ Rim.setup do |p|
21
21
  | Please ensure you have installed exiftool version 7.65 or higher and |
22
22
  | it's found in your PATH (Try "exiftool -ver" on your commandline). |
23
23
  | For more details see |
24
- | http://www.sno.phy.queensu.ca/~phil/exiftool/install.html |
24
+ | https://exiftool.org/install.html |
25
25
  +-----------------------------------------------------------------------+
26
26
  }
27
27
  p.development_dependencies << %w(contest ~>0.1)
@@ -4,10 +4,11 @@
4
4
  require_relative 'multi_exiftool/values'
5
5
  require_relative 'multi_exiftool/reader'
6
6
  require_relative 'multi_exiftool/writer'
7
+ require_relative 'multi_exiftool/batch'
7
8
 
8
9
  module MultiExiftool
9
10
 
10
- VERSION = '0.12.0'
11
+ VERSION = '0.16.0'
11
12
 
12
13
  @exiftool_command = 'exiftool'
13
14
 
@@ -25,15 +26,7 @@ module MultiExiftool
25
26
  # # error handling
26
27
  # end
27
28
  def read filenames, opts={}
28
- reader = Reader.new
29
- reader.filenames = filenames
30
- if val = opts.delete(:tags)
31
- reader.tags = val
32
- end
33
- if val = opts.delete(:group)
34
- reader.group = val
35
- end
36
- reader.options = opts unless opts.empty?
29
+ reader = Reader.new(filenames, opts)
37
30
  values = reader.read
38
31
  [values, reader.errors]
39
32
  end
@@ -47,10 +40,7 @@ module MultiExiftool
47
40
  # # do error handling
48
41
  # end
49
42
  def write filenames, values, opts={}
50
- writer = Writer.new
51
- writer.filenames = filenames
52
- writer.values = values
53
- writer.options = opts unless opts.empty?
43
+ writer = Writer.new(filenames, values, opts)
54
44
  writer.write
55
45
  writer.errors
56
46
  end
@@ -70,11 +60,35 @@ module MultiExiftool
70
60
  # unless errors.empty?
71
61
  # # do error handling
72
62
  # end
73
- def delete_values filenames, tags: :all
63
+ def delete_values filenames, opts={}
64
+ tags = opts.fetch(:tags, :all)
74
65
  values = Array(tags).inject(Hash.new) {|h,tag| h[tag] = nil; h}
75
66
  write(filenames, values)
76
67
  end
77
68
 
69
+
70
+ # Execute a batch of write commands
71
+ # Returns an array of the error messages
72
+ #
73
+ # Example:
74
+ # errors = MultiExiftool.batch do
75
+ # Dir['*.jpg'].each_with_index do |filename, i|
76
+ # write filename, {author: 'Jan Friedrich', comment: "This is file number #{i+1}."}
77
+ # end
78
+ # unless errors.empty?
79
+ # # do error handling
80
+ # end
81
+ def batch &block
82
+ batch = Batch.new
83
+ if block.arity == 0
84
+ batch.instance_exec &block
85
+ else
86
+ yield batch
87
+ end
88
+ batch.execute
89
+ batch.errors
90
+ end
91
+
78
92
  attr_accessor :exiftool_command
79
93
  attr_reader :exiftool_version
80
94
 
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'executable'
5
+
6
+ module MultiExiftool
7
+
8
+ # Allow to define a batch for different writing operations as one call of the
9
+ # ExifTool command-line application
10
+ class Batch
11
+
12
+ include Executable
13
+
14
+ # Define batch operation inside a block
15
+ def initialize &blk
16
+ @writers = []
17
+ instance_exec &blk if block_given?
18
+ end
19
+
20
+ # Define a write operation for the batch.
21
+ def write filenames, values, options={}
22
+ w = MultiExiftool::Writer.new(filenames, values, options)
23
+ @writers << w
24
+ end
25
+
26
+ # Getting the command-line arguments which would be executed
27
+ # when calling #write. It could be useful for logging, debugging or
28
+ # whatever.
29
+ def exiftool_args
30
+ @writers.map {|w| w.exiftool_args + ['-execute']}.flatten
31
+ end
32
+
33
+ private
34
+
35
+ def parse_results
36
+ @errors = @stderr.read.split(/\n/)
37
+ @errors.empty?
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -9,12 +9,15 @@ module MultiExiftool
9
9
  module Executable
10
10
 
11
11
  attr_reader :errors
12
- attr_accessor :filenames, :numerical, :options
12
+ attr_accessor :config, :filenames, :numerical, :options
13
13
 
14
- def initialize
15
- @options = {}
16
- @filenames = []
14
+ def initialize filenames=[], options={}
15
+ @options = options
16
+ @filenames = filenames
17
17
  @option_mapping = {numerical: :n}
18
+ if val = options.delete(:config)
19
+ @config = val
20
+ end
18
21
  end
19
22
 
20
23
  def options
@@ -57,7 +60,11 @@ module MultiExiftool
57
60
  end
58
61
 
59
62
  def execute_command
60
- stdin, @stdout, @stderr = Open3.popen3(exiftool_command, '-@', '-')
63
+ args = ['-@', '-']
64
+ if @config
65
+ args = ['-config', @config] + args
66
+ end
67
+ stdin, @stdout, @stderr = Open3.popen3(exiftool_command, *args)
61
68
  exiftool_args.each do |part|
62
69
  stdin << part
63
70
  stdin << "\n"
@@ -15,9 +15,17 @@ module MultiExiftool
15
15
 
16
16
  include Executable
17
17
 
18
- def initialize
19
- super
20
- @tags = []
18
+ def initialize filenames=[], opts={}
19
+ super(filenames, opts)
20
+ if val = opts.delete(:tags)
21
+ @tags = val
22
+ else
23
+ @tags = []
24
+ end
25
+ if val = opts.delete(:group)
26
+ @group = val
27
+ end
28
+ @options = opts unless opts.empty?
21
29
  end
22
30
 
23
31
  def self.mandatory_args
@@ -14,9 +14,9 @@ module MultiExiftool
14
14
 
15
15
  include Executable
16
16
 
17
- def initialize
18
- super
19
- @values = {}
17
+ def initialize filenames=[], values={}, opts={}
18
+ super(filenames, opts)
19
+ @values = values
20
20
  end
21
21
 
22
22
  def self.mandatory_args
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: multi_exiftool 0.12.0 ruby lib
2
+ # stub: multi_exiftool 0.16.0 ruby lib
3
3
  #
4
4
  # This file is automatically generated by rim.
5
5
  # PLEASE DO NOT EDIT IT DIRECTLY!
@@ -7,22 +7,22 @@
7
7
 
8
8
  Gem::Specification.new do |s|
9
9
  s.name = "multi_exiftool"
10
- s.version = "0.12.0"
10
+ s.version = "0.16.0"
11
11
 
12
12
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
13
13
  s.require_paths = ["lib"]
14
14
  s.authors = ["Jan Friedrich"]
15
- s.date = "2020-01-02"
16
- s.description = "This library a is wrapper for the ExifTool command-line application (http://www.sno.phy.queensu.ca/~phil/exiftool) written by Phil Harvey. It is designed for dealing with multiple files at once by creating commands to call exiftool with various arguments, call it and parsing the results."
15
+ s.date = "2020-10-27"
16
+ s.description = "This library is a wrapper for the ExifTool command-line application (https://exiftool.org) written by Phil Harvey. It is designed for dealing with multiple files at once by creating commands to call exiftool with various arguments, call it and parsing the results."
17
17
  s.email = "janfri26@gmail.com"
18
- s.files = ["./.aspell.pws", "Changelog", "Gemfile", "LICENSE", "README.md", "Rakefile", "lib/multi_exiftool", "lib/multi_exiftool.rb", "lib/multi_exiftool/executable.rb", "lib/multi_exiftool/reader.rb", "lib/multi_exiftool/values.rb", "lib/multi_exiftool/writer.rb", "multi_exiftool.gemspec", "regtest/read_all_tags.rb", "regtest/read_all_tags.yml", "regtest/test.jpg", "test/data", "test/data/a.jpg", "test/data/b.jpg", "test/data/c.jpg", "test/helper.rb", "test/test_executable.rb", "test/test_exiftool_stuff.rb", "test/test_functional_api.rb", "test/test_reader.rb", "test/test_values.rb", "test/test_values_using_groups.rb", "test/test_writer.rb", "test/test_writer_groups.rb"]
18
+ s.files = ["./.aspell.pws", "Changelog", "Gemfile", "LICENSE", "README.md", "Rakefile", "lib/multi_exiftool", "lib/multi_exiftool.rb", "lib/multi_exiftool/batch.rb", "lib/multi_exiftool/executable.rb", "lib/multi_exiftool/reader.rb", "lib/multi_exiftool/values.rb", "lib/multi_exiftool/writer.rb", "multi_exiftool.gemspec", "regtest/read_all_tags.rb", "regtest/read_all_tags.yml", "regtest/test.jpg", "test/data", "test/data/a.jpg", "test/data/b.jpg", "test/data/c.jpg", "test/data/example.config", "test/helper.rb", "test/test_batch.rb", "test/test_executable.rb", "test/test_exiftool_stuff.rb", "test/test_functional_api.rb", "test/test_reader.rb", "test/test_values.rb", "test/test_values_using_groups.rb", "test/test_writer.rb", "test/test_writer_groups.rb"]
19
19
  s.homepage = "https://github.com/janfri/multi_exiftool"
20
20
  s.licenses = ["MIT"]
21
- s.post_install_message = "\n+-----------------------------------------------------------------------+\n| Please ensure you have installed exiftool version 7.65 or higher and |\n| it's found in your PATH (Try \"exiftool -ver\" on your commandline). |\n| For more details see |\n| http://www.sno.phy.queensu.ca/~phil/exiftool/install.html |\n+-----------------------------------------------------------------------+\n "
21
+ s.post_install_message = "\n+-----------------------------------------------------------------------+\n| Please ensure you have installed exiftool version 7.65 or higher and |\n| it's found in your PATH (Try \"exiftool -ver\" on your commandline). |\n| For more details see |\n| https://exiftool.org/install.html |\n+-----------------------------------------------------------------------+\n "
22
22
  s.required_ruby_version = Gem::Requirement.new(">= 1.9.1")
23
23
  s.requirements = ["exiftool, version 7.65 or higher"]
24
24
  s.rubygems_version = "3.1.2"
25
- s.summary = "This library is a wrapper for the ExifTool command-line application (http://www.sno.phy.queensu.ca/~phil/exiftool)."
25
+ s.summary = "This library is a wrapper for the ExifTool command-line application (https://exiftool.org)."
26
26
 
27
27
  if s.respond_to? :specification_version then
28
28
  s.specification_version = 4
@@ -4,7 +4,7 @@ result:
4
4
  - - !ruby/object:MultiExiftool::Values
5
5
  values:
6
6
  sourcefile: regtest/test.jpg
7
- exiftoolversion: 11.7
7
+ exiftoolversion: 12.0
8
8
  filename: test.jpg
9
9
  directory: regtest
10
10
  filesize: 43 kB
@@ -0,0 +1,363 @@
1
+ # Copied from https://exiftool.org/config.html
2
+
3
+ #------------------------------------------------------------------------------
4
+ # File: example.config
5
+ #
6
+ # Description: Example user configuration file for Image::ExifTool
7
+ #
8
+ # Notes: This example file shows how to define your own shortcuts and
9
+ # add new EXIF, IPTC, XMP, PNG, MIE and Composite tags, as well
10
+ # as how to specify preferred lenses for the LensID tag, and
11
+ # define new file types and default ExifTool option values.
12
+ #
13
+ # Note that unknown tags may be extracted even if they aren't
14
+ # defined, but tags must be defined to be written. Also note
15
+ # that it is possible to override an existing tag definition
16
+ # with a user-defined tag.
17
+ #
18
+ # To activate this file, rename it to ".ExifTool_config" and
19
+ # place it in your home directory or the exiftool application
20
+ # directory. (On Mac and some Windows systems this must be done
21
+ # via the command line since the GUI's may not allow filenames to
22
+ # begin with a dot. Use the "rename" command in Windows or "mv"
23
+ # on the Mac.) This causes ExifTool to automatically load the
24
+ # file when run. Your home directory is determined by the first
25
+ # defined of the following environment variables:
26
+ #
27
+ # 1. EXIFTOOL_HOME
28
+ # 2. HOME
29
+ # 3. HOMEDRIVE + HOMEPATH
30
+ # 4. (the current directory)
31
+ #
32
+ # Alternatively, the -config option of the exiftool application
33
+ # may be used to load a specific configuration file (note that
34
+ # this must be the first option on the command line):
35
+ #
36
+ # exiftool -config example.config ...
37
+ #
38
+ # This example file defines the following 16 new tags as well as
39
+ # a number of Shortcut and Composite tags:
40
+ #
41
+ # 1. EXIF:NewEXIFTag
42
+ # 2. GPS:GPSPitch
43
+ # 3. GPS:GPSRoll
44
+ # 4. IPTC:NewIPTCTag
45
+ # 5. XMP-xmp:NewXMPxmpTag
46
+ # 6. XMP-exif:GPSPitch
47
+ # 7. XMP-exif:GPSRoll
48
+ # 8. XMP-xxx:NewXMPxxxTag1
49
+ # 9. XMP-xxx:NewXMPxxxTag2
50
+ # 10. XMP-xxx:NewXMPxxxTag3
51
+ # 11. XMP-xxx:NewXMPxxxStruct
52
+ # 12. PNG:NewPngTag1
53
+ # 13. PNG:NewPngTag2
54
+ # 14. PNG:NewPngTag3
55
+ # 15. MIE-Meta:NewMieTag1
56
+ # 16. MIE-Test:NewMieTag2
57
+ #
58
+ # For detailed information on the definition of tag tables and
59
+ # tag information hashes, see lib/Image/ExifTool/README.
60
+ #------------------------------------------------------------------------------
61
+
62
+ # Shortcut tags are used when extracting information to simplify
63
+ # commonly used commands. They can be used to represent groups
64
+ # of tags, or to provide an alias for a tag name.
65
+ %Image::ExifTool::UserDefined::Shortcuts = (
66
+ MyShortcut => ['exif:createdate','exposuretime','aperture'],
67
+ MyAlias => 'FocalLengthIn35mmFormat',
68
+ );
69
+
70
+ # NOTE: All tag names used in the following tables are case sensitive.
71
+
72
+ # The %Image::ExifTool::UserDefined hash defines new tags to be added
73
+ # to existing tables.
74
+ %Image::ExifTool::UserDefined = (
75
+ # All EXIF tags are added to the Main table, and WriteGroup is used to
76
+ # specify where the tag is written (default is ExifIFD if not specified):
77
+ 'Image::ExifTool::Exif::Main' => {
78
+ # Example 1. EXIF:NewEXIFTag
79
+ 0xd000 => {
80
+ Name => 'NewEXIFTag',
81
+ Writable => 'int16u',
82
+ WriteGroup => 'IFD0',
83
+ },
84
+ # add more user-defined EXIF tags here...
85
+ },
86
+ # the Geotag feature writes these additional GPS tags if available:
87
+ 'Image::ExifTool::GPS::Main' => {
88
+ # Example 2. GPS:GPSPitch
89
+ 0xd000 => {
90
+ Name => 'GPSPitch',
91
+ Writable => 'rational64s',
92
+ },
93
+ # Example 3. GPS:GPSRoll
94
+ 0xd001 => {
95
+ Name => 'GPSRoll',
96
+ Writable => 'rational64s',
97
+ },
98
+ },
99
+ # IPTC tags are added to a specific record type (eg. application record):
100
+ # (Note: IPTC tag ID's are limited to the range 0-255)
101
+ 'Image::ExifTool::IPTC::ApplicationRecord' => {
102
+ # Example 4. IPTC:NewIPTCTag
103
+ 160 => {
104
+ Name => 'NewIPTCTag',
105
+ Format => 'string[0,16]',
106
+ },
107
+ # add more user-defined IPTC ApplicationRecord tags here...
108
+ },
109
+ # XMP tags may be added to existing namespaces:
110
+ 'Image::ExifTool::XMP::xmp' => {
111
+ # Example 5. XMP-xmp:NewXMPxmpTag
112
+ NewXMPxmpTag => { Groups => { 2 => 'Author' } },
113
+ # add more user-defined XMP-xmp tags here...
114
+ },
115
+ # special Geotag tags for XMP-exif:
116
+ 'Image::ExifTool::XMP::exif' => {
117
+ # Example 6. XMP-exif:GPSPitch
118
+ GPSPitch => { Writable => 'rational', Groups => { 2 => 'Location' } },
119
+ # Example 7. XMP-exif:GPSRoll
120
+ GPSRoll => { Writable => 'rational', Groups => { 2 => 'Location' } },
121
+ },
122
+ # new XMP namespaces (eg. xxx) must be added to the Main XMP table:
123
+ 'Image::ExifTool::XMP::Main' => {
124
+ # namespace definition for examples 8 to 11
125
+ xxx => { # <-- must be the same as the NAMESPACE prefix
126
+ SubDirectory => {
127
+ TagTable => 'Image::ExifTool::UserDefined::xxx',
128
+ # (see the definition of this table below)
129
+ },
130
+ },
131
+ # add more user-defined XMP namespaces here...
132
+ },
133
+ # new PNG tags are added to the PNG::TextualData table:
134
+ 'Image::ExifTool::PNG::TextualData' => {
135
+ # Example 12. PNG:NewPngTag1
136
+ NewPngTag1 => { },
137
+ # Example 13. PNG:NewPngTag2
138
+ NewPngTag2 => { },
139
+ # Example 14. PNG:NewPngTag3
140
+ NewPngTag3 => { },
141
+ },
142
+ # add a new MIE tag (NewMieTag1) and group (MIE-Test) to MIE-Meta
143
+ # (Note: MIE group names must NOT end with a number)
144
+ 'Image::ExifTool::MIE::Meta' => {
145
+ # Example 15. MIE-Meta:NewMieTag1
146
+ NewMieTag1 => {
147
+ Writable => 'rational64u',
148
+ Units => [ 'cm', 'in' ],
149
+ },
150
+ # new MIE "Test" group for example 16
151
+ Test => {
152
+ SubDirectory => {
153
+ TagTable => 'Image::ExifTool::UserDefined::MIETest',
154
+ DirName => 'MIE-Test',
155
+ },
156
+ },
157
+ },
158
+ # Composite tags are added to the Composite table:
159
+ 'Image::ExifTool::Composite' => {
160
+ # Composite tags have values that are derived from the values of
161
+ # other tags. The Require/Desire elements below specify constituent
162
+ # tags that must/may exist, and the keys of these hashes are used as
163
+ # indices in the @val array of the ValueConv expression to access the
164
+ # numerical (-n) values of these tags. Print-converted values are
165
+ # accessed via the @prt array. All Require'd tags must exist for
166
+ # the Composite tag to be evaluated. If no Require'd tags are
167
+ # specified, then at least one of the Desire'd tags must exist. See
168
+ # the Composite table in Image::ExifTool::Exif for more examples,
169
+ # and lib/Image/ExifTool/README for all of the details.
170
+ BaseName => {
171
+ Require => {
172
+ 0 => 'FileName',
173
+ },
174
+ # remove the extension from FileName
175
+ ValueConv => '$val[0] =~ /(.*)\./ ? $1 : $val[0]',
176
+ },
177
+ # the next few examples demonstrate simplifications which may be
178
+ # used if only one tag is Require'd or Desire'd:
179
+ # 1) the Require lookup may be replaced with a simple tag name
180
+ # 2) "$val" may be used to represent "$val[0]" in the expression
181
+ FileExtension => {
182
+ Require => 'FileName',
183
+ ValueConv => '$val=~/\.([^.]*)$/; $1',
184
+ },
185
+ # override CircleOfConfusion tag to use D/1750 instead of D/1440
186
+ CircleOfConfusion => {
187
+ Require => 'ScaleFactor35efl',
188
+ Groups => { 2 => 'Camera' },
189
+ ValueConv => 'sqrt(24*24+36*36) / ($val * 1750)',
190
+ # an optional PrintConv may be used to format the value
191
+ PrintConv => 'sprintf("%.3f mm",$val)',
192
+ },
193
+ # generate a description for this file type
194
+ FileTypeDescription => {
195
+ Require => 'FileType',
196
+ ValueConv => 'GetFileType($val,1) || $val',
197
+ },
198
+ # calculate physical image size based on resolution
199
+ PhysicalImageSize => {
200
+ Require => {
201
+ 0 => 'ImageWidth',
202
+ 1 => 'ImageHeight',
203
+ 2 => 'XResolution',
204
+ 3 => 'YResolution',
205
+ 4 => 'ResolutionUnit',
206
+ },
207
+ ValueConv => '$val[0]/$val[2] . " " . $val[1]/$val[3]',
208
+ # (the @prt array contains print-formatted values)
209
+ PrintConv => 'sprintf("%.1fx%.1f $prt[4]", split(" ",$val))',
210
+ },
211
+ # [advanced] select largest JPEG preview image
212
+ BigImage => {
213
+ Groups => { 2 => 'Preview' },
214
+ Desire => {
215
+ 0 => 'JpgFromRaw',
216
+ 1 => 'PreviewImage',
217
+ 2 => 'OtherImage',
218
+ # (DNG and A100 ARW may be have 2 PreviewImage's)
219
+ 3 => 'PreviewImage (1)',
220
+ # (if the MPF has 2 previews, MPImage3 could be the larger one)
221
+ 4 => 'MPImage3',
222
+ },
223
+ # ValueConv may also be a code reference
224
+ # Inputs: 0) reference to list of values, 1) ExifTool object
225
+ ValueConv => sub {
226
+ my $val = shift;
227
+ my ($image, $bigImage, $len, $bigLen);
228
+ foreach $image (@$val) {
229
+ next unless ref $image eq 'SCALAR';
230
+ # check for JPEG image (or "Binary data" if -b not used)
231
+ next unless $$image =~ /^(\xff\xd8\xff|Binary data (\d+))/;
232
+ $len = $2 || length $$image; # get image length
233
+ # save largest image
234
+ next if defined $bigLen and $bigLen >= $len;
235
+ $bigLen = $len;
236
+ $bigImage = $image;
237
+ }
238
+ return $bigImage;
239
+ },
240
+ },
241
+ # **** ADD ADDITIONAL COMPOSITE TAG DEFINITIONS HERE ****
242
+ },
243
+ );
244
+
245
+ # This is a basic example of the definition for a new XMP namespace.
246
+ # This table is referenced through a SubDirectory tag definition
247
+ # in the %Image::ExifTool::UserDefined definition above.
248
+ # The namespace prefix for these tags is 'xxx', which corresponds to
249
+ # an ExifTool family 1 group name of 'XMP-xxx'.
250
+ %Image::ExifTool::UserDefined::xxx = (
251
+ GROUPS => { 0 => 'XMP', 1 => 'XMP-xxx', 2 => 'Image' },
252
+ NAMESPACE => { 'xxx' => 'http://ns.myname.com/xxx/1.0/' },
253
+ WRITABLE => 'string', # (default to string-type tags)
254
+ # Example 8. XMP-xxx:NewXMPxxxTag1 (an alternate-language tag)
255
+ # - replace "NewXMPxxxTag1" with your own tag name (eg. "MyTag")
256
+ NewXMPxxxTag1 => { Writable => 'lang-alt' },
257
+ # Example 9. XMP-xxx:NewXMPxxxTag2 (a string tag in the Author category)
258
+ NewXMPxxxTag2 => { Groups => { 2 => 'Author' } },
259
+ # Example 10. XMP-xxx:NewXMPxxxTag3 (an unordered List-type tag)
260
+ NewXMPxxxTag3 => { List => 'Bag' },
261
+ # Example 11. XMP-xxx:NewXMPxxxStruct (a structured tag)
262
+ # - example structured XMP tag
263
+ NewXMPxxxStruct => {
264
+ # the "Struct" entry defines the structure fields
265
+ Struct => {
266
+ # optional namespace prefix and URI for structure fields
267
+ # (required only if different than NAMESPACE above)
268
+ # --> multiple entries may exist in this namespace lookup,
269
+ # with the last one alphabetically being the default for
270
+ # the fields, but each field may have a "Namespace"
271
+ # element to specify which prefix to use
272
+ NAMESPACE => { 'test' => 'http://x.y.z/test/' },
273
+ # optional structure name (used for warning messages only)
274
+ STRUCT_NAME => 'MyStruct',
275
+ # optional rdf:type property for the structure
276
+ TYPE => 'http://x.y.z/test/xystruct',
277
+ # structure fields (very similar to tag definitions)
278
+ X => { Writable => 'integer' },
279
+ Y => { Writable => 'integer' },
280
+ # a nested structure...
281
+ Things => {
282
+ List => 'Bag',
283
+ Struct => {
284
+ NAMESPACE => { thing => 'http://x.y.z/thing/' },
285
+ What => { },
286
+ Where => { },
287
+ },
288
+ },
289
+ },
290
+ List => 'Seq', # structures may also be elements of a list
291
+ },
292
+ # Each field in the structure has a corresponding flattened tag that is
293
+ # generated automatically with an ID made from a concatenation of the
294
+ # original structure tag ID and the field name (after capitalizing the
295
+ # first letter of the field name if necessary). The Name and/or
296
+ # Description of these flattened tags may be changed if desired, but all
297
+ # other tag properties are taken from the structure field definition.
298
+ # The "Flat" flag must be used when setting the Name or Description of a
299
+ # flattened tag. For example:
300
+ NewXMPxxxStructX => { Name => 'SomeOtherName', Flat => 1 },
301
+ );
302
+
303
+ # Adding a new MIE group requires a few extra definitions
304
+ use Image::ExifTool::MIE;
305
+ %Image::ExifTool::UserDefined::MIETest = (
306
+ %Image::ExifTool::MIE::tableDefaults, # default MIE table entries
307
+ GROUPS => { 0 => 'MIE', 1 => 'MIE-Test', 2 => 'Document' },
308
+ WRITE_GROUP => 'MIE-Test',
309
+ # Example 16. MIE-Test:NewMieTag2
310
+ NewMieTag2 => { }, # new user-defined tag in MIE-Test group
311
+ );
312
+
313
+ # A special 'Lenses' list can be defined to give priority to specific lenses
314
+ # in the logic to determine a lens model for the Composite:LensID tag
315
+ @Image::ExifTool::UserDefined::Lenses = (
316
+ 'Sigma AF 10-20mm F4-5.6 EX DC',
317
+ 'Tokina AF193-2 19-35mm f/3.5-4.5',
318
+ );
319
+
320
+ # User-defined file types to recognize
321
+ %Image::ExifTool::UserDefined::FileTypes = (
322
+ XXX => { # <-- the extension of the new file type (case insensitive)
323
+ # BaseType specifies the format upon which this file is based (case
324
+ # sensitive). If BaseType is defined, then the file will be fully
325
+ # supported, and in this case the Magic pattern should not be defined
326
+ BaseType => 'TIFF',
327
+ MIMEType => 'image/x-xxx',
328
+ Description => 'My XXX file type',
329
+ # if the BaseType is writable by ExifTool, then the new file type
330
+ # will also be writable unless otherwise specified, like this:
331
+ Writable => 0,
332
+ },
333
+ YYY => {
334
+ # without BaseType, the file will be recognized but not supported
335
+ Magic => '0123abcd', # regular expression to match at start of file
336
+ MIMEType => 'application/test',
337
+ Description => 'My YYY file type',
338
+ },
339
+ ZZZ => {
340
+ # if neither BaseType nor Magic are defined, the file will be
341
+ # recognized by extension only. MIMEType will be application/unknown
342
+ # unless otherwise specified
343
+ Description => 'My ZZZ file type',
344
+ },
345
+ # if only BaseType is specified, then the following simplified syntax
346
+ # may be used. In this example, files with extension "TTT" will be
347
+ # processed as JPEG files
348
+ TTT => 'JPEG',
349
+ );
350
+
351
+ # Change default location for writing QuickTime tags so Keys is preferred
352
+ # (by default, the PREFERRED levels are: ItemList=2, UserData=1, Keys=0)
353
+ use Image::ExifTool::QuickTime;
354
+ $Image::ExifTool::QuickTime::Keys{PREFERRED} = 3;
355
+
356
+ # Specify default ExifTool option values
357
+ # (see the Options function documentation for available options)
358
+ %Image::ExifTool::UserDefined::Options = (
359
+ CoordFormat => '%.6f', # change default GPS coordinate format
360
+ Duplicates => 1, # make -a default for the exiftool app
361
+ GeoMaxHDOP => 4, # ignore GPS fixes with HDOP > 4
362
+ RequestAll => 3, # request additional tags not normally generated
363
+ );
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'helper'
5
+
6
+ class TestBatch < Test::Unit::TestCase
7
+
8
+ MANDATORY_WRITER_ARGS = MultiExiftool::Writer.mandatory_args
9
+
10
+ context 'exiftool_args method' do
11
+
12
+ setup do
13
+ @filenames = %w(a.jpg b.jpg c.jpg)
14
+ @multiple_values = @filenames.map do |filename|
15
+ {author: 'janfri', comment: "Comment for file #{filename}"}
16
+ end
17
+ end
18
+
19
+ test 'only filenames and values' do
20
+ batch = MultiExiftool::Batch.new
21
+ @filenames.zip @multiple_values do |filename, values|
22
+ batch.write filename, values
23
+ end
24
+ exiftool_args = @filenames.zip(@multiple_values).map do |filename, values|
25
+ [MANDATORY_WRITER_ARGS, '-author=janfri', "-comment=Comment for file #{filename}", filename, '-execute']
26
+ end.flatten
27
+ assert_equal exiftool_args, batch.exiftool_args
28
+ end
29
+
30
+ test 'filenames, values and options' do
31
+ options = {overwrite_original: true}
32
+ batch = MultiExiftool::Batch.new
33
+ @filenames.zip @multiple_values do |filename, values|
34
+ batch.write filename, values, options
35
+ end
36
+ exiftool_args = @filenames.zip(@multiple_values).map do |filename, _values|
37
+ [MANDATORY_WRITER_ARGS, '-overwrite_original', '-author=janfri', "-comment=Comment for file #{filename}", filename, '-execute']
38
+ end.flatten
39
+ assert_equal exiftool_args, batch.exiftool_args
40
+ end
41
+ end
42
+ end
@@ -60,6 +60,18 @@ class TestFunctionalApi < Test::Unit::TestCase
60
60
  end
61
61
  end
62
62
 
63
+ test 'successful reading with user defined tags in config file' do
64
+ run_in_temp_dir do
65
+ values, errors = MultiExiftool.read(%w[a.jpg b.jpg c.jpg], tags: %w[basename fileextension])
66
+ assert_equal [nil, nil, nil], values.map(&:basename)
67
+ assert_equal [nil, nil, nil], values.map(&:file_extension)
68
+ assert_equal [], errors
69
+ values, errors = MultiExiftool.read(%w[a.jpg b.jpg c.jpg], config: 'example.config', tags: %w[basename fileextension])
70
+ assert_equal %w[a b c], values.map(&:basename)
71
+ assert_equal %w[jpg]*3, values.map(&:file_extension)
72
+ assert_equal [], errors
73
+ end
74
+ end
63
75
  end
64
76
 
65
77
  context 'writing' do
@@ -188,4 +200,93 @@ class TestFunctionalApi < Test::Unit::TestCase
188
200
 
189
201
  end
190
202
 
203
+ context 'batch' do
204
+
205
+ setup do
206
+ @filenames = %w(a.jpg b.jpg c.jpg)
207
+ @multiple_values = @filenames.map do |fn|
208
+ {author: 'Jan Friedrich', comment: "Comment for file #{fn}"}
209
+ end
210
+ end
211
+
212
+ context 'instance_exec' do
213
+
214
+ test 'only filenames and values' do
215
+ run_in_temp_dir do
216
+ filenames = @filenames
217
+ multiple_values = @multiple_values
218
+ errors = MultiExiftool.batch do
219
+ filenames.zip multiple_values do |filename, values|
220
+ write filename, values
221
+ end
222
+ end
223
+ assert_equal [], errors
224
+ values, errors = MultiExiftool.read(@filenames)
225
+ assert_equal ['Jan Friedrich'] * 3, values.map {|e| e['Author']}
226
+ assert_equal ['Comment for file a.jpg', 'Comment for file b.jpg', 'Comment for file c.jpg'], values.map {|e| e['Comment']}
227
+ assert_equal [], errors
228
+ assert_equal 3, Dir['*_original'].size
229
+ end
230
+ end
231
+
232
+ test 'options with boolean argument' do
233
+ run_in_temp_dir do
234
+ filenames = @filenames
235
+ multiple_values = @multiple_values
236
+ options = {overwrite_original: true}
237
+ errors = MultiExiftool.batch do
238
+ filenames.zip multiple_values do |filename, values|
239
+ write filename, values, options
240
+ end
241
+ end
242
+ assert_equal [], errors
243
+ values, errors = MultiExiftool.read(@filenames)
244
+ assert_equal ['Jan Friedrich'] * 3, values.map {|e| e['Author']}
245
+ assert_equal ['Comment for file a.jpg', 'Comment for file b.jpg', 'Comment for file c.jpg'], values.map {|e| e['Comment']}
246
+ assert_equal [], errors
247
+ assert_equal [], Dir['*_original']
248
+ end
249
+ end
250
+
251
+ end
252
+
253
+ context 'yield' do
254
+
255
+ test 'only filenames and values' do
256
+ run_in_temp_dir do
257
+ errors = MultiExiftool.batch do |batch|
258
+ @filenames.zip @multiple_values do |filename, values|
259
+ batch.write filename, values
260
+ end
261
+ end
262
+ assert_equal [], errors
263
+ values, errors = MultiExiftool.read(@filenames)
264
+ assert_equal ['Jan Friedrich'] * 3, values.map {|e| e['Author']}
265
+ assert_equal ['Comment for file a.jpg', 'Comment for file b.jpg', 'Comment for file c.jpg'], values.map {|e| e['Comment']}
266
+ assert_equal [], errors
267
+ assert_equal 3, Dir['*_original'].size
268
+ end
269
+ end
270
+
271
+ test 'options with boolean argument' do
272
+ run_in_temp_dir do
273
+ options = {overwrite_original: true}
274
+ errors = MultiExiftool.batch do |batch|
275
+ @filenames.zip @multiple_values do |filename, values|
276
+ batch.write filename, values, options
277
+ end
278
+ end
279
+ assert_equal [], errors
280
+ values, errors = MultiExiftool.read(@filenames)
281
+ assert_equal ['Jan Friedrich'] * 3, values.map {|e| e['Author']}
282
+ assert_equal ['Comment for file a.jpg', 'Comment for file b.jpg', 'Comment for file c.jpg'], values.map {|e| e['Comment']}
283
+ assert_equal [], errors
284
+ assert_equal [], Dir['*_original']
285
+ end
286
+ end
287
+
288
+ end
289
+
290
+ end
291
+
191
292
  end
@@ -157,6 +157,20 @@ class TestReader < Test::Unit::TestCase
157
157
  end
158
158
  end
159
159
 
160
+ test 'successful reading with user defined tags in config file' do
161
+ run_in_temp_dir do
162
+ @reader.filenames = %w(a.jpg b.jpg c.jpg)
163
+ @reader.tags = %w[basename]
164
+ res = @reader.read
165
+ assert_equal [nil, nil, nil], res.map(&:basename)
166
+ assert_equal [], @reader.errors
167
+ @reader.config = 'example.config'
168
+ res = @reader.read
169
+ assert_equal %w[a b c], res.map(&:basename)
170
+ assert_equal [], @reader.errors
171
+ end
172
+ end
173
+
160
174
  end
161
175
 
162
176
  end
@@ -137,19 +137,19 @@ class TestValues < Test::Unit::TestCase
137
137
 
138
138
  test 'different formats as string' do
139
139
  @hash.keys.each do |k|
140
- assert_true @values.has_tag? k
140
+ assert_equal true, @values.has_tag?(k)
141
141
  end
142
142
  end
143
143
 
144
144
  test 'different formats as symbol' do
145
145
  @hash.keys.each do |k|
146
- assert_true @values.has_tag? k.to_sym
146
+ assert_equal true, @values.has_tag?(k.to_sym)
147
147
  end
148
148
  end
149
149
 
150
150
  test 'non existent key' do
151
151
  ['iso', 'ISO', :iso, :ISO].each do |t|
152
- assert_false @values.has_tag? t
152
+ assert_equal false, @values.has_tag?(t)
153
153
  end
154
154
  end
155
155
  end
@@ -182,13 +182,13 @@ class TestValues < Test::Unit::TestCase
182
182
 
183
183
  test 'existing keys' do
184
184
  [:fnumber, :f_number, :FNumber, 'fnumber', 'f_number', 'FNumber', :author, :Author, 'author', 'Author'].each do |t|
185
- assert_true @values.respond_to? t
185
+ assert_equal true, @values.respond_to?(t)
186
186
  end
187
187
  end
188
188
 
189
189
  test 'non existing key' do
190
190
  ['iso', 'ISO', :iso, :ISO].each do |t|
191
- assert_false @values.respond_to? t
191
+ assert_equal false, @values.respond_to?(t)
192
192
  end
193
193
  end
194
194
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multi_exiftool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Friedrich
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-02 00:00:00.000000000 Z
11
+ date: 2020-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '2'
83
- description: This library a is wrapper for the ExifTool command-line application (http://www.sno.phy.queensu.ca/~phil/exiftool)
83
+ description: This library is a wrapper for the ExifTool command-line application (https://exiftool.org)
84
84
  written by Phil Harvey. It is designed for dealing with multiple files at once by
85
85
  creating commands to call exiftool with various arguments, call it and parsing the
86
86
  results.
@@ -96,6 +96,7 @@ files:
96
96
  - README.md
97
97
  - Rakefile
98
98
  - lib/multi_exiftool.rb
99
+ - lib/multi_exiftool/batch.rb
99
100
  - lib/multi_exiftool/executable.rb
100
101
  - lib/multi_exiftool/reader.rb
101
102
  - lib/multi_exiftool/values.rb
@@ -107,7 +108,9 @@ files:
107
108
  - test/data/a.jpg
108
109
  - test/data/b.jpg
109
110
  - test/data/c.jpg
111
+ - test/data/example.config
110
112
  - test/helper.rb
113
+ - test/test_batch.rb
111
114
  - test/test_executable.rb
112
115
  - test/test_exiftool_stuff.rb
113
116
  - test/test_functional_api.rb
@@ -123,7 +126,7 @@ metadata: {}
123
126
  post_install_message: "\n+-----------------------------------------------------------------------+\n|
124
127
  Please ensure you have installed exiftool version 7.65 or higher and |\n| it's
125
128
  found in your PATH (Try \"exiftool -ver\" on your commandline). |\n| For more
126
- details see |\n| http://www.sno.phy.queensu.ca/~phil/exiftool/install.html
129
+ details see |\n| https://exiftool.org/install.html
127
130
  \ |\n+-----------------------------------------------------------------------+\n
128
131
  \ "
129
132
  rdoc_options: []
@@ -142,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
145
  requirements:
143
146
  - exiftool, version 7.65 or higher
144
147
  rubygems_version: 3.1.2
145
- signing_key:
148
+ signing_key:
146
149
  specification_version: 4
147
- summary: This library is a wrapper for the ExifTool command-line application (http://www.sno.phy.queensu.ca/~phil/exiftool).
150
+ summary: This library is a wrapper for the ExifTool command-line application (https://exiftool.org).
148
151
  test_files: []