datefmt: Define 1 year as 365.2425 days instead of 365.25
[sunny256-utils.git] / sumdur
blobe93f5d113fc358f09f59c92a1c58179845050602
1 #!/bin/sh
3 #==============================================================================
4 # sumdur
5 # File ID: a15d6810-9d7d-11e9-b544-4f45262dc9b5
7 # Print total duration for multiple video/audio files.
9 # Author: Øyvind A. Holm <sunny@sunbase.org>
10 # License: GNU General Public License version 2 or later.
11 #==============================================================================
13 progname=sumdur
14 VERSION=0.2.1
16 opt_days=0
17 opt_no_frac=0
18 opt_help=0
19 opt_quiet=0
20 opt_unit=''
21 opt_verbose=0
22 opt_zero_terminated=0
23 while test -n "$1"; do
24 case "$1" in
25 -d|--days) opt_days=1; shift ;;
26 -F|--no-frac) opt_no_frac=1; shift ;;
27 -h|--help) opt_help=1; shift ;;
28 -q|--quiet) opt_quiet=$(($opt_quiet + 1)); shift ;;
29 -u|--unit) opt_unit=$2; shift 2 ;;
30 -v|--verbose) opt_verbose=$(($opt_verbose + 1)); shift ;;
31 -z|--zero-terminated) opt_zero_terminated=1; shift ;;
32 --version) echo $progname $VERSION; exit 0 ;;
33 --) shift; break ;;
35 if printf '%s\n' "$1" | grep -q ^-; then
36 echo "$progname: $1: Unknown option" >&2
37 exit 1
38 else
39 break
41 break ;;
42 esac
43 done
44 opt_verbose=$(($opt_verbose - $opt_quiet))
46 if test "$opt_help" = "1"; then
47 test $opt_verbose -gt 0 && { echo; echo $progname $VERSION; }
48 cat <<END
50 Print total duration for multiple video/audio files. If no files are
51 specified on the command line, read from stdin.
53 Usage: $progname [options] [files]
55 Options:
57 -d, --days
58 Include days in the output format, "d:hh:mm:ss.ssss".
59 -F, --no-frac
60 Don't use fractional seconds.
61 -h, --help
62 Show this help.
63 -q, --quiet
64 Be more quiet. Can be repeated to increase silence.
65 -u UNIT, --unit UNIT
66 Instead of using the hh:mm:ss.ssss format, display the total
67 duration as UNIT. Valid values for UNIT are: seconds, minutes,
68 hours, days, weeks, months, years. Only the first two letters are
69 used, so the abbreviation "mi" for "minutes" is ok.
70 -v, --verbose
71 Increase level of verbosity. Can be repeated.
72 -z, --zero-terminated
73 Filenames from stdin are separated by a zero byte (NUL) instead of
74 newline.
75 --version
76 Print version information.
78 Examples:
80 $progname *.opus
81 $progname <playlist.txt
82 find . -name '*.mp4' | $progname
83 find . -name '*.mp3' -print0 | $progname -z -u ho
85 END
86 exit 0
89 if test -z "$(bc --version 2>/dev/null)"; then
90 echo "$progname: bc(1) not found" >&2
91 exit 1
93 if test -z "$(ffprobe -version 2>/dev/null)"; then
94 echo "$progname: ffprobe(1) from FFmpeg not found" >&2
95 exit 1
98 test "$opt_zero_terminated" = "1" && sep_str='-0' || sep_str='-d\n'
100 unit_div=0
101 if test -n "$opt_unit"; then
102 case "$(echo $opt_unit | tr '[A-Z]' '[a-z]' | head -c 2)" in
103 se) unit_div=1 ;;
104 mi) unit_div=60 ;;
105 ho) unit_div=3600 ;;
106 da) unit_div=86400 ;;
107 we) unit_div=604800 ;;
108 mo) unit_div=2629800 ;;
109 ye) unit_div=31557600 ;;
110 *) echo "$progname: Invalid value in -u/--unit option" >&2
111 exit 1
112 break ;;
113 esac
116 secs=$(
117 if test -n "$1"; then
118 sep_str='-d\n'
119 for f in "$@"; do
120 echo "$f"
121 done
122 else
124 fi \
125 | xargs $sep_str -L1 \
126 ffprobe -v quiet -of csv=p=0 -show_entries format=duration \
127 | paste -sd+ \
128 | bc
131 test -z "$secs" && secs=0
133 if test "$opt_days" = "1"; then
134 d1_str='days = floor(secs / 86400); secs -= days * 86400;'
135 d2_str="print \"printf '%u:%02u:%02u:%02u' \","
136 d2_str="$d2_str days, \" \", hours, \" \", mins, \" \", secs, \"; \";"
137 else
138 d1_str=''
139 d2_str="print \"printf '%02u:%02u:%02u' \","
140 d2_str="$d2_str hours, \" \", mins, \" \", secs, \"; \";"
143 cat <<END | bc | sh | perl -pe 's/^\./0./'
144 scale = 50;
146 /* From <http://phodd.net/gnu-bc/code/funcs.bc>, thanks */
147 define floor(x) {
148 auto os, xx;
149 os = scale;
150 scale = 0;
151 xx = x / 1;
152 if (xx > x) .=xx--;
153 scale = os;
154 return xx;
157 /* From <http://phodd.net/gnu-bc/bcfaq.html#bczeroes>, thanks again */
158 define trunc(x) {
159 auto os;
160 os = scale;
161 for (scale = 0; scale <= os; scale++) {
162 if (x == x / 1) {
163 x /= 1;
164 scale = os;
165 return x;
170 secs = $secs;
172 if ($unit_div) {
173 if ($opt_no_frac) {
174 print "echo ", floor(trunc(secs / $unit_div));
175 } else {
176 scale = 8;
177 print "echo ", trunc(secs / $unit_div);
179 } else {
180 $d1_str
181 hours = floor(secs / 3600);
182 secs -= hours * 3600;
184 mins = floor(secs / 60);
185 secs -= mins * 60;
187 frac = secs;
188 secs = floor(secs);
189 frac -= secs;
190 $(test "$opt_no_frac" = "1" && echo "frac = 0;")
192 $d2_str
193 if (frac > 0) print "echo -n ", trunc(frac), "; ";
194 print "echo";
198 # vim: set ts=8 sw=8 sts=8 noet fo+=w tw=79 fenc=UTF-8 :