forked from vitorgalvao/tiny-scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
progressbar
executable file
·183 lines (151 loc) · 5.47 KB
/
progressbar
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
#!/bin/bash
readonly program="$(basename "${0}")"
# Defaults
bar_color='#f12b24'
bar_height_percent='1'
bar_pos='bottom'
seconds_delay='1.5'
output_file='output.gif'
function message {
echo "${1}"
}
function wrong_arguments {
tput setaf 1
echo 'You need either give multiple images or one video file as arguments' >&2
tput sgr0
usage
exit 1
}
function depends_on {
readonly local all_deps=("${@}")
local missing_deps=()
for dep in "${all_deps[@]}"; do
if ! command -v "${dep}" &>/dev/null; then
missing_deps+=("${dep}")
fi
done
if [[ "${#missing_deps[@]}" -gt 0 ]]; then
tput setaf 1
echo -e '\nThis script has unmet dependencies. You need to install these first:'
printf ' %s\n' "${missing_deps[@]}"
tput sgr0
exit 1
fi
}
function syntax_error {
echo -e "${program}: ${1}\nTry \`${program} --help\` for more information." >&2
exit 1
}
function usage {
echo "
Usage:
${program} [options] <file...>
Options:
-c, --bar-color <color> Default: #f12b24.
-s, --bar-height <integer> Bar’s height as a percent of the total height. Default: 1.
-p, --bar-position [top|bottom] Default: bottom.
-d, --delay <number> Delay between each frame, in seconds. Default: 1.5.
-o, --output-file <file> File to output to. Default: output.gif in current directory. Saves to video when given the .mov extension.
-g, --gifski Use gifski for the conversion.
-h, --help Show this help.
" | sed -E 's/^ {4}//'
}
# Available flags
args=()
while [[ "${1}" ]]; do
case "${1}" in
-h | --help)
usage
exit 0
;;
-c | --bar-color)
bar_color="${2}"
shift
;;
-s | --bar-height)
bar_height_percent="${2}"
shift
;;
-p | --bar-position)
bar_pos="${2}"
shift
;;
-d | --delay)
seconds_delay="${2}"
shift
;;
-o | --output-file)
output_file="${2}"
shift
;;
-g | --gifski)
use_gifski='true'
;;
--)
shift
args+=("${@}")
break
;;
-*)
syntax_error "unrecognized option: ${1}"
;;
*)
args+=("${1}")
;;
esac
shift
done
set -- "${args[@]}"
trap 'exit 1' SIGINT
depends_on 'convert' 'identify' 'ffmpeg' 'ffprobe' 'gfv'
# Determine if working from images or video
[[ "${#}" -eq 0 ]] && wrong_arguments
if [[ "${#}" -eq 1 ]]; then
[[ "$(file --mime-type --brief "${1}")" != 'video'* ]] && wrong_arguments
readonly background_video="${1}"
else
readonly background_video="$(mktemp).mov"
readonly images=("${@}")
for file in "${images[@]}"; do
[[ "$(file --mime-type --brief "${file}")" != 'image'* ]] && wrong_arguments
done
readonly frame_delay="$(bc <<< "${seconds_delay} * 100")"
message 'Generating intermediary video file…'
convert -delay "${frame_delay}" "${images[@]}" "${background_video}"
fi
message 'Generating progress bar…'
readonly total_frames="$(ffprobe -loglevel error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 "${background_video}")"
readonly total_steps="$(bc <<< "${total_frames} - 1")" # Remove one from the total since we will start counting steps from 0. This is for the logic of having no bar on the first step and to map to the array correctly.
readonly canvas_width="$(ffprobe -loglevel error -count_frames -show_entries stream=width -of default=nokey=1:noprint_wrappers=1 "${background_video}")"
readonly canvas_height="$(ffprobe -loglevel error -count_frames -show_entries stream=height -of default=nokey=1:noprint_wrappers=1 "${background_video}")"
readonly bar_height_px="$(bc <<< "${canvas_height} * ${bar_height_percent} / 100")"
[[ "${bar_pos}" == 'top' ]] && readonly bar_ystart='0' || readonly bar_ystart="$(bc <<< "${canvas_height} - ${bar_height_px}")"
readonly bar_yend="$(bc <<< "${bar_ystart} + ${bar_height_px}")"
readonly tmp_bar_graphics_dir="$(mktemp -d)"
readonly tmp_bar_video="$(mktemp).mov"
# Make bar graphics.
for step_name in $(seq -w 0 "${total_steps}"); do
[[ "${step_name}" =~ ^0+$ ]] && step_number='0' || step_number="$(sed -E 's/^0+//' <<< "${step_name}")" # Remove leading zeros
if [[ "${step_number}" -eq 0 ]]; then
bar_width='0' # First frame shold never have a bar. Without this we'd have to divide by zero.
elif [[ "${step_number}" -eq "${total_steps}" ]]; then
bar_width="${canvas_width}" # Last frame should always fill the full width. Without this we may get a fractional result slightly smaller.
else
bar_width="$(bc -l <<< "${canvas_width} / ${total_steps} * ${step_number}")"
fi
convert -size "${canvas_width}"x"${canvas_height}" canvas:transparent -fill "${bar_color}" -draw "rectangle 0,${bar_ystart} ${bar_width},${bar_yend}" "${tmp_bar_graphics_dir}/${step_name}.png"
done
# Make bar graphics into a video and superimpose on the original.
ffmpeg -loglevel error -pattern_type glob -i "${tmp_bar_graphics_dir}/*.png" -vcodec png "${tmp_bar_video}"
readonly tmp_superimposed_video="$(mktemp).mov"
ffmpeg -i "${background_video}" -i "${tmp_bar_video}" -filter_complex overlay "${tmp_superimposed_video}"
# Make gif
message 'Creating gif…'
if [[ "${output_file}" == *'.mov' ]]; then
mv "${tmp_superimposed_video}" "${output_file}"
else
options=()
[[ -n "${use_gifski}" ]] && options+=('--gifski')
gfv "${options[@]}" "${tmp_superimposed_video}" --output-file "${output_file}"
fi
message "Saved to ${output_file}"