-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtimecode
More file actions
executable file
·213 lines (193 loc) · 5.09 KB
/
timecode
File metadata and controls
executable file
·213 lines (193 loc) · 5.09 KB
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/bin/bash
## CONSTANTS:
{
export TCSCRIPT_VERSION=0.2.2
export TCSCRIPT_DATE_MODIFIED=2025-10-24
export TCSCRIPT_NAME="$(basename "$0")"
export TCSCRIPT_ATTRIBUTION="${TCSCRIPT_NAME} version ${TCSCRIPT_VERSION} ${TCSCRIPT_DATE_MODIFIED}"
E_ARGS=1
TIMECODE_COMPOUND_SCALE=(
1
60
60
24
365 # @( hack ) using only ints, discarding leapyear day, but more friendly for formatting
)
TIMECODE_UNITS=(
second
minute
hour
day
year
)
}
## FUNCTIONS:
{
pad() {
local length=$1
local padChar="$2"
local string="$3"
while [[ ${#string} -lt $length ]]; do
string="${padChar}${string}"
done
echo -n "$string"
}
secondsFromTimecode() {
local input="$1"
if [[ $input =~ ^[0-9.:]+$ ]]; then
local inputInteger=$input
local decimal=
if [[ $inputInteger =~ \. ]]; then
local decimal=${inputInteger/*./}
[[ -n $decimal ]] && decimal=".${decimal}"
inputInteger=${inputInteger/.*/}
fi
local oIFS="$IFS"
local IFS=":"
local timecodeSegments=($inputInteger)
local IFS="$oIFS"
local scale=1
local segmentIndex
local output=0
local i
for (( i=0; i < ${#timecodeSegments[@]}; i++ )); do
local segmentIndex=$(( ${#timecodeSegments[@]} - 1 - i ))
scale=$(( scale * ${TIMECODE_COMPOUND_SCALE[$i]} ))
output=$(echo "$output + ${timecodeSegments[$segmentIndex]} * $scale" | bc)
done
[[ -n $decimal ]] && output+="${decimal}"
else
output="$input"
fi
echo -n $output
}
timecodeFromSeconds() {
local status=0
local input="$1"
if [[ $input =~ ^[0-9.]+$ ]]; then
local remainder=$input
local decimal=
if [[ $input =~ \. ]]; then
local decimal=${remainder/*./}
[[ -n $decimal ]] && decimal=".${decimal}"
remainder=${remainder/.*/}
fi
local timecodeSegments=()
local i nextI newTimecodeSegments newSegment
for (( i=0; i < ${#TIMECODE_COMPOUND_SCALE[@]}; i++ )); do # start from minutes
let nextI=$(( i + 1 ))
if [[ $nextI -lt ${#TIMECODE_COMPOUND_SCALE[@]} ]]; then
newSegment=$(( remainder % ${TIMECODE_COMPOUND_SCALE[$nextI]} ))
remainder=$(( remainder / ${TIMECODE_COMPOUND_SCALE[$nextI]} )) # int
else
newSegment=$remainder
remainder=0
fi
if [[ $newSegment -gt 0 || $remainder -gt 0 ]]; then
newTimecodeSegments=($(pad 2 '0' "$newSegment"))
[[ ${#timecodeSegments[@]} -gt 0 ]] && newTimecodeSegments+=(${timecodeSegments[@]}) # i.e. prepend new
timecodeSegments=(${newTimecodeSegments[@]})
else
break
fi
done
if [[ ${#timecodeSegments[@]} -gt 0 ]]; then
local oIFS="$IFS"
local IFS=":"
local output=''
if [[ ${#timecodeSegments[@]} -eq 1 ]]; then
output+='00:'
fi
output+="${timecodeSegments[*]}${decimal:0:4}"
local IFS="$oIFS"
else
output='00:00.000'
fi
else
output="$input"
status=2
fi
echo -n $output
return $status
}
normalizeTimeCode() {
local normalized status=0
normalized="$(timecodeFromSeconds "$(secondsFromTimecode "$1")")"
status=$?
echo -n "$normalized"
return $status
}
doTimecodesDiffer() {
a="$(normalizeTimeCode $1)"
b="$(normalizeTimeCode $2)"
[[ $a != $b ]]
}
describeTimecode() {
local input="$1"
local output="$input"
local oIFS="$IFS"
local IFS=$':'
local timecodeSegments=($input)
IFS="$oIFS"
local lastSegmentIndex=$(( ${#timecodeSegments[@]} - 1 ))
output+=" ${TIMECODE_UNITS[$lastSegmentIndex]}"
if [[ ${timecodeSegments[$lastSegmentIndex]} != '01' ]]; then # @( hack ) comparison against padded string
output+="s"
fi
echo -n "$output"
}
}
## APPLICATION:
{
timecode()
{
local status=0
local showUnits=false
local shouldDecode=false
local duration
while [[ $# -gt 0 ]]; do
case $1 in
-d|--decode) shouldDecode=true;;
-u|u) showUnits=true;;
-h|--help|h) timecode_showHelp; return;;
-V|--version) echo "$TCSCRIPT_ATTRIBUTION"; return;;
*) duration="$1";;
esac
shift
done
if [[ -z $duration ]]; then
echo "Please pass one duration argument."
return 1
fi
if [[ $shouldDecode == true ]]
then
secondsFromTimecode "$duration"
status=$?
else
timecode="$(normalizeTimeCode "$duration")"
status=$?
if [[ $showUnits = true ]]; then
timecode="$(describeTimecode "$timecode")"
fi
echo -n "$timecode"
echo "" >&2
fi
return $status
}
timecode_showHelp() {
echo "USAGE: timecode [-uh] SECONDS"
echo
echo "OPTIONS:"
echo " -d Decode timecode, output seconds. Alias --decode."
echo " -u Show units"
echo " -h Show this help"
echo " -V Show version number. Alias --version."
echo
echo "$TCSCRIPT_ATTRIBUTION"
}
}
## EXECUTION:
if [[ "${BASH_SOURCE[0]}" == "$0" ]]
then
timecode "$@"
fi