slider (4344B) - View raw
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#!/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:s:" o; do case "${o}" in c) bgc="$OPTARG" ;; t) fgc="$OPTARG" ;; f) font="$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" | rg -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" | rg -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) -f text font for text slides (sans serif 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 | rg -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/,//))" 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}" ]; } && magick -size "${res:-1920x1080}" canvas:"${bgc:-black}" -gravity center "$content" -resize 1920x1080 -composite "$prepdir/$base" else { [ ! -f "$prepdir/$base" ] || [ -n "${redo+x}" ]; } && magick -size "${res:-1920x1080}" -background "${bgc:-black}" -fill "${fgc:-white}" -font "${font:-Sans}" -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 endtime="$((totseconds - seconds))" 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.