timestamp_maker 1.2.1 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/timestamp +8 -2
- data/lib/timestamp_maker.rb +62 -48
- data/lib/timestamp_maker/{video_timestamper.rb → handlers/ffmpeg.rb} +36 -6
- data/lib/timestamp_maker/handlers/image_magick.rb +113 -0
- data/lib/timestamp_maker/mime_recognizers/marcel.rb +13 -0
- data/lib/timestamp_maker/time_zone_lookupers/geo_name.rb +30 -0
- data/lib/timestamp_maker/time_zone_lookupers/mock.rb +15 -0
- data/lib/timestamp_maker/time_zone_lookupers/wheretz.rb +18 -0
- metadata +22 -5
- data/lib/timestamp_maker/image_timestamper.rb +0 -69
- data/lib/timestamp_maker/mime_recognizer.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7f96f255213ceac42648b8ad1317b745d64062f6ad116fd8365d64408077101
|
4
|
+
data.tar.gz: 6c4c191ee701c7ffbf539b9665df9b3064608f50cf7eee06cffede24f46aa7a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f4e5a1e7f61304642cc758d82a4ec2a95af43a4f10fda93cb1f813468a1f6a6a321da5606acca79ad8724ca9ed79af162b8bedf82b3d9c0cae51ffd59a111de
|
7
|
+
data.tar.gz: 02712023f7dcd89f9083b95074ddde9770c6b73083e314d380bc71dd5020896a650ef509f79ff7183821f5ca18193805c2c1e9cc5a8cd65624ff5796bbe74214
|
data/bin/timestamp
CHANGED
@@ -16,7 +16,8 @@ options = {
|
|
16
16
|
coordinate_origin: 'top-left',
|
17
17
|
x: 32,
|
18
18
|
y: 32,
|
19
|
-
font_padding: 8
|
19
|
+
font_padding: 8,
|
20
|
+
require: []
|
20
21
|
}
|
21
22
|
|
22
23
|
option_parser = OptionParser.new do |parser|
|
@@ -69,6 +70,10 @@ option_parser = OptionParser.new do |parser|
|
|
69
70
|
parser.on('--font-padding NUM', Integer, 'Defaults to 8.') do |value|
|
70
71
|
options[:font_padding] = value
|
71
72
|
end
|
73
|
+
|
74
|
+
parser.on('-r LIB', '--require LIB', Array, 'Comma-separated Ruby libs') do |value|
|
75
|
+
options[:require] = value
|
76
|
+
end
|
72
77
|
end
|
73
78
|
option_parser.parse!
|
74
79
|
|
@@ -80,7 +85,8 @@ end
|
|
80
85
|
input = ARGV.shift
|
81
86
|
output = ARGV.shift
|
82
87
|
|
83
|
-
|
88
|
+
options[:require].each { |i| require i }
|
89
|
+
TimestampMaker.instance.add_timestamp(
|
84
90
|
input,
|
85
91
|
output,
|
86
92
|
format: options[:format],
|
data/lib/timestamp_maker.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'marcel'
|
4
3
|
require 'pathname'
|
5
|
-
require 'timestamp_maker/
|
6
|
-
require 'timestamp_maker/
|
7
|
-
require 'timestamp_maker/
|
4
|
+
require 'timestamp_maker/handlers/image_magick'
|
5
|
+
require 'timestamp_maker/handlers/ffmpeg'
|
6
|
+
require 'timestamp_maker/mime_recognizers/marcel'
|
7
|
+
require 'timestamp_maker/time_zone_lookupers/wheretz'
|
8
8
|
require 'tzinfo'
|
9
9
|
|
10
|
-
|
10
|
+
class TimestampMaker
|
11
11
|
COORDINATE_ORIGINS = %w[
|
12
12
|
top-left
|
13
13
|
top-right
|
@@ -15,55 +15,69 @@ module TimestampMaker
|
|
15
15
|
bottom-right
|
16
16
|
].freeze
|
17
17
|
|
18
|
-
|
19
|
-
@image_timestamper = ImageTimestamper
|
20
|
-
@video_timestamper = VideoTimestamper
|
18
|
+
attr_accessor :mime_recognizer, :handlers
|
21
19
|
|
22
|
-
|
23
|
-
|
20
|
+
singleton_class.attr_writer :instance
|
21
|
+
def self.instance
|
22
|
+
@instance ||= new
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
mime_type = mime_recognizer.recognize(input_path)
|
40
|
-
processor =
|
41
|
-
case mime_type.split('/').first
|
42
|
-
when 'image' then image_timestamper
|
43
|
-
when 'video' then video_timestamper
|
44
|
-
else raise "Unsupported MIME type: ##{mime_type}"
|
45
|
-
end
|
46
|
-
time = processor.creation_time(input_path) if time.nil?
|
47
|
-
raise ArgumentError unless time.is_a?(Time)
|
25
|
+
def initialize(
|
26
|
+
mime_recognizer: MimeRecognizers::Marcel.new,
|
27
|
+
handlers: [
|
28
|
+
Handlers::ImageMagick.new(
|
29
|
+
time_zone_lookuper: TimeZoneLookupers::Wheretz.new
|
30
|
+
),
|
31
|
+
Handlers::Ffmpeg.new(
|
32
|
+
time_zone_lookuper: TimeZoneLookupers::Wheretz.new
|
33
|
+
)
|
34
|
+
]
|
35
|
+
)
|
36
|
+
@mime_recognizer = mime_recognizer
|
37
|
+
@handlers = handlers
|
38
|
+
end
|
48
39
|
|
49
|
-
|
40
|
+
def add_timestamp(
|
41
|
+
input_path, output_path,
|
42
|
+
format: '%Y-%m-%d %H:%M:%S',
|
43
|
+
time: nil,
|
44
|
+
font_size: 32,
|
45
|
+
font_family: 'Sans',
|
46
|
+
font_color: 'white',
|
47
|
+
background_color: '#000000B3',
|
48
|
+
time_zone: nil,
|
49
|
+
coordinate_origin: 'top-left',
|
50
|
+
x: 32,
|
51
|
+
y: 32,
|
52
|
+
font_padding: 8
|
53
|
+
)
|
54
|
+
mime_type = mime_recognizer.recognize(input_path)
|
55
|
+
handler = handlers.find { |i| i.accept?(mime_type) }
|
56
|
+
raise "Unsupported MIME type: ##{mime_type}" if handler.nil?
|
50
57
|
|
51
|
-
|
52
|
-
|
53
|
-
end
|
58
|
+
time = handler.creation_time(input_path) if time.nil?
|
59
|
+
raise ArgumentError unless time.is_a?(Time)
|
54
60
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
background_color: background_color,
|
62
|
-
coordinate_origin: coordinate_origin,
|
63
|
-
x: x,
|
64
|
-
y: y,
|
65
|
-
font_padding: font_padding
|
61
|
+
time.localtime(TZInfo::Timezone.get(time_zone)) unless time_zone.nil?
|
62
|
+
|
63
|
+
unless COORDINATE_ORIGINS.include?(coordinate_origin)
|
64
|
+
raise(
|
65
|
+
ArgumentError,
|
66
|
+
"coordinate origin should be one of #{COORDINATE_ORIGINS.join(',')}"
|
66
67
|
)
|
67
68
|
end
|
69
|
+
|
70
|
+
handler.add_timestamp(
|
71
|
+
input_path, output_path, time,
|
72
|
+
format: format,
|
73
|
+
font_size: font_size,
|
74
|
+
font_family: font_family,
|
75
|
+
font_color: font_color,
|
76
|
+
background_color: background_color,
|
77
|
+
coordinate_origin: coordinate_origin,
|
78
|
+
x: x,
|
79
|
+
y: y,
|
80
|
+
font_padding: font_padding
|
81
|
+
)
|
68
82
|
end
|
69
83
|
end
|
@@ -6,9 +6,20 @@ require 'open3'
|
|
6
6
|
require 'English'
|
7
7
|
require 'tzinfo'
|
8
8
|
|
9
|
-
|
10
|
-
module
|
11
|
-
class
|
9
|
+
class TimestampMaker
|
10
|
+
module Handlers
|
11
|
+
class Ffmpeg
|
12
|
+
|
13
|
+
attr_accessor :time_zone_lookuper
|
14
|
+
|
15
|
+
def initialize(time_zone_lookuper:)
|
16
|
+
@time_zone_lookuper = time_zone_lookuper
|
17
|
+
end
|
18
|
+
|
19
|
+
def accept?(mime_type)
|
20
|
+
mime_type.start_with?('video/')
|
21
|
+
end
|
22
|
+
|
12
23
|
def add_timestamp(
|
13
24
|
input_path,
|
14
25
|
output_path,
|
@@ -45,13 +56,14 @@ module TimestampMaker
|
|
45
56
|
]
|
46
57
|
|
47
58
|
tz = tz_env_string(time)
|
48
|
-
raise "Command failed with exit #{$CHILD_STATUS.exitstatus}: #{command.first}" unless system({ 'TZ' => tz },
|
59
|
+
raise "Command failed with exit #{$CHILD_STATUS.exitstatus}: #{command.first}" unless system({ 'TZ' => tz },
|
60
|
+
*command)
|
49
61
|
end
|
50
62
|
|
51
63
|
def creation_time(input_path)
|
52
64
|
command = %W[
|
53
65
|
ffprobe -v warning -print_format json
|
54
|
-
-show_entries format_tags=creation_time,com.apple.quicktime.creationdate
|
66
|
+
-show_entries format_tags=creation_time,com.apple.quicktime.creationdate,location
|
55
67
|
#{input_path}
|
56
68
|
]
|
57
69
|
stdout_string, status = Open3.capture2(*command)
|
@@ -61,11 +73,29 @@ module TimestampMaker
|
|
61
73
|
iso8601_string = parsed['format']['tags']['com.apple.quicktime.creationdate'] || parsed['format']['tags']['creation_time']
|
62
74
|
raise 'Cannot find creation time' if iso8601_string.nil?
|
63
75
|
|
64
|
-
Time.iso8601(iso8601_string)
|
76
|
+
time = Time.iso8601(iso8601_string)
|
77
|
+
|
78
|
+
iso6709_string = parsed['format']['tags']['location']
|
79
|
+
if iso6709_string && (time_zone = retrieve_time_zone_from_iso6709(iso6709_string))
|
80
|
+
begin
|
81
|
+
return TZInfo::Timezone.get(time_zone).to_local(time)
|
82
|
+
rescue TZInfo::InvalidTimezoneIdentifier
|
83
|
+
warn "Can not find time zone: #{time_zone}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
time
|
65
88
|
end
|
66
89
|
|
67
90
|
private
|
68
91
|
|
92
|
+
def retrieve_time_zone_from_iso6709(string)
|
93
|
+
data = string.match(/([+-]\d*\.?\d*)([+-]\d*\.?\d*)/)
|
94
|
+
latitude = data[1].to_f
|
95
|
+
longitude = data[2].to_f
|
96
|
+
time_zone_lookuper.lookup(latitude: latitude, longitude: longitude)
|
97
|
+
end
|
98
|
+
|
69
99
|
def tz_env_string(time)
|
70
100
|
return time.zone.name if time.zone.is_a? TZInfo::Timezone
|
71
101
|
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'time'
|
5
|
+
require 'English'
|
6
|
+
require 'tzinfo'
|
7
|
+
|
8
|
+
class TimestampMaker
|
9
|
+
module Handlers
|
10
|
+
class ImageMagick
|
11
|
+
GRAVITY_MAP = {
|
12
|
+
'top-left' => 'NorthWest',
|
13
|
+
'top-right' => 'NorthEast',
|
14
|
+
'bottom-left' => 'SouthWest',
|
15
|
+
'bottom-right' => 'SouthEast'
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
attr_accessor :time_zone_lookuper
|
19
|
+
|
20
|
+
def initialize(time_zone_lookuper:)
|
21
|
+
@time_zone_lookuper = time_zone_lookuper
|
22
|
+
end
|
23
|
+
|
24
|
+
def accept?(mime_type)
|
25
|
+
mime_type.start_with?('image/')
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_timestamp(
|
29
|
+
input_path,
|
30
|
+
output_path,
|
31
|
+
time,
|
32
|
+
format:,
|
33
|
+
font_size:,
|
34
|
+
font_family:,
|
35
|
+
font_color:,
|
36
|
+
background_color:,
|
37
|
+
coordinate_origin:,
|
38
|
+
x:,
|
39
|
+
y:,
|
40
|
+
font_padding:
|
41
|
+
)
|
42
|
+
time_string = time.strftime(format)
|
43
|
+
command = %W[
|
44
|
+
convert #{input_path}
|
45
|
+
(
|
46
|
+
-background #{background_color}
|
47
|
+
-fill #{font_color}
|
48
|
+
-family #{font_family}
|
49
|
+
-pointsize #{font_size}
|
50
|
+
-gravity NorthWest
|
51
|
+
-splice #{font_padding}x#{font_padding}
|
52
|
+
-gravity SouthEast
|
53
|
+
-splice #{font_padding}x#{font_padding}
|
54
|
+
label:#{time_string}
|
55
|
+
)
|
56
|
+
-gravity #{GRAVITY_MAP[coordinate_origin]}
|
57
|
+
-geometry +#{x}+#{y}
|
58
|
+
-composite #{output_path}
|
59
|
+
]
|
60
|
+
raise "Command failed with exit #{$CHILD_STATUS.exitstatus}: #{command.first}" unless system(*command)
|
61
|
+
end
|
62
|
+
|
63
|
+
def creation_time(input_path)
|
64
|
+
command = %W[
|
65
|
+
identify -format %[exif:DateTime*]%[exif:OffsetTime*]%[exif:GPSLatitude*]%[exif:GPSLongitude*] #{input_path}
|
66
|
+
]
|
67
|
+
|
68
|
+
stdout_string, status = Open3.capture2(*command)
|
69
|
+
raise unless status.success?
|
70
|
+
|
71
|
+
parsed = Hash[stdout_string.split("\n").map! { |i| i[5..-1].split('=') }]
|
72
|
+
|
73
|
+
time_string = parsed['DateTimeOriginal'] || parsed['DateTimeDigitized'] || parsed['DateTime']
|
74
|
+
raise 'Cannot find creation time' if time_string.nil?
|
75
|
+
|
76
|
+
time_arguments = time_string.split(/[: ]/).map(&:to_i)
|
77
|
+
|
78
|
+
if (time_zone = retrieve_time_zone_by_coordinate(parsed))
|
79
|
+
begin
|
80
|
+
return TZInfo::Timezone.get(time_zone).local_time(*time_arguments)
|
81
|
+
rescue TZInfo::InvalidTimezoneIdentifier
|
82
|
+
warn "Can not find time zone: #{time_zone}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
time_offset_string = parsed['OffsetTimeOriginal'] || parsed['OffsetTimeDigitized'] || parsed['OffsetTime']
|
87
|
+
raise 'Can not find time offset' if time_offset_string.nil?
|
88
|
+
|
89
|
+
Time.new(*time_arguments, time_offset_string)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def parse_coordinate_number(string)
|
95
|
+
degree, minute, second = string.split(', ').map! { |i| Rational(i) }
|
96
|
+
(degree + minute / 60 + second / 3600).to_f
|
97
|
+
end
|
98
|
+
|
99
|
+
def retrieve_time_zone_by_coordinate(exif)
|
100
|
+
unless exif['GPSLatitude'] && exif['GPSLatitudeRef'] && exif['GPSLongitude'] && exif['GPSLongitudeRef']
|
101
|
+
return nil
|
102
|
+
end
|
103
|
+
|
104
|
+
latitude = parse_coordinate_number(exif['GPSLatitude'])
|
105
|
+
latitude = -latitude if exif['GPSLatitudeRef'] == 'S'
|
106
|
+
longitude = parse_coordinate_number(exif['GPSLongitude'])
|
107
|
+
longitude = -longitude if exif['GPSLongitudeRef'] == 'W'
|
108
|
+
|
109
|
+
time_zone_lookuper.lookup(latitude: latitude, longitude: longitude)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
class TimestampMaker
|
8
|
+
module TimeZoneLookupers
|
9
|
+
ENDPOINT = URI.parse('http://api.geonames.org/timezoneJSON')
|
10
|
+
|
11
|
+
class GeoName
|
12
|
+
attr_accessor :username
|
13
|
+
|
14
|
+
def initialize(username:)
|
15
|
+
@username = username
|
16
|
+
end
|
17
|
+
|
18
|
+
def lookup(latitude:, longitude:)
|
19
|
+
query = URI.encode_www_form(
|
20
|
+
[['lat', latitude], ['lng', longitude], ['username', username]]
|
21
|
+
)
|
22
|
+
response = Net::HTTP.get_response(URI.parse("#{ENDPOINT}?#{query}"))
|
23
|
+
raise "Got HTTP status code: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
|
24
|
+
|
25
|
+
parsed = JSON.parse(response.body)
|
26
|
+
parsed['timezoneId']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'wheretz'
|
4
|
+
require 'json' # wheretz forgets to require json
|
5
|
+
|
6
|
+
class TimestampMaker
|
7
|
+
module TimeZoneLookupers
|
8
|
+
class Wheretz
|
9
|
+
def lookup(latitude:, longitude:)
|
10
|
+
case result = WhereTZ.lookup(latitude, longitude)
|
11
|
+
when String then result
|
12
|
+
when Array then result.first
|
13
|
+
else raise 'Something went wrong with WhereTZ'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timestamp_maker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Weihang Jian
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-07-
|
11
|
+
date: 2021-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: marcel
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: wheretz
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.0.6
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.0.6
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: minitest
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -76,9 +90,12 @@ extra_rdoc_files: []
|
|
76
90
|
files:
|
77
91
|
- bin/timestamp
|
78
92
|
- lib/timestamp_maker.rb
|
79
|
-
- lib/timestamp_maker/
|
80
|
-
- lib/timestamp_maker/
|
81
|
-
- lib/timestamp_maker/
|
93
|
+
- lib/timestamp_maker/handlers/ffmpeg.rb
|
94
|
+
- lib/timestamp_maker/handlers/image_magick.rb
|
95
|
+
- lib/timestamp_maker/mime_recognizers/marcel.rb
|
96
|
+
- lib/timestamp_maker/time_zone_lookupers/geo_name.rb
|
97
|
+
- lib/timestamp_maker/time_zone_lookupers/mock.rb
|
98
|
+
- lib/timestamp_maker/time_zone_lookupers/wheretz.rb
|
82
99
|
homepage: https://github.com/tonytonyjan/timestamp_maker
|
83
100
|
licenses:
|
84
101
|
- MIT
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'open3'
|
4
|
-
require 'time'
|
5
|
-
require 'English'
|
6
|
-
|
7
|
-
module TimestampMaker
|
8
|
-
module ImageTimestamper
|
9
|
-
GRAVITY_MAP = {
|
10
|
-
'top-left' => 'NorthWest',
|
11
|
-
'top-right' => 'NorthEast',
|
12
|
-
'bottom-left' => 'SouthWest',
|
13
|
-
'bottom-right' => 'SouthEast'
|
14
|
-
}.freeze
|
15
|
-
|
16
|
-
def self.add_timestamp(
|
17
|
-
input_path,
|
18
|
-
output_path,
|
19
|
-
time,
|
20
|
-
format:,
|
21
|
-
font_size:,
|
22
|
-
font_family:,
|
23
|
-
font_color:,
|
24
|
-
background_color:,
|
25
|
-
coordinate_origin:,
|
26
|
-
x:,
|
27
|
-
y:,
|
28
|
-
font_padding:
|
29
|
-
)
|
30
|
-
time_string = time.strftime(format)
|
31
|
-
command = %W[
|
32
|
-
convert #{input_path}
|
33
|
-
(
|
34
|
-
-background #{background_color}
|
35
|
-
-fill #{font_color}
|
36
|
-
-family #{font_family}
|
37
|
-
-pointsize #{font_size}
|
38
|
-
-gravity NorthWest
|
39
|
-
-splice #{font_padding}x#{font_padding}
|
40
|
-
-gravity SouthEast
|
41
|
-
-splice #{font_padding}x#{font_padding}
|
42
|
-
label:#{time_string}
|
43
|
-
)
|
44
|
-
-gravity #{GRAVITY_MAP[coordinate_origin]}
|
45
|
-
-geometry +#{x}+#{y}
|
46
|
-
-composite #{output_path}
|
47
|
-
]
|
48
|
-
raise "Command failed with exit #{$CHILD_STATUS.exitstatus}: #{command.first}" unless system(*command)
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.creation_time(input_path)
|
52
|
-
command = %W[
|
53
|
-
identify -format %[exif:DateTime*]%[exif:OffsetTime*] #{input_path}
|
54
|
-
]
|
55
|
-
|
56
|
-
stdout_string, status = Open3.capture2(*command)
|
57
|
-
raise unless status.success?
|
58
|
-
|
59
|
-
parsed = Hash[stdout_string.split("\n").map! { |i| i[5..-1].split('=') }]
|
60
|
-
|
61
|
-
time_string = parsed['DateTimeOriginal'] || parsed['DateTimeDigitized'] || parsed['DateTime']
|
62
|
-
raise 'Cannot find creation time' if time_string.nil?
|
63
|
-
|
64
|
-
time_offset_string = parsed['OffsetTimeOriginal'] || parsed['OffsetTimeDigitized'] || parsed['OffsetTime'] || 'Z'
|
65
|
-
|
66
|
-
Time.strptime("#{time_string} #{time_offset_string}", '%Y:%m:%d %H:%M:%S %Z')
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|