From 0d1a84f59d8cdd62c97410f96f8a13c98c51453a Mon Sep 17 00:00:00 2001
From: marlonivo <email@marlonivo.xyz>
Date: Mon, 14 Apr 2025 09:16:38 +0000
Subject: initial

---
 .local/bin/slider | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)
 create mode 100755 .local/bin/slider

(limited to '.local/bin/slider')

diff --git a/.local/bin/slider b/.local/bin/slider
new file mode 100755
index 0000000..3460c77
--- /dev/null
+++ b/.local/bin/slider
@@ -0,0 +1,126 @@
+#!/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" | 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)
+  -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 | 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}" -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
+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.
-- 
cgit v1.2.3