forked from julianmarcos/dot
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
4.0 KiB
124 lines
4.0 KiB
#!/bin/sh |
|
|
|
# Give a file with images and timecodes and creates a video slideshow of them. |
|
# |
|
# Timecodes must be in format 00:00:00. |
|
# |
|
# Imagemagick and ffmpeg required. |
|
|
|
# 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 |
|
|
|
esac done |
|
|
|
# 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: |
|
|
|
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" |
|
|
|
{ |
|
while read -r x; |
|
do |
|
# Get the time from the first column. |
|
time="${x%% *}" |
|
seconds="$(date '+%s' -d "$time")" |
|
# Duration is not used on the first looped item. |
|
duration="$((seconds - prevseconds))" |
|
|
|
# Get the filename/text content from the rest. |
|
content="${x#* }" |
|
base="$(basename "$content")" |
|
base="${base%.*}.jpg" |
|
|
|
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' |
|
duration $duration" |
|
|
|
# Keep the information required for the next file. |
|
prevbase="$base" |
|
prevtime="$time" |
|
prevseconds="$(date '+%s' -d "$prevtime")" |
|
done < "$file" |
|
# Do last file which must be given twice as follows |
|
echo "file '$base' |
|
duration ${endtime:-5} |
|
file '$base'" |
|
} > "$prepfile" |
|
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 -hide_banner -y -f concat -safe 0 -i "$prepfile" -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile" |
|
fi |
|
|
|
# Might also try: |
|
# -vf "fps=${fps:-24},format=yuv420p" "$outfile" |
|
# but has given some problems.
|
|
|