#!/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