-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathhuman-media-time
executable file
·115 lines (91 loc) · 2.96 KB
/
human-media-time
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env ruby
require 'open3'
require 'optparse'
require 'pathname'
# Options
ARGV.push('--help') if ARGV.empty?
options = {}
OptionParser.new do |parser|
parser.banner = <<~BANNER
Calculate and present total running time of media files in a human-readable way.
Options are mutually exclusive.
Usage:
#{File.basename($PROGRAM_NAME)} [options] <file...>
Options:
BANNER
parser.on(
'-u <s|d|h|m>', '--unit', %w[s d h m],
'Output a whole number of the total time rounded to the unit.'
)
parser.on(
'-p <s|d|h|m>', '--precision', %w[s d h m],
'Output human-readable result rounded and truncated to the chosen unit. Values of zero are ommited unless there is only one.'
)
end.parse!(into: options)
input_files = ARGV.map { |arg|
init_path = Pathname.new(arg)
next nil unless init_path.exist?
next init_path if init_path.file?
next init_path.glob('**/*').select(&:file?) if init_path.directory?
next nil
}.flatten.compact
# Parse time values
total_time = {} # Each unit has the total value of everything
split_time = {} # Each unit has its corresponding value with the others discounted
seconds_total = input_files.map { |file|
Open3.capture2(
'ffprobe',
'-loglevel', 'quiet',
'-output_format', 'csv=p=0',
'-show_entries', 'format=duration',
file.to_path
).first.to_f
}.reduce(0, :+)
# Building the hashes in this order simplifies the 'reduce' later on
total_time['d'] = seconds_total / (60 * 60 * 24)
total_time['h'] = seconds_total / (60 * 60)
total_time['m'] = seconds_total / (60)
total_time['s'] = seconds_total
split_time['d'] = total_time['d'] % 365
split_time['h'] = total_time['h'] % 24
split_time['m'] = total_time['m'] % 60
split_time['s'] = total_time['s'] % 60
# Output unit
if options[:unit]
puts total_time[options[:unit]].round
exit 0
end
# If no precision is given, assume seconds
options[:precision] = 's' if options[:precision].nil?
# The split_times hash includes floats, so here is where we round the values we want and discard the ones we do not
time_hash = {}
split_time.keys.each do |unit|
if unit == options[:precision]
time_hash[unit] = split_time[unit].round
break # Since we already reached our desired precision, all further keys can be ignored
end
time_hash[unit] = split_time[unit].to_i
end
# Normalise rare cases where rounding precision would give an underisable result
# Without this, a starting point of '1d 23h 50m 53s' rounded to hours would result in '1d 24h' instead of '2d'
if time_hash['s'] == 60
time_hash['m'] += 1
time_hash.delete('s')
end
if time_hash['m'] == 60
time_hash['h'] += 1
time_hash.delete('m')
end
if time_hash['h'] == 24
time_hash['d'] += 1
time_hash.delete('h')
end
# Eliminate zeros
time_hash.reject! { |_, value| value.zero? }
# Output when all zeroes
if time_hash.empty?
puts "0#{options[:precision]}"
exit 0
end
# Output human-readable format
puts time_hash.reduce('') { |string, (key, value)| "#{string}#{value}#{key} " }.strip