diff --git a/.local/bin/slider b/.local/bin/slider index 0fa6f88..950b496 100755 --- a/.local/bin/slider +++ b/.local/bin/slider @@ -6,26 +6,76 @@ # # Imagemagick and ffmpeg required. -# TODO: -# - add the ability to include text, using imagemagick text to image feature. +# Application cache if not stated elsewhere. +cache="${XDG_CACHE_HOME:-$HOME/.cache}/slider" +while getopts "hvrpi:c:a:o:d:f:t:e:x:" o; do case "${o}" in + c) bgc="$OPTARG" ;; + t) fgc="$OPTARG" ;; + i) file="$OPTARG" ;; + a) audio="$OPTARG" ;; + o) outfile="$OPTARG" ;; + d) prepdir="$OPTARG" ;; + r) redo="$OPTARG" ;; + s) ppt="$OPTARG" ;; + e) endtime="$OPTARG" ;; + x) res="$OPTARG" + echo "$res" | grep -qv "^[0-9]\+x[0-9]\+$" && + echo "Resolution must be dimensions separated by a 'x': 1280x720, etc." && + exit 1 ;; + p) echo "Purge old build files in $cache? [y/N]" + read -r confirm + echo "$confirm" | grep -iq "^y$" && rm -rf "$cache" && echo "Done." + exit ;; + v) verbose=True ;; + *) echo "$(basename "$0") usage: + -i input timecode list (required) + -a audio file + -c color of background (use html names, black is default) + -t text color for text slides (white is default) + -s text font size for text slides (150 is default) + -o output video file + -e if no audio given, the time in seconds that the last slide will be shown (5 is default) + -x resolution (1920x1080 is default) + -d tmp directory + -r rerun imagemagick commands even if done previously (in case files or background has changed) + -p purge old build files instead of running + -v be verbose" && exit 1 -[ -z "$1" ] && echo "Usage: - first arg: file with timecodes - second arg: audio file (optional)" && exit 1 +esac done -prepdir="$1-src" -prepfile="$prepdir/$1.prep" -outfile="$1.mp4" +# Check that the input file looks like it should. +{ head -n 1 "$file" 2>/dev/null | grep -q "^00:00:00 " ;} || { + echo "Give an input file with -i." && + echo "The file should look as this example: -totaldur="$(ffmpeg -i "$2" 2>&1 | awk '/Duration/ {print $2}' | sed s/,//)" -totseconds="$(date '+%s' -d "$totaldur")" +00:00:00 first_image.jpg +00:00:03 otherdirectory/next_image.jpg +00:00:09 this_image_starts_at_9_seconds.jpg +etc... +Timecodes and filenames must be separated by Tabs." && + exit 1 + } + +if [ -n "${audio+x}" ]; then + # Check that the audio file looks like an actual audio file. + case "$(file --dereference --brief --mime-type -- "$audio")" in + audio/*) ;; + *) echo "That doesn't look like an audio file."; exit 1 ;; + esac + totseconds="$(date '+%s' -d $(ffmpeg -i "$audio" 2>&1 | awk '/Duration/ {print $2}' | sed s/,//))" + endtime="$((totseconds-seconds))" +fi + +prepdir="${prepdir:-$cache/$file}" +outfile="${outfile:-$file.mp4}" +prepfile="$prepdir/$file.prep" + +[ -n "${verbose+x}" ] && echo "Preparing images... May take a while depending on the number of files." mkdir -p "$prepdir" -echo "Preparing images... May take a while depending on the number of files." { - while read -r x; do # Get the time from the first column. @@ -34,14 +84,20 @@ do # Duration is not used on the first looped item. duration="$((seconds - prevseconds))" - # Get the filename from the rest. - filename="${x#* }" - base="$(basename "$filename")" + # Get the filename/text content from the rest. + content="${x#* }" + base="$(basename "$content")" base="${base%.*}.jpg" - # Resize and read the image file, but not if already done. - [ ! -f "$prepdir/$base" ] && - convert -size 1920x1080 canvas:black -gravity center "$filename" -resize 1920x1080 -composite "$prepdir/$base" + if [ -f "$content" ]; then + # If images have already been made in a previous run, do not recreate + # them unless -r was given. + { [ ! -f "$prepdir/$base" ] || [ -n "${redo+x}" ] ;} && + convert -size "${res:-1920x1080}" canvas:"${bgc:-black}" -gravity center "$content" -resize 1920x1080 -composite "$prepdir/$base" + else + { [ ! -f "$prepdir/$base" ] || [ -n "${redo+x}" ] ;} && + convert -size "${res:-1920x1080}" -background "${bgc-black}" -fill "${fgc:-white}" -pointsize "${ppt:-150}" -gravity center label:"$content" "$prepdir/$base" + fi # If the first line, do not write yet. [ "$time" = "00:00:00" ] || echo "file '$prevbase' @@ -51,17 +107,16 @@ duration $duration" prevbase="$base" prevtime="$time" prevseconds="$(date '+%s' -d "$prevtime")" -done < "$1" +done < "$file" # Do last file which must be given twice as follows echo "file '$base' -duration $((totseconds-seconds)) +duration ${endtime:-5} file '$base'" } > "$prepfile" - -if [ -f "$2" ]; then - ffmpeg -y -f concat -safe 0 -i "$prepfile" -i "$2" -c:a aac -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile" +if [ -n "${audio+x}" ]; then + ffmpeg -hide_banner -y -f concat -safe 0 -i "$prepfile" -i "$audio" -c:a aac -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile" else - ffmpeg -y -f concat -safe 0 -i "$prepfile" -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile" + ffmpeg -hide_banner -y -f concat -safe 0 -i "$prepfile" -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile" fi # Might also try: