2 # Desc: Combines video files into time lapse video.
3 # Usage: timelapse_from_videos.sh [FILES]
4 # Example: timelapse_from_videos.sh ./*.MP4
6 declare -ag arrayPosArgs
7 declare -Ag appRollCall
# Associative array for storing app status
8 declare -Ag fileRollCall
# Associative array for storing file status
9 declare -Ag dirRollCall
# Associative array for storing dir status
11 yell
() { echo "$0: $*" >&2; } # print script path and all args to stderr
12 die
() { yell
"$*"; exit 111; } # same as yell() but non-zero exit status
13 try
() { "$@" || die
"cannot $*"; } # runs args as command, reports args if command fails
15 # Description: Prints verbose message ("vbm") to stderr if opVerbose is set to "true".
16 # Usage: vbm "DEBUG :verbose message here"
21 # Depends: bash 5.1.16, GNU-coreutils 8.30 (echo, date)
23 if [ "$opVerbose" = "true" ]; then
24 functionTime
="$(date --iso-8601=ns)"; # Save current time in nano seconds.
25 echo "[$functionTime]:$0:""$*" 1>&2; # Display argument text.
29 return 0; # Function finished.
30 } # Displays message if opVerbose true
32 # Desc: If arg is a command, save result in assoc array 'appRollCall'
33 # Usage: checkapp arg1 arg2 arg3 ...
35 # Input: global assoc. array 'appRollCall'
36 # Output: adds/updates key(value) to global assoc array 'appRollCall'
42 if command -v "$arg" 1>/dev
/null
2>&1; then # Check if arg is a valid command
43 appRollCall
[$arg]="true";
44 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi;
46 appRollCall
[$arg]="false"; returnState
="false";
50 #===Determine function return code===
51 if [ "$returnState" = "true" ]; then
56 } # Check that app exists
58 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
59 # Usage: checkfile arg1 arg2 arg3 ...
61 # Input: global assoc. array 'fileRollCall'
62 # Output: adds/updates key(value) to global assoc array 'fileRollCall';
63 # Output: returns 0 if app found, 1 otherwise
69 if [ -f "$arg" ]; then
70 fileRollCall
["$arg"]="true";
71 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi;
73 fileRollCall
["$arg"]="false"; returnState
="false";
77 #===Determine function return code===
78 if [ "$returnState" = "true" ]; then
83 } # Check that file exists
85 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
86 # Usage: checkdir arg1 arg2 arg3 ...
88 # Input: global assoc. array 'dirRollCall'
89 # Output: adds/updates key(value) to global assoc array 'dirRollCall';
90 # Output: returns 0 if all args are dirs; 1 otherwise
96 if [ -z "$arg" ]; then
97 dirRollCall
["(Unspecified Dirname(s))"]="false"; returnState
="false";
98 elif [ -d "$arg" ]; then
99 dirRollCall
["$arg"]="true";
100 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi
102 dirRollCall
["$arg"]="false"; returnState
="false";
106 #===Determine function return code===
107 if [ "$returnState" = "true" ]; then
112 } # Check that dir exists
114 # Desc: Displays missing apps, files, and dirs
115 # Usage: displayMissing
117 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
118 # Output: stderr: messages indicating missing apps, file, or dirs
119 # Output: returns exit code 0 if nothing missing; 1 otherwise
120 # Depends: bash 5, checkAppFileDir()
121 local missingApps value appMissing missingFiles fileMissing
122 local missingDirs dirMissing
124 #==BEGIN Display errors==
125 #===BEGIN Display Missing Apps===
126 missingApps
="Missing apps :";
127 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
128 for key
in "${!appRollCall[@]}"; do
129 value
="${appRollCall[$key]}";
130 if [ "$value" = "false" ]; then
131 #echo "DEBUG:Missing apps: $key => $value";
132 missingApps
="$missingApps""$key ";
136 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
137 echo "$missingApps" 1>&2;
140 #===END Display Missing Apps===
142 #===BEGIN Display Missing Files===
143 missingFiles
="Missing files:";
144 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
145 for key
in "${!fileRollCall[@]}"; do
146 value
="${fileRollCall[$key]}";
147 if [ "$value" = "false" ]; then
148 #echo "DEBUG:Missing files: $key => $value";
149 missingFiles
="$missingFiles""$key ";
153 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
154 echo "$missingFiles" 1>&2;
157 #===END Display Missing Files===
159 #===BEGIN Display Missing Directories===
160 missingDirs
="Missing dirs:";
161 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
162 for key
in "${!dirRollCall[@]}"; do
163 value
="${dirRollCall[$key]}";
164 if [ "$value" = "false" ]; then
165 #echo "DEBUG:Missing dirs: $key => $value";
166 missingDirs
="$missingDirs""$key ";
170 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
171 echo "$missingDirs" 1>&2;
174 #===END Display Missing Directories===
176 #==END Display errors==
177 #==BEGIN Determine function return code===
178 if [ "$appMissing" == "true" ] ||
[ "$fileMissing" == "true" ] ||
[ "$dirMissing" == "true" ]; then
183 #==END Determine function return code===
184 } # Display missing apps, files, dirs
186 # Desc: Display script usage information
191 # Depends: GNU-coreutils 8.30 (cat)
194 timelapse_from_videos.sh [ options ] [FILE...]
198 Display help information.
200 Display script version.
202 Display debugging info.
204 Define output file path.
206 Indicate end of options.
209 timelapse_from_videos.sh -o output.mp4 in1.mp4 in2.mp4 in3.mp4
210 timelapse_from_videos.sh -o output.mp4 -- in1.mp4 in2.mp4 in3.mp4
212 } # Display information on how to use this script.
214 # Desc: Displays script version and license information.
217 # Input: scriptVersion var containing version string
219 # Depends: vbm(), yell, GNU-coreutils 8.30
221 # Initialize function
222 vbm
"DEBUG:showVersion function called."
225 timelapse_from_videos.sh 0.0.1
226 Copyright (C) 2022 Steven Baltakatei Sandoval
227 License GPLv3: GNU GPL version 3
228 This is free software; you are free to change and redistribute it.
229 There is NO WARRANTY, to the extent permitted by law.
232 Copyright (C) 2020 Free Software Foundation, Inc.
233 License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
234 This is free software: you are free to change and redistribute it.
235 There is NO WARRANTY, to the extent permitted by law.
239 vbm
"DEBUG:showVersion function ended."
240 return 0; # Function finished.
241 } # Display script version.
243 # Desc: Processes arguments provided to script.
244 # Usage: processArgs "$@"
246 # Input: "$@" (list of arguments provided to the function)
247 # Output: Sets following variables used by other functions:
248 # opVerbose Indicates verbose mode enable status. (ex: "true", "false")
249 # pathFileOut1 Path to output file.
250 # opFileOut1_overwrite Indicates whether file pathFileOut1 should be overwritten (ex: "true", "false").
251 # arrayPosArgs Array of remaining positional argments
253 # yell() Displays messages to stderr.
254 # vbm() Displays messsages to stderr if opVerbose set to "true".
255 # showUsage() Displays usage information about parent script.
256 # showVersion() Displays version about parent script.
257 # arrayPosArgs Global array for storing non-option positional arguments (i.e. arguments following the `--` option).
258 # External dependencies: bash (5.1.16), echo
260 # [1]: Marco Aurelio (2014-05-08). "echo that outputs to stderr". https://stackoverflow.com/a/23550347
261 # [2]: "Handling positional parameters" (2018-05-12). https://wiki.bash-hackers.org/scripting/posparams
263 # Initialize function
264 vbm
"DEBUG:processArgs function called."
267 while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
268 #yell "DEBUG:Starting processArgs while loop." # Debug stderr message. See [1].
269 #yell "DEBUG:Provided arguments are:""$*" # Debug stderr message. See [1].
271 -h |
--help) showUsage
; exit 1;; # Display usage.
272 --version) showVersion
; exit 1;; # Show version
273 -v |
--verbose) opVerbose
="true"; vbm
"DEBUG:Verbose mode enabled.";; # Enable verbose mode. See [1].
274 -o |
--output-file) # Define output file path
275 if [ -f "$2" ]; then # If $2 is file that exists, prompt user to continue to overwrite, set pathFileOut1 to $2, pop $2.
276 yell
"Specified output file $2 already exists. Overwrite? (y/n):"
277 read -r m
; case $m in
278 y | Y |
yes) opFileOut1_overwrite
="true";;
279 n | N | no
) opFileOut1_overwrite
="false";;
280 *) yell
"Invalid selection. Exiting."; exit 1;;
282 if [ "$opFileOut1_overwrite" == "true" ]; then
285 vbm
"DEBUG:Output file pathFileOut1 set to:""$2";
287 yell
"ERORR:Exiting in order to not overwrite output file:""$pathFileOut1";
293 vbm
"DEBUG:Output file pathFileOut1 set to:""$2";
295 --) # End of all options. See [2].
298 vbm
"DEBUG:adding to arrayPosArgs:$arg";
299 arrayPosArgs
+=("$arg");
302 -*) showUsage
; yell
"ERROR: Unrecognized option."; exit 1;; # Display usage
303 *) # Handle all other arguments. See [1].
304 ## Store in arrayPosArgs
306 vbm
"DEBUG:adding to arrayPosArgs:$arg";
307 arrayPosArgs
+=("$arg");
315 vbm
"DEBUG:processArgs function ended."
316 return 0; # Function finished.
317 } # Evaluate script options from positional arguments (ex: $1, $2, $3, etc.).
320 # arrayPosArgs Array of remaining positional argments
321 # Depends: ffmpeg 4.4.2, ffprobe 4.4.2
324 ## Check required commands
325 checkapp ffmpeg ffprobe
;
329 vbm
"$(declare -p arrayPosArgs;)";
330 ### Check non-option positional arguments are files
331 for arg
in "${arrayPosArgs[@]}"; do
332 if [[ ! -f "$arg" ]]; then
333 die
"FATAL:Not a file:$arg (at $(readlink -f "$arg") )"; fi;
336 # Check that files are video files
345 # Author: Steven Baltakatei Sandoval