Skip to content

Instantly share code, notes, and snippets.

@Eltiech
Last active October 5, 2024 16:04
Show Gist options
  • Save Eltiech/5520052ba29b0c97912622ef5747707b to your computer and use it in GitHub Desktop.
Save Eltiech/5520052ba29b0c97912622ef5747707b to your computer and use it in GitHub Desktop.
#!/bin/bash
#Batch processor for assorted media yielding web playback compatible mp4s
#basic summary of args
#batch-ffmpegger.sh tuningparameter [FILES]... [trimstring]
#tuningparameter - ffmpeg's x264 tuning parameter. should be "film" or "animation" most likely.
# (googlit for the other options which you probably don't need)
#FILES - however many files you want. Doesn't do directories, use a wildcard/*.mkv etc
#trimstring - an optional custom string to trim the torrent-name and resolution stuff off the end
# of the file. a regex down below catches most of it though
# if the last argument isn't an existing file, it will be treated as the trimstring
#notes on behavior of this itteration:
#* If you don't have a version of ffmpeg with --enable-libfdk_aac, fix that. it's better than ffmpeg's built-in, just
# not default because of stupid software patents. If you don't do this, audio arguments in the final command will need tweaking
#* If surround sound is detected it will be downsampled with a formula superior to ffmpeg's default
#* Necessary bc firefox chokes on surround sound last I checked (not recently, admittedly)
#* DOES NOT account for 7.1 mid channels. Most of the crap I process is 5.1.
#* preset is set to slow down in the main ffmpeg command, change that to medium or fast if you're
# like, really impatient and/or your machine sucks
#* volume is boosted till the max volume within the file is 0.0 (loudest without clipping)
# This is highly desirable for most surround sound downsampling(often heckin quiet otherwise)
# This may make things overall undesirably loud if your media item has absolutely no loud noises (haven't had this happen much in practice)
# ideally I guess one would have prefer a target mean volume or some such. but it don't be like that rn.
#* target res is generally gonna be scaled down to 720p if source is 800p or greater. (no change if less)
#* Scaling is done with lanczos. Makes for "I can't believe it's not [one standard step in resolution up] sharpness"
#* crf is 25. Decent balance of size/quality imho. I sometimes go as high as 27. 23 is ffmpeg/x264's default. Higher num = lower quality.
#* tweaking may necessary if there are multiple audio or video tracks, or they're in some nonstandard order
#* Quits processing on a bad file, remove """|| echo "Something borked for $i" && exit""" if you don't want that
#* the file's metadata title is set to the trimmed name of file.
#* as current written everything goes to /tmp/
#* Batch ffmpeger? I barely know 'er
#like change this if you want to nice it lower (higher priority). +5 makes it more of a background thing.
sudo renice +5 $$
IFS=$'\n'
echo "files: ${@:${#@}}"
TRIMSTRING=""
LASTARG="${@:${#@}}"
if [ ! -e "$LASTARG" ]; then
TRIMSTRING="$LASTARG"
echo "Custom trim string: $TRIMSTRING"
#remove the last arg
set -- "${@:1:$(($#-1))}"
echo "$@"
fi
function trimname()
{
FNAME="$1"
SHORT="$(perl -pe 's/([^\/]+?)([ \.]+(1080i|1080p|HEVC|AVC|x264|FiNAL|x265|H264|H265|720i|720p|BluRay|REPACK|AMZN|PROPER|2160p|MULTi)[^\/]+)?\.\w{2,4}$/\1/'<<<"$FNAME")"
#replace periods with spaces to avoid Some.Show.Name.S01E05 looking stuff
SHORT="$(perl -pe 's/\./ /g'<<<"${SHORT##*/}")"
if [ -n "$TRIMSTRING" ]; then
SHORT="${SHORT% ${TRIMSTRING}*}"
fi
echo -n "$SHORT"
}
#preview the trimmed names
echo "Cleaned up names are:"
for i in "${@:2}"; do
echo "$(trimname "$i")"
done
for i in "${@:2}"; do
SHORTNAME="$(trimname "$i")"
echo "SHORTNAME: $SHORTNAME"
FFPROBE="$(ffprobe "$i" 2>&1)"
#SUBTRACK="$(grep -Poh '(?<=#)\d:\d{1,2}(?=\(eng\): Subtitle)')<<<"$FFPROBE")"
#tries and find the resolution. also tries to avoid attached images like cover art or included thumbnails
VIDTRACK="$(grep -v "attached"<<<"$FFPROBE"| grep -Po 'Stream #0:\d(\[\w{3,6}\])?(\(\w{3}\))?: Video.*?, \d{3,4}x\d{3,4}')"
echo "VIDTRACK: $VIDTRACK"
[ -z "$VIDTRACK" ] && echo "shit, bad vidtrack detection for $i" && exit
#split the resolution string into an array
IFS=" x"
vtsplit=($VIDTRACK)
IFS="\n"
HEIGHT="${vtsplit[-1]}"
WIDTH="${vtsplit[-2]}"
echo "${WIDTH} x ${HEIGHT}"
#if it's 800p or higher, let's shrink it.
if ((HEIGHT>799)); then
WIDTH=$((720 * WIDTH / HEIGHT))
WIDTH=$((WIDTH + WIDTH%2)) #even it out if something's odd
HEIGHT=720
fi
echo "Res will be ${WIDTH} x ${HEIGHT}"
sleep 2
grep -P 'Audio:.*?(5|7).(0|1)'<<<"$FFPROBE"
if grep -P 'Audio:.*?(5|7).(0|1)'<<<"$FFPROBE"; then
echo "Surround sound detected?"
grep -P 'Audio:.*?(5|7).(0|1)'<<<"$FFPROBE"
AUDIOFILTER="pan=stereo|FL=0.5*FC+0.707*FL+0.707*BL+0.5*LFE|FR=0.5*FC+0.707*FR+0.707*BR+0.5*LFE"
fi
#
[[ -n "$AUDIOFILTER" ]]&&COMMA=","
BOOST="$(ffmpeg -hide_banner -i "$i" -map 0:a -filter:a "${AUDIOFILTER}${COMMA}volumedetect" -vn -sn -dn -f null /dev/null 2>&1 | grep -Po "(?<=max_volume: -)[0-9\.]+")"
[ -z "$BOOST" ] && BOOST="0"
BOOST="$BOOST"
echo "BOOST IS $BOOST"
BOOST="${BOOST}dB"
AUDIOFILTER="${AUDIOFILTER}${COMMA}volume=$BOOST"
echo "Audio Filter will be $AUDIOFILTER"
#\"$AUDIOFILTER\""
#if there's an audio filter string, we needa add the flag
#with the boost stuff now moved out of the surround stuff though, always needed..whatever
[[ -n "$AUDIOFILTER" ]]&&AFB="-filter:a"
echo "SHORTNAME: $SHORTNAME"
ffmpeg -hide_banner -y -canvas_size "${WIDTH}x${HEIGHT}" -i "$i" -sn -c:v libx264 -crf 24 -preset slow -pix_fmt yuv420p -filter_complex "[0:v]scale=w=${WIDTH}:h=${HEIGHT}[vid]" -map "[vid]" -sws_flags lanczos $AFB "$AUDIOFILTER" -map "0:a" -tune "$1" -c:a libfdk_aac -vbr 5 -movflags +faststart -threads 0 -dn -metadata Title="$SHORTNAME" -f mp4 "/tmp/${SHORTNAME}.mp4" || echo "Something borked for $i" && exit
#Insert whatever uploading bits you wanna use here, if you want
#rm "/tmp/${SHORTNAME}"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment