#!/bin/bash
# blurp scene detected frames
# not using anything from singularity, needs:
# ffmpeg and catimg (and maybe imagemagick)
# img2sixel (if term has sixel support and you enable this)
# last update: 20240416
# maybe fixed 1000th detection bug
# sixel support via img2sixel
# usage:
# sceneFrameMitHtml *.mp4
# sixel=1 sceneFrameMitHtml *.mov
# sceneFrameMitHtml --wait movie.mov # < wait for user keypress before scrolling on
# parallel -j 3 --gnu sceneFrameMitHtml {} ::: *.mp4
# test:
# watch -n 1 tree $HOME/tmp/test/
debug=""
htmlstart() {
# var
rnd="$1-$RANDOM" # input file name and random number
rnd="${rnd// /_}" # spaces to underscores
# config html root fodler
html="$HOME/tmp/html/$rnd"
mkdir -p "$html" || { echo "!! unable to create a $html subdir" >&2; tmp=; exit 1; }
touch "$html/index.html"
echo "
$title" > "$html/index.html"
cat >> "$html/index.html" <<'_EOF'
_EOF
}
htmlend() {
echo "" >> "$html/index.html"
echo "" >> "$html/index.html"
}
# tmp dir
tmp="$HOME/tmp/test/$RANDOM"
trap '[ -n "$tmp" ] && kill %% && rm -fr "$tmp"' EXIT
mkdir -p "$tmp" "$tmp/make" "$tmp/show" || { echo '!! unable to create a tmpdir' >&2; tmp=; exit 1; }
# --wait or -w # must be 1st parameter
if [[ "$1" == "-w" ]] || [[ "$1" == "--wait" ]]; then # wait for anykey before scrolling on
userwait="1"
shift
fi
# main
while [ $# -gt 0 ]; do # for each file loop
# crop detection old
#crop="$(ffmpeg -i "$1" -t 100 -vf cropdetect -f null - 2>&1 | awk '/crop/ { print $NF }' | tail -1)"
# crop detection new
# movie duration/2 for crop detection
dur=$(ffprobe -v panic -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$1")
halfdur=$(bc <<< "$dur / 2.00")
howlong=$(bc <<< "$halfdur + 25") # seconds
# crop detection
#crop="$(ffmpeg -ss "$halfdur" -to "$howlong" -i "$1" -vf cropdetect=limit=65 -f null - 2>&1 | awk '/crop/ { print $NF }' | tail -1)"
crop="$(ffmpeg -ss "$halfdur" -to "$howlong" -i "$1" -vf "pp=autolevels|f, cropdetect" -f null - 2>&1 | awk '/crop/ { print $NF }' | tail -1)"
if [ -z "$crop" ]; then
echo "Crop detection failed, exiting..."
#exit 1
shift
continue
else
(( debug )) && echo "crop: $crop"
fi
# first frame, I want as well
ffmpeg -hide_banner -loglevel panic -i "$1" -vf "${crop}" -vframes 1 -pix_fmt rgb24 "$tmp/make/0000.png" || exit
# scene detect
ffmpeg -hide_banner -loglevel info -nostats -i "$1" -threads 0 -vf "${crop},select=gt(scene\,0.2)",showinfo -pix_fmt rgb24 -vsync vfr "$tmp/make/%04d.png" 2> "$tmp/detect.log" >/dev/null &
pid="$!"
# title
echo; echo
title="$1"
# toilet -t -f smmono12 "$1"
echo "$title"; echo
htmlstart "$1"
sleep 2 # why?
# checking if the pid is still active OR some files left to process OR show
# ps -p $pid &>/dev/null && echo "true"
# ls "$tmp/make"* && echo "true"
# ls "$tmp/show"* && echo "true"
#while ps -p $pid &>/dev/null || ls "$tmp/make"* >/dev/null 2>&1 || ls "$tmp/show" >/dev/null 2>&1; do
while ps -p $pid &>/dev/null || [ "$(ls -A "$tmp/make/")" ] || [ "$(ls -A "$tmp/show/")" ] ; do
if [ "$(ls -A "$tmp/make/")" ]; then
mv "$tmp/make/"* "$tmp/show" >/dev/null 2>&1
fi
if [ "$(ls -A "$tmp/show/")" ]; then
pushd "$tmp/show" >/dev/null || exit
# lets just hardcode 16:9
#mogrify -resize 1920x1080! *
for file in *; do # this are images
name="${file%.*}" # name without extension
# frame number must be the same as in detection log
frame="$(echo "$name" | sed -r 's/^0+//g')"
frame="$(( frame - 1))"
termheight="$(tput lines)" && height="$(( termheight * 1 / 2 ))"
# give me frame and time position
#var="$(cat "$tmp/detect.log" | grep "n: \+$frame ")"
# BUG is here, "n: \+$frame will not get 'n:1000'
# BUG var="$(grep "n: \+$frame " "$tmp/detect.log")"
# \s matches any whitespace chars (including newline, tab), and you can use * to match ZERO or more of them
var="$(grep "n:\s*$frame " "$tmp/detect.log")";
(( debug )) && echo "var: $var"
tmptime="${var#*pts_time:}"
# this will break with never versions of ffmpeg
#tmptime="${tmptime%pos:*}"
# ^ replaced with more general 'cut before any alpha char':
tmptime="${tmptime%%[[:alpha:]]*}"
(( debug )) && echo "tmptime: $tmptime"
time="$(date -u -d @"${tmptime}" +"%H:%M:%S,%3N" 2>/dev/null)"
(( debug )) && echo "time: $time"
# lets do the 25fps timecode instead
front="$(echo "$time" | cut -d',' -f1)" # part before comma
miliseconds="$(echo "$time" | cut -d',' -f2)" # part after comma
miliseconds="$(echo "$miliseconds" | sed 's/^0*//')" # remove leading zeroes
#echo "$miliseconds"
#set -x
frame_number="$((miliseconds / 40))" # assuming 25 fps timecode is needed
frame_number="$(printf "%02d" "$frame_number")" # pad with zeros
#set +x
if [[ "$name" != "0000" ]]; then
time="$front:$frame_number"
else
time="00:00:00:00"
fi
echo "$name $time $title"
if [[ "$sixel" != "1" ]]; then
# catimg
dualheight="$(( height * 4 / 3 ))" # 2 is 2 big for my taste
#dualheight="$(( height * 2 ))" # bigger
catimg -H "${dualheight}" "$file" || exit 1
else # use sixels
img2sixel -w 600 "$file" || exit 1
fi
if (( userwait )); then
read -n 1 -s -r -p "" # wait for any key from user
else
sleep 0.1
fi
# html write as well
convert "$file" -resize 562000@\> "$html/$name.jpg" || exit 1
#echo " | $name $time" >> "$html/index.html" # fuck css layouts
echo "$name $time " >> "$html/index.html" # unfuck css layouts
rm "$file" >/dev/null 2>&1
done
popd >/dev/null || exit
fi
sleep 1
done
wait
# store ffmpeg detect log as well
pushd "$tmp" >/dev/null && zip "$html/detect" "detect.log" >/dev/null && popd >/dev/null || exit
htmlend
# echo fps for informational purposes
(( debug )) && fps="$(grep -o "[0-9]* fps" "$tmp/detect.log" | head -n 1 | awk '{print $1}')"
(( fps )) && echo "fps=$fps"
htmlindex="$html/index.html"
echo "path: $htmlindex"
if command -v wslpath >/dev/null 2>&1; then
echo -n "windows path: "
wslpath -w "$htmlindex"
fi
echo
shift
done
|