Skip to content

Instantly share code, notes, and snippets.

@Voldrix
Created March 11, 2023 16:23
Show Gist options
  • Save Voldrix/a66cb5c66dfae00e3f1e931e7a368adf to your computer and use it in GitHub Desktop.
Save Voldrix/a66cb5c66dfae00e3f1e931e7a368adf to your computer and use it in GitHub Desktop.
Generate .BIF media preview thumbnails (trick play)
#!/bin/bash
#Generate .BIF media preview thumbnails
#REQUIRES: ffmpeg, mediainfo, and roku's biftool
if [ -z "$1" ]; then echo -e "\nGenerate .BIF media preview thumbnails\nFiles are stored in source folder\n\nUsage: ${0##*/} [video files/directories...] <Absolute or Relative paths. Recursive>\n";exit 1;fi
tmpdir="/tmp/bifgen"
[ -d "$tmpdir" ] && rm -f "$tmpdir"/* || mkdir -p "$tmpdir"
IFS=$'\n'
wd="$(pwd)"
cd /tmp/
new=0
#Store bif files in separate directory tree. The directory tree will match the folder structure of the input files, but with a different directory prefix
#e.g. /dir/vids/* => /bif/*
#disabled if null, .bif in source folder
indir=""
outdir=""
for z;do
#Input parameter is a relative or absolute path
[[ "$z" =~ ^/ ]] || z="$wd/$z"
[ -e "$z" ] || continue
[ -d "$z" ] && files=($(find "$z" -type f -regextype sed -regex ".*\.[mM][kKpP][vV4]"))
[ -f "$z" ] && files=("$z")
for i in "${files[@]}";do
if [[ -n "$indir" && "$i" =~ ^"$indir" ]]; then
outd=$(sed "s:^${indir}:${outdir}:" <<< ${i%/*})
[ -d "$outd" ] || (mkdir -p "$outd" && echo -e "\e[35mMKDIR: $outd\e[39m")
out="${outd}/${i##*/}.bif"
else
out="${i}.bif"
fi
#bif already exists, don't overwrite
if [ -f "$out" ];then
echo -e "\e[33mSKIP: $out\e[39m"
continue
fi
duration=$(mediainfo --Inform="General;%Duration%" "$i")
separation=10 #1-15min 1-90frames
[ -n "$duration" ] && [ "$duration" -gt 900000 ] && separation=12 #15-70min 90-350frames (150frames/30min)
[ -n "$duration" ] && [ "$duration" -gt 4200000 ] && separation=22 #70min-2hr 190-327frames
[ -n "$duration" ] && [ "$duration" -gt 7200000 ] && separation=26 #2hr+ 277frames+
#Do not select frames at a fixed time interval, as the timecode will drift.
#BIF does not contain timecodes, you have to calculate them by multiplying the framerate/separation by the frame number.
#Selecting the first frame after the modulo of the separation time rolls over to zero, you ensure the frame number multiplication will line up with the timecode.
ffmpeg -nostdin -i "$i" -vsync vfr -vf "select=gt(ld(1)\,st(1\,mod(t\,${separation})))+isnan(prev_selected_t),scale=240:160:force_original_aspect_ratio=decrease:flags=bicubic:sws_dither=none" -c:v mjpeg -qscale:v 4 -v 0 -y -f image2 "${tmpdir}/%08d.jpg"
if [ $? -ne 0 ];then
rm -f "${tmpdir}"/*
continue
fi
#ffmpeg's image sequence output starts at number one. We must shift every image down one to start at zero in order to line up with the timecode
j="${tmpdir}/00000000.jpg"
for f in "$tmpdir"/*; do
mv "$f" "$j"
j="$f"
done
#Make sure biftool is in $PATH, or add absolute path to it
biftool -t ${separation}000 "${tmpdir}"
if [ -f "${tmpdir##*/}.bif" ]; then
mv "${tmpdir##*/}.bif" "$out" && echo -e "\e[32m$out\e[39m" && chmod 664 "$out" || echo -e "\e[31mFAILED: $out\e[39m"
((new++))
else
echo -e "\e[31mFAILED: $i\e[39m"
fi
rm -f "${tmpdir}"/*
done
done
rmdir "$tmpdir"
echo "new bif created: $new"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment