Module: FilePipeline::FileOperations::ExifManipulable

Included in:
ExifRecovery, ExifRedaction, ExifRestoration, VersionedFile
Defined in:
lib/file_pipeline/file_operations/exif_manipulable.rb

Overview

Mixin with methods to facilitate work with Exif metadata.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.file_tagsObject

Returns an Array of tags to be ignored during comparison. These can be merged with an ExifManipulable including FileOperation’s options to skip tags (e.g. the skip_tags option in ExifRestoration).

The included tags relate to the file properties (e.g. filesize, MIME-type) that will have been altered by any prior operation, such as file format conversions.



16
17
18
19
# File 'lib/file_pipeline/file_operations/exif_manipulable.rb', line 16

def self.file_tags
  %w[FileSize FileModifyDate FileAccessDate FileInodeChangeDate
     FilePermissions FileType FileTypeExtension MIMEType]
end

.parse_tag_error(message) ⇒ Object

:nodoc:



21
22
23
24
# File 'lib/file_pipeline/file_operations/exif_manipulable.rb', line 21

def self.parse_tag_error(message) # :nodoc:
  /Warning: Sorry, (?<tag>\w+) is not writable/
    .match(message) { |match| match[:tag] }
end

.strip_path(str) ⇒ Object

:nodoc:



26
27
28
# File 'lib/file_pipeline/file_operations/exif_manipulable.rb', line 26

def self.strip_path(str) # :nodoc:
  str.sub(%r{ - /?(/|[-:.]+|\w+)+\.\w+$}, '')
end

Instance Method Details

#delete_tags(out_file, tags_to_delete) ⇒ Object

Redacts (deletes) all tags_to_delete in out_file.



31
32
33
34
35
36
37
38
39
# File 'lib/file_pipeline/file_operations/exif_manipulable.rb', line 31

def delete_tags(out_file, tags_to_delete)
  exif, = read_exif out_file
  values = exif.select { |tag| tags_to_delete.include? tag }
  values_to_delete = values.transform_values { nil }
  return 'Info: nothing to delete.' if values.empty?

  log, = write_exif out_file, values_to_delete
  [log, values]
end

#missing_exif_fields(this_exif, other_exif) ⇒ Object

Compares to hashes with exif tags and values and returns a hash with the tags that are present in other_exif but absent in this_exif.



44
45
46
47
48
# File 'lib/file_pipeline/file_operations/exif_manipulable.rb', line 44

def missing_exif_fields(this_exif, other_exif)
  other_exif.delete_if do |tag, _|
    this_exif.key?(tag) || options[:skip_tags].include?(tag)
  end
end

#parse_exif_errors(errs, values) ⇒ Object

:args: error_messages, exif

Takes an array of error_messages and a hash (exif) with tags and their values and parses errors where tags could not be written.

Returns an array with a log (any messages that were not errors where a tag could not be written) and data (a hash with any tags that could not be written, and the associated values from exif)



58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/file_pipeline/file_operations/exif_manipulable.rb', line 58

def parse_exif_errors(errs, values)
  errs.each_with_object(LogDataParser.template) do |message, info|
    errors, data = info
    tag = ExifManipulable.parse_tag_error(message)
    if tag
      data.store tag, values[tag]
      next info
    end
    errors << ExifManipulable.strip_path(message)
    info
  end
end

#read_exif(*files) ⇒ Object

Reads exif information for one or more files. Returns an array of hashes, one for each file, with tags and their values.



73
74
75
76
77
78
79
# File 'lib/file_pipeline/file_operations/exif_manipulable.rb', line 73

def read_exif(*files)
  file_paths = files.map { |f| File.expand_path(f) }
  results, errors = MultiExiftool.read file_paths
  raise 'Error reading Exif' unless errors.empty?

  results.map(&:to_h)
end

#write_exif(out_file, values) ⇒ Object

Writes values (a hash with exif tags as keys) to out_file.

Returns an array with a log (an array of messages - strings) and a hash with all tags/values that could not be written.



85
86
87
88
89
90
91
92
93
# File 'lib/file_pipeline/file_operations/exif_manipulable.rb', line 85

def write_exif(out_file, values)
  writer = MultiExiftool::Writer.new
  writer.filenames = Dir[File.expand_path(out_file)]
  writer.overwrite_original = true
  writer.values = values
  return if writer.write

  parse_exif_errors(writer.errors, values)
end