From 8d865f377170c3ce01040c80e8d36d3749da8cd4 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Thu, 7 Nov 2019 07:42:43 -0800
Subject: [PATCH 01/29] v0.9

---
 burnSubs | 553 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 553 insertions(+)
 create mode 100755 burnSubs

diff --git a/burnSubs b/burnSubs
new file mode 100755
index 0000000..589f85e
--- /dev/null
+++ b/burnSubs
@@ -0,0 +1,553 @@
+#!/bin/bash
+set -e
+set -o nounset
+set -o errexit
+
+################################################################################
+# burnSubs
+# version 0.9.1
+#################3
+# Wishlist:
+#   queue encodes
+#   finish TODOs
+#	list subtitles flag
+#	finish help flag
+#	audio recode flag
+################################################################################
+
+function machineSetup() {
+	# Default setup
+	export FF_ENC="libx264"
+	export FF_HW=""
+	export FILT_PFX=""
+	export FILT_SFX=""
+	CRF=${OPTS_CRF:-23}
+	export FF_STD="-preset myTranscoderPreset -crf $CRF -tune animation \
+		-preset medium -movflags +faststart"
+	export FF_EXT="-profile:v high -level 4.0"
+	# Handle the time limit argument used for testing if it was passed
+	if [[ "${OPTS_TIMELIMIT}" != "null" ]]; then
+		export LIM_TIME="-t ${OPTS_TIMELIMIT}"
+	else
+		export LIM_TIME=""
+	fi
+
+	# The default extraProc function. This should be overridden by each encoder
+	# as required. For example, to set bitrate after a video has been parsed.
+	function extraProc() {
+		return
+	}
+
+	# Use the machine hostname to set the default encoding options based upon
+	# my preferences.
+	echo "Machine identification:"
+	if [[ "${OPTS_FORCESOFT}" == "true" ]]; then
+		echo "   > Hostname: $(hostname)"
+		export OPTS_ENC="allsoft"
+	else
+		if [[ "$(hostname)" == "Kusanagi" ]]; then
+			echo "   > Hostname: č‰č–™ē“ å­"
+			export OPTS_ENC="nvidia"
+			echo "FIX FFMPEG!"
+			#export FFMPEG="/opt/ffmpeg-nvenc/bin/ffmpeg"
+			export LD_LIBRARY_PATH="/opt/ffmpeg-nvenc/lib"
+			export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
+		elif [[ "$(hostname)" == "grad-heo-lappy" ]]; then
+			echo "   > Hostname: $(hostname)"
+			export OPTS_ENC="vaapi"
+		else
+			echo "   > Hostname: $(hostname)"
+			export OPTS_ENC="allsoft"
+			export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
+		fi
+	fi
+	
+	# Configure audio filtergraph if needed.
+	if [[ "${OPTS_TRANS_AUDIO}" == true ]]; then
+		FILT_AUDIO="-c:a aac"
+		if [[ "${OPTS_LPF_AUDIO}" == true ]]; then
+			FILT_AUDIO="-filter:a highpass=f=7 ${FILT_AUDIO}"
+		fi
+
+		if [[ "$(hostname)" == "Ram-the-Red" ]]; then
+			FILT_AUDIO="${FILT_AUDIO} -strict -2"
+		fi
+	else
+		FILT_AUDIO="-c:a copy"
+	fi
+
+	# Configure the encoder based upon the specific encoder chain required
+	## NVIDIA GPU Encode/Decode
+	if [[ "$OPTS_ENC" == "nvidia" ]]; then
+		echo "   > Using nVidia Encoder"
+		echo "   >>> full hardware"
+		# NVIDIA GPU encoding options
+		export FF_ENC="h264_nvenc"
+		# Assuming h264 input video
+		# TODO: Handle non h264 input streams
+		#export FF_HW="-hwaccel cuvid -c:v h264_cuvid"
+		#export FF_HW="-hwaccel cuvid"
+		# Frame count lookahead
+		export FF_EXT="${FF_EXT} -rc-lookahead 30"
+
+	## Intel CPU Encode/Decode
+	elif [[  "$OPTS_ENC" == "vaapi" ]]; then
+		echo "   > Using VAAPI."
+		# apt install libvdpau-va-gl1
+		#export FF_BIN="/opt/ffmpeg/bin/ffmpeg"
+
+		# Full hardware
+		if [[ "${OPTS_FORCEPARTSOFT}" == "false" ]]; then
+			echo "   >>> full hardware"
+			# Full hardware decode/encode
+			export FF_HW="-hwaccel vaapi -hwaccel_device \
+				/dev/dri/renderD128 -hwaccel_output_format vaapi"
+			export FF_ENC="h264_vaapi"
+			export FILT_PFX="scale_vaapi,hwmap=mode=read+write+direct,format=nv12,"
+			export FILT_SFX=",hwmap"
+			# Override the extra options used for this encoder
+			export FF_EXT="-profile:v 100 -level 40"
+		# Mixed software hardware
+		else
+			echo "   >>> hardware decode"
+			echo "     > software encode"
+			# software encode, hardware decode only
+			export FF_HW="-hwaccel vaapi -hwaccel_device /dev/dri/renderD128"
+		fi
+		
+		# Disable this shit
+		# set the bitrate based upon the old video size.
+		if [[ $(false) ]]; then
+			function extraProc() {
+				echo -n "   > determine video size: "
+				LEN_COMPLEX=$("$JQ" '.streams[0].tags.DURATION' \
+					"${STREAMS_ALL}" | tr -d '"')
+				LEN_SECONDS=$("$DATE" -u +'%s' -d "1970-01-01 $LEN_COMPLEX")
+				# shellcheck disable=SC2016
+				VIDEO_SIZE_KBYTES=$("$FFPROBE"  -select_streams v -show_entries \
+					packet=size -of default=nokey=1:noprint_wrappers=1 \
+					-i "${INPUT_VIDEO}" | "$AWK" '{s+=$1} END {print s/1024.0}')
+				KBITRATE=$("$PYTHON3" -c \
+					"print('%.0f' % (1.0*8.0*$VIDEO_SIZE_KBYTES/$LEN_SECONDS))")
+				export FF_EXT="-b:v ${KBITRATE}K "
+			}
+		fi
+	# Full Software encoding/decoding. No hardware assistance.
+	else
+		echo "   > Using default full software chain."
+	fi
+	sleep 1
+	echo "====> Starting <===="
+}
+
+function setupBins() {
+	# The first argument is the "default" binary name, and the second
+	# is the variable where that binary's path is stored. Using *export* to
+	# override the variable before "setupBins" is called will let you specify
+	# a specific instance of the binary to use instead.
+	setAndValidateBin "ffmpeg" "FFMPEG"
+	setAndValidateBin "ffprobe" "FFPROBE"
+	setAndValidateBin "jq" "JQ"
+	setAndValidateBin "python3" "PYTHON3"
+	setAndValidateBin "awk" "AWK"
+	setAndValidateBin "date" "DATE"
+}
+
+# Used by the above function to evaluate overrides if they are set, and
+# otherwise to simply return the existing binary as an absolute path.
+function setAndValidateBin() {
+	binaryVariable="$2"
+	# shellcheck disable=SC2086
+	eval binaryCurrentValue="\${$binaryVariable:-}"
+	if [[ "" == "$binaryCurrentValue" ]]; then
+		binaryCurrentValue="$1"
+	fi
+
+	# Try to extract the results of the current binary as an absolute path
+	binaryCurrentValue=$(readlink -f "$(which "$binaryCurrentValue")") || /bin/true
+	# And fail if we don't have it
+	if [[ ! -x "$binaryCurrentValue" ]]; then
+		echo "ERROR: Required binary '$1' not found."
+		exit
+	fi
+	
+	# shellcheck disable=SC2086
+	eval export $binaryVariable="$binaryCurrentValue"
+}
+
+# The fall back cleanup function to remove the shit in temp. Set to trap in
+# the setup temp function further on.
+function doCleanup() {
+	# On eject return to where we started, and frag the cleanup directory
+	cd "$WD"
+	if [[ "$OPTS_DEBUG" == "false" ]]; then
+		echo "=> Cleaning up."
+		rm -r "$TMP"
+	else
+		echo "tmp dir: $TMP"
+	fi
+	
+	alertUser
+}
+
+# All we do is note that we'll have to move around, and this helps us keep our
+# bearings and avoid polluting the working directory.
+function setupTemp() {
+	echo "=> Performing Setup:"
+	# The FFMPEG calls require all of the files to actually exist on disk. To
+	# accommodate this, we setup a temporary working directory to dump the files
+	# and we will remove it's contents when things are done. This provides a
+	# location for the font files to be placed, as well as a location for the
+	# raw subtitles files.
+	trap doCleanup EXIT
+
+	export WD="$PWD"
+	export TMP=$(mktemp -d /tmp/transFont.XXXX)
+}
+
+function dumpFonts() {
+	# Dumb font dump
+	cd "$FONTDIR"
+	"$FFMPEG" -dump_attachment:t "" -i "${INPUT_VIDEO}" -vn -an \
+		-f null /dev/null 2>/dev/null || /bin/true
+	cd "$WD"
+}
+
+function setupFonts() {
+	echo "   > setting up fonts..."
+	# Simply setup an encode specific font configuration setup. This will direct
+	# the system to use any font files that we have provided in $TMP/font_files
+	# then fall back to the system configuration afterwards.
+	export FONTDIR="${TMP}/font_files"
+	FC_DIR="${TMP}/fontconfig"
+	FC_FILE="${FC_DIR}/fonts.conf"
+	mkdir "${FONTDIR}"
+	mkdir "${FC_DIR}"
+	{
+		echo '<?xml version="1.0"?>';
+		echo '<!DOCTYPE fontconfig SYSTEM "fonts.dtd">';
+		echo '<fontconfig>';
+		echo '	<dir>'"${FONTDIR}"'</dir>';
+		echo '	<include>/etc/fonts/fonts.conf</include>';
+		echo '</fontconfig>'
+	} > "${FC_FILE}"
+	# This export line presents the configuration override to FFMPEG later on.
+	export FONTCONFIG_FILE="${FC_FILE}"
+	
+	dumpFonts
+}
+
+function setupPreset() {
+	echo "   > setting up preset..."
+	# The preset system provides a way to specify specific encoding options.
+	# This can easily be removed as desired.
+	presetName="$1"
+	# And include the ffmpeg preset
+	export FFMPEG_DATADIR="${TMP}/ffmpeg"
+	FFMPEG_PRESET_FILE="${FFMPEG_DATADIR}/${FF_ENC}-${presetName}.preset"
+
+	mkdir "${FFMPEG_DATADIR}"
+	{	
+		# This preset is for the roku
+		echo 'coder=1'
+		echo 'flags=+loop'
+		echo 'cmp=+chroma'
+		echo 'partitions=+parti8x8+parti4x4+partp8x8+partb8x8'
+		echo 'me_method=umh'
+		echo 'subq=8'
+		echo 'me_range=16'
+		echo 'g=250'
+		echo 'keyint_min=25'
+		echo 'sc_threshold=40'
+		echo 'i_qfactor=0.71'
+		echo 'b_strategy=2'
+		echo 'qcomp=0.6'
+		echo 'qmin=10'
+		echo 'qmax=51'
+		echo 'qdiff=4'
+		echo 'bf=4'
+		echo 'refs=4'
+		echo 'directpred=3'
+		echo 'trellis=1'
+		echo 'flags2=+wpred+mixed_refs+dct8x8+fastpskip'
+	} > "${FFMPEG_PRESET_FILE}"
+}
+
+function parseStreams() {
+	# Most MKV files will only have a single subtitle file. In the case we have
+	# multiple subtitles we wish to handle conversion gracefully. To do so, we
+	# extract all of the track information (the first ffprobe call), then use
+	# the json parsing tool jq to extract subtitle data for styled subtitles.
+	#
+	# 
+	STREAMS_ALL="${TMP}/streams.json"
+	export STREAMS_SUB="${TMP}/subs.json"
+	"$FFPROBE" -v error -of json -show_streams "${INPUT_VIDEO}" 2>/dev/null > "${STREAMS_ALL}"
+	
+	# Extract subtitles
+	#$item.codec_type == "subtitle" \&\& 
+	# shellcheck disable=SC2016
+	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
+	export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
+	
+}
+
+function listSubtitles() {
+	# TODO: 
+	return
+}
+
+function selectSubs() {
+	# TODO: handle multiple subtitle files
+	# TODO: verify the the subtitle index is legal
+	if [[ $SUB_COUNT -eq 0 ]]; then
+		echo "   > ERROR: No subtitles! Todo!"
+		export SUBTITLE_INDEX=-1
+	elif [[ $SUB_COUNT -eq 1 ]]; then
+		export SUBTITLE_INDEX=$($JQ '.[].i' "$STREAMS_SUB")
+		SUBTITLE_NAME=$($JQ '.[].t' "$STREAMS_SUB")
+		echo "   > subtitles: [${SUBTITLE_INDEX}] ${SUBTITLE_NAME}"
+	else
+		echo "   > ERROR: Multiple subtitles! Todo!"
+		export SUBTITLE_INDEX=-1
+	fi
+}
+
+
+function extractSubs() {
+	echo "   > extracting subtitles"
+	export SUBTITLE_FILE="${TMP}/ripped.ass"
+	extractIndex="$1"
+	"$FFMPEG" -i "${INPUT_VIDEO}" -map 0:"${extractIndex}" -vn -an -c:s copy -c:a copy \
+		"$SUBTITLE_FILE" 2>/dev/null
+
+}
+
+function doTranscode() {
+	echo "=> Starting transcode:"
+	# shellcheck disable=SC2086
+	echo "$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
+		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
+		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT}	\
+		"${OUTPUT_VIDEO}"
+	if [[ "$OPTS_DRYRUN" == true ]]; then
+		return
+	fi
+	# shellcheck disable=SC2086
+	"$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
+		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
+		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT}	\
+		"${OUTPUT_VIDEO}"
+	export FINAL_STATUS=$?
+}
+
+function runExtraProc() {
+	if [[ "$FF_ENC" == "$1" ]]; then
+		extraProc
+	fi
+}
+
+
+function klobberCheck() {
+	if [[ -e "${OUTPUT_VIDEO}" ]]; then
+		if [[ "$OPTS_KLOBBER" == "true" ]]; then
+			echo "   > klobbering old file '${OUTPUT_VIDEO}'"
+			rm -v "${OUTPUT_VIDEO}"
+		else
+			echo "   > refusing to klobber old file '${OUTPUT_VIDEO}'"
+			exit 1
+		fi
+	fi
+}
+
+function alertUser() {
+	# if the function isn't available, then define an empty function to
+	# reject the message into the void
+	if ! which notify-send >/dev/null; then
+		function notify-send() { return; }
+	fi
+	
+	if [[ $FINAL_STATUS -ne 0 ]]; then
+		notify-send -u normal --icon error "šŸ—™ Transcode failed!" \
+			"$(basename "${INPUT_VIDEO}")\nšŸž©šŸž©šŸž©šŸž©šŸž©\n$(basename "${OUTPUT_VIDEO}")"
+	else
+		notify-send -u normal --icon ${NOTIFY_ICON} "āœ” Transcode finished" \
+			"$(basename "${INPUT_VIDEO}")\n↓↓↓↓↓\n$(basename "${OUTPUT_VIDEO}")"
+	fi
+}
+
+
+# And verify arguments
+######
+# Defaults
+OPTS_KLOBBER=false
+OPTS_LISTSUBS=false
+OPTS_SELSUB=-1
+OPTS_LISTSUBS=false
+OPTS_FORCESOFT=false
+OPTS_FORCEPARTSOFT=false
+OPTS_ENC="allsoft"
+OPTS_TIMELIMIT=null
+OPTS_DRYRUN=false
+OPTS_DEBUG=false
+OPTS_LPF_AUDIO=false
+OPTS_TRANS_AUDIO=false
+unset OPT_CRF
+# this is the --icon flag passed to notify-send at the end of the transcode
+NOTIFY_ICON="face-tired"
+# preinitalized final ffmpeg status to assumed error
+FINAL_STATUS=1
+
+### Argument Parsing
+#	Input/Output videos (non-flagged arguments)
+#	optional:
+#		-k		do clobber output
+#		-s #	subtitle track
+#		-l		list subtitle tracks of input files		
+#		--soft	force software encoder
+#		-d		debug, don't cleanup
+###
+
+
+######
+# Reformat and organize the input strings
+OPT_STRING=$(getopt -o 'hkls:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix' -- "$@")
+# reassign them as positional arguments
+eval set -- "$OPT_STRING"
+
+while true; do
+	case "$1" in
+		"-t")
+			OPTS_TIMELIMIT="$2"
+			echo ">> !! limiting encode to time '$OPTS_TIMELIMIT'"
+			shift 2
+			continue
+		;;
+		"-k")
+			OPTS_KLOBBER=true
+			echo ">> !! klobber when encoding time comes."
+			shift
+			continue
+		;;
+		"-l")
+			OPTS_LISTSUBS=true
+			echo ">> !! list subtitles and exit"
+			shift
+			continue
+		;;
+		"-s")
+			OPTS_SELSUB="$2"
+			#TODO: verify legal subtitle track number convention"
+			echo "TODO: verify legal subtitle track number convention" 
+			echo ">> !! Selecting subtitle track #${OPTS_SELSUB}"
+			shift 2
+			continue
+		;;
+		"--psoft")
+			OPTS_FORCEPARTSOFT=true
+			echo ">> !! forcing software encoding."
+			shift
+			continue
+		;;
+		"--audio")
+			OPTS_LPF_AUDIO=true
+			OPTS_TRANS_AUDIO=true
+			echo ">> !! low pass filter audio to AAC."
+			shift
+			continue
+		;;
+		"--audiofix")
+			OPTS_TRANS_AUDIO=true
+			echo ">> !! no filter audio to AAC."
+			shift
+			continue
+		;;
+		"--soft")
+			OPTS_FORCESOFT=true
+			echo ">> !! forcing software decoding/encoding."
+			shift
+			continue
+		;;
+		"--crf")
+			OPTS_CRF="$2"
+			echo ">> !! CRF Override CRF='$OPTS_CRF'"
+			shift 2
+			continue
+		;;
+		"-d")
+			OPTS_DEBUG=true
+			echo ">> !! debug enabled."
+			echo ">>      temp files will not be cleaned up."
+			shift
+			continue
+		;;
+		"--dry")
+			OPTS_DRYRUN=true
+			echo ">> !! dry run. No encode."
+			shift
+			continue
+		;;
+		"--")
+			shift # all arguments parsed
+			break
+		;;
+		"-h" | "--help")
+			# TODO: Display help
+			shift # all arguments parsed
+			echo "TODO: HELP!" # Display HELP
+			exit
+		;;
+		*)
+			echo "Arg: $1"
+			echo "Internal Error!" >&2
+			exit
+		;;
+	esac
+done
+
+# Now parse POSITIONAL ARGUMENTS
+if [[ $# -ne 2 ]]; then
+	echo "ERROR: Incorrect number of positional arguments. Expected 2, got $#"
+	echo "       $0 [args] <input> <output>"
+	exit
+else
+	INPUT_VIDEO="$(readlink -f "$1")"
+	OUTPUT_VIDEO="$2"
+fi
+
+###############
+# Configure the encoder based upon the hostname
+machineSetup
+
+###############
+# Check and validate the binaries we use to parse and setup the encoding chain
+setupBins
+# Setup the temp space for working files
+setupTemp
+# Extract embedded fonts and configure fontconfig
+setupFonts
+# Export the preset file used for the Roku
+setupPreset myTranscoderPreset
+
+###############
+# Parse stream data to identify subtitles
+parseStreams
+# Now! If OPTS_LISTSUBS is defined, then we branch to list subs and exit.
+if [[ "$OPTS_LISTSUBS" == "true" ]]; then
+	listSubtitles
+	exit	
+fi
+# ask the user for the subtitle file if more than one is available
+selectSubs
+# extract the selected subtitle file
+extractSubs $SUBTITLE_INDEX
+# Set the bitrate if that function wasn't disabled
+runExtraProc "h264_vaapi"
+
+# If we're clobbering, now is the time to do the clobbering
+klobberCheck
+# Kickoff the transcode
+doTranscode
+
+# cleanup automatically executes after the done message is cleared
+echo "done."

From bd87bacacbfaa7986cb184e52f4439a7783fec94 Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Thu, 7 Nov 2019 07:42:43 -0800
Subject: [PATCH 02/29] v0.9

---
 burnSubs | 553 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 553 insertions(+)
 create mode 100755 burnSubs

diff --git a/burnSubs b/burnSubs
new file mode 100755
index 0000000..589f85e
--- /dev/null
+++ b/burnSubs
@@ -0,0 +1,553 @@
+#!/bin/bash
+set -e
+set -o nounset
+set -o errexit
+
+################################################################################
+# burnSubs
+# version 0.9.1
+#################3
+# Wishlist:
+#   queue encodes
+#   finish TODOs
+#	list subtitles flag
+#	finish help flag
+#	audio recode flag
+################################################################################
+
+function machineSetup() {
+	# Default setup
+	export FF_ENC="libx264"
+	export FF_HW=""
+	export FILT_PFX=""
+	export FILT_SFX=""
+	CRF=${OPTS_CRF:-23}
+	export FF_STD="-preset myTranscoderPreset -crf $CRF -tune animation \
+		-preset medium -movflags +faststart"
+	export FF_EXT="-profile:v high -level 4.0"
+	# Handle the time limit argument used for testing if it was passed
+	if [[ "${OPTS_TIMELIMIT}" != "null" ]]; then
+		export LIM_TIME="-t ${OPTS_TIMELIMIT}"
+	else
+		export LIM_TIME=""
+	fi
+
+	# The default extraProc function. This should be overridden by each encoder
+	# as required. For example, to set bitrate after a video has been parsed.
+	function extraProc() {
+		return
+	}
+
+	# Use the machine hostname to set the default encoding options based upon
+	# my preferences.
+	echo "Machine identification:"
+	if [[ "${OPTS_FORCESOFT}" == "true" ]]; then
+		echo "   > Hostname: $(hostname)"
+		export OPTS_ENC="allsoft"
+	else
+		if [[ "$(hostname)" == "Kusanagi" ]]; then
+			echo "   > Hostname: č‰č–™ē“ å­"
+			export OPTS_ENC="nvidia"
+			echo "FIX FFMPEG!"
+			#export FFMPEG="/opt/ffmpeg-nvenc/bin/ffmpeg"
+			export LD_LIBRARY_PATH="/opt/ffmpeg-nvenc/lib"
+			export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
+		elif [[ "$(hostname)" == "grad-heo-lappy" ]]; then
+			echo "   > Hostname: $(hostname)"
+			export OPTS_ENC="vaapi"
+		else
+			echo "   > Hostname: $(hostname)"
+			export OPTS_ENC="allsoft"
+			export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
+		fi
+	fi
+	
+	# Configure audio filtergraph if needed.
+	if [[ "${OPTS_TRANS_AUDIO}" == true ]]; then
+		FILT_AUDIO="-c:a aac"
+		if [[ "${OPTS_LPF_AUDIO}" == true ]]; then
+			FILT_AUDIO="-filter:a highpass=f=7 ${FILT_AUDIO}"
+		fi
+
+		if [[ "$(hostname)" == "Ram-the-Red" ]]; then
+			FILT_AUDIO="${FILT_AUDIO} -strict -2"
+		fi
+	else
+		FILT_AUDIO="-c:a copy"
+	fi
+
+	# Configure the encoder based upon the specific encoder chain required
+	## NVIDIA GPU Encode/Decode
+	if [[ "$OPTS_ENC" == "nvidia" ]]; then
+		echo "   > Using nVidia Encoder"
+		echo "   >>> full hardware"
+		# NVIDIA GPU encoding options
+		export FF_ENC="h264_nvenc"
+		# Assuming h264 input video
+		# TODO: Handle non h264 input streams
+		#export FF_HW="-hwaccel cuvid -c:v h264_cuvid"
+		#export FF_HW="-hwaccel cuvid"
+		# Frame count lookahead
+		export FF_EXT="${FF_EXT} -rc-lookahead 30"
+
+	## Intel CPU Encode/Decode
+	elif [[  "$OPTS_ENC" == "vaapi" ]]; then
+		echo "   > Using VAAPI."
+		# apt install libvdpau-va-gl1
+		#export FF_BIN="/opt/ffmpeg/bin/ffmpeg"
+
+		# Full hardware
+		if [[ "${OPTS_FORCEPARTSOFT}" == "false" ]]; then
+			echo "   >>> full hardware"
+			# Full hardware decode/encode
+			export FF_HW="-hwaccel vaapi -hwaccel_device \
+				/dev/dri/renderD128 -hwaccel_output_format vaapi"
+			export FF_ENC="h264_vaapi"
+			export FILT_PFX="scale_vaapi,hwmap=mode=read+write+direct,format=nv12,"
+			export FILT_SFX=",hwmap"
+			# Override the extra options used for this encoder
+			export FF_EXT="-profile:v 100 -level 40"
+		# Mixed software hardware
+		else
+			echo "   >>> hardware decode"
+			echo "     > software encode"
+			# software encode, hardware decode only
+			export FF_HW="-hwaccel vaapi -hwaccel_device /dev/dri/renderD128"
+		fi
+		
+		# Disable this shit
+		# set the bitrate based upon the old video size.
+		if [[ $(false) ]]; then
+			function extraProc() {
+				echo -n "   > determine video size: "
+				LEN_COMPLEX=$("$JQ" '.streams[0].tags.DURATION' \
+					"${STREAMS_ALL}" | tr -d '"')
+				LEN_SECONDS=$("$DATE" -u +'%s' -d "1970-01-01 $LEN_COMPLEX")
+				# shellcheck disable=SC2016
+				VIDEO_SIZE_KBYTES=$("$FFPROBE"  -select_streams v -show_entries \
+					packet=size -of default=nokey=1:noprint_wrappers=1 \
+					-i "${INPUT_VIDEO}" | "$AWK" '{s+=$1} END {print s/1024.0}')
+				KBITRATE=$("$PYTHON3" -c \
+					"print('%.0f' % (1.0*8.0*$VIDEO_SIZE_KBYTES/$LEN_SECONDS))")
+				export FF_EXT="-b:v ${KBITRATE}K "
+			}
+		fi
+	# Full Software encoding/decoding. No hardware assistance.
+	else
+		echo "   > Using default full software chain."
+	fi
+	sleep 1
+	echo "====> Starting <===="
+}
+
+function setupBins() {
+	# The first argument is the "default" binary name, and the second
+	# is the variable where that binary's path is stored. Using *export* to
+	# override the variable before "setupBins" is called will let you specify
+	# a specific instance of the binary to use instead.
+	setAndValidateBin "ffmpeg" "FFMPEG"
+	setAndValidateBin "ffprobe" "FFPROBE"
+	setAndValidateBin "jq" "JQ"
+	setAndValidateBin "python3" "PYTHON3"
+	setAndValidateBin "awk" "AWK"
+	setAndValidateBin "date" "DATE"
+}
+
+# Used by the above function to evaluate overrides if they are set, and
+# otherwise to simply return the existing binary as an absolute path.
+function setAndValidateBin() {
+	binaryVariable="$2"
+	# shellcheck disable=SC2086
+	eval binaryCurrentValue="\${$binaryVariable:-}"
+	if [[ "" == "$binaryCurrentValue" ]]; then
+		binaryCurrentValue="$1"
+	fi
+
+	# Try to extract the results of the current binary as an absolute path
+	binaryCurrentValue=$(readlink -f "$(which "$binaryCurrentValue")") || /bin/true
+	# And fail if we don't have it
+	if [[ ! -x "$binaryCurrentValue" ]]; then
+		echo "ERROR: Required binary '$1' not found."
+		exit
+	fi
+	
+	# shellcheck disable=SC2086
+	eval export $binaryVariable="$binaryCurrentValue"
+}
+
+# The fall back cleanup function to remove the shit in temp. Set to trap in
+# the setup temp function further on.
+function doCleanup() {
+	# On eject return to where we started, and frag the cleanup directory
+	cd "$WD"
+	if [[ "$OPTS_DEBUG" == "false" ]]; then
+		echo "=> Cleaning up."
+		rm -r "$TMP"
+	else
+		echo "tmp dir: $TMP"
+	fi
+	
+	alertUser
+}
+
+# All we do is note that we'll have to move around, and this helps us keep our
+# bearings and avoid polluting the working directory.
+function setupTemp() {
+	echo "=> Performing Setup:"
+	# The FFMPEG calls require all of the files to actually exist on disk. To
+	# accommodate this, we setup a temporary working directory to dump the files
+	# and we will remove it's contents when things are done. This provides a
+	# location for the font files to be placed, as well as a location for the
+	# raw subtitles files.
+	trap doCleanup EXIT
+
+	export WD="$PWD"
+	export TMP=$(mktemp -d /tmp/transFont.XXXX)
+}
+
+function dumpFonts() {
+	# Dumb font dump
+	cd "$FONTDIR"
+	"$FFMPEG" -dump_attachment:t "" -i "${INPUT_VIDEO}" -vn -an \
+		-f null /dev/null 2>/dev/null || /bin/true
+	cd "$WD"
+}
+
+function setupFonts() {
+	echo "   > setting up fonts..."
+	# Simply setup an encode specific font configuration setup. This will direct
+	# the system to use any font files that we have provided in $TMP/font_files
+	# then fall back to the system configuration afterwards.
+	export FONTDIR="${TMP}/font_files"
+	FC_DIR="${TMP}/fontconfig"
+	FC_FILE="${FC_DIR}/fonts.conf"
+	mkdir "${FONTDIR}"
+	mkdir "${FC_DIR}"
+	{
+		echo '<?xml version="1.0"?>';
+		echo '<!DOCTYPE fontconfig SYSTEM "fonts.dtd">';
+		echo '<fontconfig>';
+		echo '	<dir>'"${FONTDIR}"'</dir>';
+		echo '	<include>/etc/fonts/fonts.conf</include>';
+		echo '</fontconfig>'
+	} > "${FC_FILE}"
+	# This export line presents the configuration override to FFMPEG later on.
+	export FONTCONFIG_FILE="${FC_FILE}"
+	
+	dumpFonts
+}
+
+function setupPreset() {
+	echo "   > setting up preset..."
+	# The preset system provides a way to specify specific encoding options.
+	# This can easily be removed as desired.
+	presetName="$1"
+	# And include the ffmpeg preset
+	export FFMPEG_DATADIR="${TMP}/ffmpeg"
+	FFMPEG_PRESET_FILE="${FFMPEG_DATADIR}/${FF_ENC}-${presetName}.preset"
+
+	mkdir "${FFMPEG_DATADIR}"
+	{	
+		# This preset is for the roku
+		echo 'coder=1'
+		echo 'flags=+loop'
+		echo 'cmp=+chroma'
+		echo 'partitions=+parti8x8+parti4x4+partp8x8+partb8x8'
+		echo 'me_method=umh'
+		echo 'subq=8'
+		echo 'me_range=16'
+		echo 'g=250'
+		echo 'keyint_min=25'
+		echo 'sc_threshold=40'
+		echo 'i_qfactor=0.71'
+		echo 'b_strategy=2'
+		echo 'qcomp=0.6'
+		echo 'qmin=10'
+		echo 'qmax=51'
+		echo 'qdiff=4'
+		echo 'bf=4'
+		echo 'refs=4'
+		echo 'directpred=3'
+		echo 'trellis=1'
+		echo 'flags2=+wpred+mixed_refs+dct8x8+fastpskip'
+	} > "${FFMPEG_PRESET_FILE}"
+}
+
+function parseStreams() {
+	# Most MKV files will only have a single subtitle file. In the case we have
+	# multiple subtitles we wish to handle conversion gracefully. To do so, we
+	# extract all of the track information (the first ffprobe call), then use
+	# the json parsing tool jq to extract subtitle data for styled subtitles.
+	#
+	# 
+	STREAMS_ALL="${TMP}/streams.json"
+	export STREAMS_SUB="${TMP}/subs.json"
+	"$FFPROBE" -v error -of json -show_streams "${INPUT_VIDEO}" 2>/dev/null > "${STREAMS_ALL}"
+	
+	# Extract subtitles
+	#$item.codec_type == "subtitle" \&\& 
+	# shellcheck disable=SC2016
+	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
+	export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
+	
+}
+
+function listSubtitles() {
+	# TODO: 
+	return
+}
+
+function selectSubs() {
+	# TODO: handle multiple subtitle files
+	# TODO: verify the the subtitle index is legal
+	if [[ $SUB_COUNT -eq 0 ]]; then
+		echo "   > ERROR: No subtitles! Todo!"
+		export SUBTITLE_INDEX=-1
+	elif [[ $SUB_COUNT -eq 1 ]]; then
+		export SUBTITLE_INDEX=$($JQ '.[].i' "$STREAMS_SUB")
+		SUBTITLE_NAME=$($JQ '.[].t' "$STREAMS_SUB")
+		echo "   > subtitles: [${SUBTITLE_INDEX}] ${SUBTITLE_NAME}"
+	else
+		echo "   > ERROR: Multiple subtitles! Todo!"
+		export SUBTITLE_INDEX=-1
+	fi
+}
+
+
+function extractSubs() {
+	echo "   > extracting subtitles"
+	export SUBTITLE_FILE="${TMP}/ripped.ass"
+	extractIndex="$1"
+	"$FFMPEG" -i "${INPUT_VIDEO}" -map 0:"${extractIndex}" -vn -an -c:s copy -c:a copy \
+		"$SUBTITLE_FILE" 2>/dev/null
+
+}
+
+function doTranscode() {
+	echo "=> Starting transcode:"
+	# shellcheck disable=SC2086
+	echo "$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
+		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
+		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT}	\
+		"${OUTPUT_VIDEO}"
+	if [[ "$OPTS_DRYRUN" == true ]]; then
+		return
+	fi
+	# shellcheck disable=SC2086
+	"$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
+		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
+		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT}	\
+		"${OUTPUT_VIDEO}"
+	export FINAL_STATUS=$?
+}
+
+function runExtraProc() {
+	if [[ "$FF_ENC" == "$1" ]]; then
+		extraProc
+	fi
+}
+
+
+function klobberCheck() {
+	if [[ -e "${OUTPUT_VIDEO}" ]]; then
+		if [[ "$OPTS_KLOBBER" == "true" ]]; then
+			echo "   > klobbering old file '${OUTPUT_VIDEO}'"
+			rm -v "${OUTPUT_VIDEO}"
+		else
+			echo "   > refusing to klobber old file '${OUTPUT_VIDEO}'"
+			exit 1
+		fi
+	fi
+}
+
+function alertUser() {
+	# if the function isn't available, then define an empty function to
+	# reject the message into the void
+	if ! which notify-send >/dev/null; then
+		function notify-send() { return; }
+	fi
+	
+	if [[ $FINAL_STATUS -ne 0 ]]; then
+		notify-send -u normal --icon error "šŸ—™ Transcode failed!" \
+			"$(basename "${INPUT_VIDEO}")\nšŸž©šŸž©šŸž©šŸž©šŸž©\n$(basename "${OUTPUT_VIDEO}")"
+	else
+		notify-send -u normal --icon ${NOTIFY_ICON} "āœ” Transcode finished" \
+			"$(basename "${INPUT_VIDEO}")\n↓↓↓↓↓\n$(basename "${OUTPUT_VIDEO}")"
+	fi
+}
+
+
+# And verify arguments
+######
+# Defaults
+OPTS_KLOBBER=false
+OPTS_LISTSUBS=false
+OPTS_SELSUB=-1
+OPTS_LISTSUBS=false
+OPTS_FORCESOFT=false
+OPTS_FORCEPARTSOFT=false
+OPTS_ENC="allsoft"
+OPTS_TIMELIMIT=null
+OPTS_DRYRUN=false
+OPTS_DEBUG=false
+OPTS_LPF_AUDIO=false
+OPTS_TRANS_AUDIO=false
+unset OPT_CRF
+# this is the --icon flag passed to notify-send at the end of the transcode
+NOTIFY_ICON="face-tired"
+# preinitalized final ffmpeg status to assumed error
+FINAL_STATUS=1
+
+### Argument Parsing
+#	Input/Output videos (non-flagged arguments)
+#	optional:
+#		-k		do clobber output
+#		-s #	subtitle track
+#		-l		list subtitle tracks of input files		
+#		--soft	force software encoder
+#		-d		debug, don't cleanup
+###
+
+
+######
+# Reformat and organize the input strings
+OPT_STRING=$(getopt -o 'hkls:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix' -- "$@")
+# reassign them as positional arguments
+eval set -- "$OPT_STRING"
+
+while true; do
+	case "$1" in
+		"-t")
+			OPTS_TIMELIMIT="$2"
+			echo ">> !! limiting encode to time '$OPTS_TIMELIMIT'"
+			shift 2
+			continue
+		;;
+		"-k")
+			OPTS_KLOBBER=true
+			echo ">> !! klobber when encoding time comes."
+			shift
+			continue
+		;;
+		"-l")
+			OPTS_LISTSUBS=true
+			echo ">> !! list subtitles and exit"
+			shift
+			continue
+		;;
+		"-s")
+			OPTS_SELSUB="$2"
+			#TODO: verify legal subtitle track number convention"
+			echo "TODO: verify legal subtitle track number convention" 
+			echo ">> !! Selecting subtitle track #${OPTS_SELSUB}"
+			shift 2
+			continue
+		;;
+		"--psoft")
+			OPTS_FORCEPARTSOFT=true
+			echo ">> !! forcing software encoding."
+			shift
+			continue
+		;;
+		"--audio")
+			OPTS_LPF_AUDIO=true
+			OPTS_TRANS_AUDIO=true
+			echo ">> !! low pass filter audio to AAC."
+			shift
+			continue
+		;;
+		"--audiofix")
+			OPTS_TRANS_AUDIO=true
+			echo ">> !! no filter audio to AAC."
+			shift
+			continue
+		;;
+		"--soft")
+			OPTS_FORCESOFT=true
+			echo ">> !! forcing software decoding/encoding."
+			shift
+			continue
+		;;
+		"--crf")
+			OPTS_CRF="$2"
+			echo ">> !! CRF Override CRF='$OPTS_CRF'"
+			shift 2
+			continue
+		;;
+		"-d")
+			OPTS_DEBUG=true
+			echo ">> !! debug enabled."
+			echo ">>      temp files will not be cleaned up."
+			shift
+			continue
+		;;
+		"--dry")
+			OPTS_DRYRUN=true
+			echo ">> !! dry run. No encode."
+			shift
+			continue
+		;;
+		"--")
+			shift # all arguments parsed
+			break
+		;;
+		"-h" | "--help")
+			# TODO: Display help
+			shift # all arguments parsed
+			echo "TODO: HELP!" # Display HELP
+			exit
+		;;
+		*)
+			echo "Arg: $1"
+			echo "Internal Error!" >&2
+			exit
+		;;
+	esac
+done
+
+# Now parse POSITIONAL ARGUMENTS
+if [[ $# -ne 2 ]]; then
+	echo "ERROR: Incorrect number of positional arguments. Expected 2, got $#"
+	echo "       $0 [args] <input> <output>"
+	exit
+else
+	INPUT_VIDEO="$(readlink -f "$1")"
+	OUTPUT_VIDEO="$2"
+fi
+
+###############
+# Configure the encoder based upon the hostname
+machineSetup
+
+###############
+# Check and validate the binaries we use to parse and setup the encoding chain
+setupBins
+# Setup the temp space for working files
+setupTemp
+# Extract embedded fonts and configure fontconfig
+setupFonts
+# Export the preset file used for the Roku
+setupPreset myTranscoderPreset
+
+###############
+# Parse stream data to identify subtitles
+parseStreams
+# Now! If OPTS_LISTSUBS is defined, then we branch to list subs and exit.
+if [[ "$OPTS_LISTSUBS" == "true" ]]; then
+	listSubtitles
+	exit	
+fi
+# ask the user for the subtitle file if more than one is available
+selectSubs
+# extract the selected subtitle file
+extractSubs $SUBTITLE_INDEX
+# Set the bitrate if that function wasn't disabled
+runExtraProc "h264_vaapi"
+
+# If we're clobbering, now is the time to do the clobbering
+klobberCheck
+# Kickoff the transcode
+doTranscode
+
+# cleanup automatically executes after the done message is cleared
+echo "done."

From 1823bcab3f1f457a4f91cbeac3f373f01c487b31 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Thu, 7 Nov 2019 07:42:58 -0800
Subject: [PATCH 03/29] v0.9.1

---
 burnSubs | 40 ++++++++++++++++++++++++++++++++++------
 1 file changed, 34 insertions(+), 6 deletions(-)
 mode change 100755 => 100644 burnSubs

diff --git a/burnSubs b/burnSubs
old mode 100755
new mode 100644
index 589f85e..6b44de0
--- a/burnSubs
+++ b/burnSubs
@@ -294,6 +294,17 @@ function parseStreams() {
 
 function listSubtitles() {
 	# TODO: 
+	echo ""
+	echo "available subtitles:"
+	printf "Num: %4s %s\n" "LANG" "Subtitle Title String"
+	echo "---------------------------------"
+	for iSUB in $(seq 0 $(($SUB_COUNT-1))); do
+		X_TITLE=$($JQ '.['$iSUB'].t' $STREAMS_SUB)
+		X_INDEX=$($JQ '.['$iSUB'].i' $STREAMS_SUB)
+		X_LANG=$($JQ '.['$iSUB'].lang' $STREAMS_SUB)
+		printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE" 
+	done
+	echo ""
 	return
 }
 
@@ -303,13 +314,30 @@ function selectSubs() {
 	if [[ $SUB_COUNT -eq 0 ]]; then
 		echo "   > ERROR: No subtitles! Todo!"
 		export SUBTITLE_INDEX=-1
-	elif [[ $SUB_COUNT -eq 1 ]]; then
-		export SUBTITLE_INDEX=$($JQ '.[].i' "$STREAMS_SUB")
-		SUBTITLE_NAME=$($JQ '.[].t' "$STREAMS_SUB")
-		echo "   > subtitles: [${SUBTITLE_INDEX}] ${SUBTITLE_NAME}"
 	else
-		echo "   > ERROR: Multiple subtitles! Todo!"
-		export SUBTITLE_INDEX=-1
+		if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then
+			LANG_TEST=$($JQ '[.[].lang] | index("eng")' "$STREAMS_SUB")
+			echo "   > WARNING: Multiple subtitles!"
+			printf "       Using default selection rules... "
+			if [[ "$LANG_TEST" == "null" ]]; then
+				echo "English not found!"
+				echo "    ==> Reverting to first subtitle file."
+				export SUBTITLE_INDEX=$($JQ '.[0].i' "$STREAMS_SUB")
+			else
+				# we found english
+				echo "English found"
+				export SUBTITLE_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_SUB")
+			fi
+		else
+			if [[ $SUB_COUNT -eq 1 ]]; then
+				export SUBTITLE_INDEX=$($JQ '.[].i' "$STREAMS_SUB")
+			else
+				export SUBTITLE_INDEX=$OPTS_SELSUB
+			fi
+		fi
+		SUBTITLE_ARRAY_INDEX=$($JQ '[.[].i] | index('$SUBTITLE_INDEX')' "$STREAMS_SUB")
+		SUBTITLE_NAME=$($JQ '.['$SUBTITLE_ARRAY_INDEX'].t' "$STREAMS_SUB")
+		echo "   > subtitles: [${SUBTITLE_INDEX}] ${SUBTITLE_NAME}"
 	fi
 }
 

From 59eb1dad5ad0a9dd5cdcb57e5391da06b1d04c8e Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Thu, 7 Nov 2019 07:42:58 -0800
Subject: [PATCH 04/29] v0.9.1

---
 burnSubs | 40 ++++++++++++++++++++++++++++++++++------
 1 file changed, 34 insertions(+), 6 deletions(-)
 mode change 100755 => 100644 burnSubs

diff --git a/burnSubs b/burnSubs
old mode 100755
new mode 100644
index 589f85e..6b44de0
--- a/burnSubs
+++ b/burnSubs
@@ -294,6 +294,17 @@ function parseStreams() {
 
 function listSubtitles() {
 	# TODO: 
+	echo ""
+	echo "available subtitles:"
+	printf "Num: %4s %s\n" "LANG" "Subtitle Title String"
+	echo "---------------------------------"
+	for iSUB in $(seq 0 $(($SUB_COUNT-1))); do
+		X_TITLE=$($JQ '.['$iSUB'].t' $STREAMS_SUB)
+		X_INDEX=$($JQ '.['$iSUB'].i' $STREAMS_SUB)
+		X_LANG=$($JQ '.['$iSUB'].lang' $STREAMS_SUB)
+		printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE" 
+	done
+	echo ""
 	return
 }
 
@@ -303,13 +314,30 @@ function selectSubs() {
 	if [[ $SUB_COUNT -eq 0 ]]; then
 		echo "   > ERROR: No subtitles! Todo!"
 		export SUBTITLE_INDEX=-1
-	elif [[ $SUB_COUNT -eq 1 ]]; then
-		export SUBTITLE_INDEX=$($JQ '.[].i' "$STREAMS_SUB")
-		SUBTITLE_NAME=$($JQ '.[].t' "$STREAMS_SUB")
-		echo "   > subtitles: [${SUBTITLE_INDEX}] ${SUBTITLE_NAME}"
 	else
-		echo "   > ERROR: Multiple subtitles! Todo!"
-		export SUBTITLE_INDEX=-1
+		if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then
+			LANG_TEST=$($JQ '[.[].lang] | index("eng")' "$STREAMS_SUB")
+			echo "   > WARNING: Multiple subtitles!"
+			printf "       Using default selection rules... "
+			if [[ "$LANG_TEST" == "null" ]]; then
+				echo "English not found!"
+				echo "    ==> Reverting to first subtitle file."
+				export SUBTITLE_INDEX=$($JQ '.[0].i' "$STREAMS_SUB")
+			else
+				# we found english
+				echo "English found"
+				export SUBTITLE_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_SUB")
+			fi
+		else
+			if [[ $SUB_COUNT -eq 1 ]]; then
+				export SUBTITLE_INDEX=$($JQ '.[].i' "$STREAMS_SUB")
+			else
+				export SUBTITLE_INDEX=$OPTS_SELSUB
+			fi
+		fi
+		SUBTITLE_ARRAY_INDEX=$($JQ '[.[].i] | index('$SUBTITLE_INDEX')' "$STREAMS_SUB")
+		SUBTITLE_NAME=$($JQ '.['$SUBTITLE_ARRAY_INDEX'].t' "$STREAMS_SUB")
+		echo "   > subtitles: [${SUBTITLE_INDEX}] ${SUBTITLE_NAME}"
 	fi
 }
 

From e5b5ea6f808acea18e05a3b3f0c2fe7568b7e9d4 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Thu, 7 Nov 2019 07:43:25 -0800
Subject: [PATCH 05/29] v0.9.2

---
 burnSubs | 39 ++++++++++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 7 deletions(-)
 mode change 100644 => 100755 burnSubs

diff --git a/burnSubs b/burnSubs
old mode 100644
new mode 100755
index 6b44de0..f019935
--- a/burnSubs
+++ b/burnSubs
@@ -5,12 +5,11 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.9.1
+# version 0.9.2
 #################3
 # Wishlist:
 #   queue encodes
 #   finish TODOs
-#	list subtitles flag
 #	finish help flag
 #	audio recode flag
 ################################################################################
@@ -48,7 +47,7 @@ function machineSetup() {
 		if [[ "$(hostname)" == "Kusanagi" ]]; then
 			echo "   > Hostname: č‰č–™ē“ å­"
 			export OPTS_ENC="nvidia"
-			echo "FIX FFMPEG!"
+			echo "---> FIX FFMPEG!"
 			#export FFMPEG="/opt/ffmpeg-nvenc/bin/ffmpeg"
 			export LD_LIBRARY_PATH="/opt/ffmpeg-nvenc/lib"
 			export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
@@ -312,7 +311,9 @@ function selectSubs() {
 	# TODO: handle multiple subtitle files
 	# TODO: verify the the subtitle index is legal
 	if [[ $SUB_COUNT -eq 0 ]]; then
-		echo "   > ERROR: No subtitles! Todo!"
+		echo "   > ERROR: No subtitles!"
+		echo "      > Reverting to a dry run."
+		export OPTS_DRYRUN=true
 		export SUBTITLE_INDEX=-1
 	else
 		if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then
@@ -343,8 +344,13 @@ function selectSubs() {
 
 
 function extractSubs() {
-	echo "   > extracting subtitles"
 	export SUBTITLE_FILE="${TMP}/ripped.ass"
+	if [[ $SUBTITLE_INDEX -lt 0 ]]; then
+		echo "   > skipping subtitles (TODO: tidy cleanup)"
+		return
+	else
+		echo "   > extracting subtitles"
+	fi
 	extractIndex="$1"
 	"$FFMPEG" -i "${INPUT_VIDEO}" -map 0:"${extractIndex}" -vn -an -c:s copy -c:a copy \
 		"$SUBTITLE_FILE" 2>/dev/null
@@ -522,7 +528,26 @@ while true; do
 		"-h" | "--help")
 			# TODO: Display help
 			shift # all arguments parsed
-			echo "TODO: HELP!" # Display HELP
+			echo "$(basename $0) [args] <input> <output>"
+			#echo "TODO: HELP!" # Display HELP
+			cat << _EOT
+  ----------------------------------------------------------------------------
+    -k          auto-klobber when ffmpeg asks
+
+    -t <t>      ffmpeg encoding time limit
+    --crf <#>   override CRF setting
+    --soft      force software decode and encode
+    --psoft     use software encoding (allow hardware decode when available)
+    --audiofix  transcode audio
+    --audio     transcode audio, and low-pass filter as well
+
+    -l          list subtitles (no encoding)
+    -s <#>      select specific subtitle track number
+                #TODO: verify legal subtitle track number convention
+
+    -d          debug (no cleanup)
+    --dry       dry run (no encoding)
+_EOT
 			exit
 		;;
 		*)
@@ -536,7 +561,7 @@ done
 # Now parse POSITIONAL ARGUMENTS
 if [[ $# -ne 2 ]]; then
 	echo "ERROR: Incorrect number of positional arguments. Expected 2, got $#"
-	echo "       $0 [args] <input> <output>"
+	echo "       $(basename $0) [args] <input> <output>"
 	exit
 else
 	INPUT_VIDEO="$(readlink -f "$1")"

From 91e6fa045139eb9cc8c9158f4c9346e3f1f882f0 Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Thu, 7 Nov 2019 07:43:25 -0800
Subject: [PATCH 06/29] v0.9.2

---
 burnSubs | 39 ++++++++++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 7 deletions(-)
 mode change 100644 => 100755 burnSubs

diff --git a/burnSubs b/burnSubs
old mode 100644
new mode 100755
index 6b44de0..f019935
--- a/burnSubs
+++ b/burnSubs
@@ -5,12 +5,11 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.9.1
+# version 0.9.2
 #################3
 # Wishlist:
 #   queue encodes
 #   finish TODOs
-#	list subtitles flag
 #	finish help flag
 #	audio recode flag
 ################################################################################
@@ -48,7 +47,7 @@ function machineSetup() {
 		if [[ "$(hostname)" == "Kusanagi" ]]; then
 			echo "   > Hostname: č‰č–™ē“ å­"
 			export OPTS_ENC="nvidia"
-			echo "FIX FFMPEG!"
+			echo "---> FIX FFMPEG!"
 			#export FFMPEG="/opt/ffmpeg-nvenc/bin/ffmpeg"
 			export LD_LIBRARY_PATH="/opt/ffmpeg-nvenc/lib"
 			export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
@@ -312,7 +311,9 @@ function selectSubs() {
 	# TODO: handle multiple subtitle files
 	# TODO: verify the the subtitle index is legal
 	if [[ $SUB_COUNT -eq 0 ]]; then
-		echo "   > ERROR: No subtitles! Todo!"
+		echo "   > ERROR: No subtitles!"
+		echo "      > Reverting to a dry run."
+		export OPTS_DRYRUN=true
 		export SUBTITLE_INDEX=-1
 	else
 		if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then
@@ -343,8 +344,13 @@ function selectSubs() {
 
 
 function extractSubs() {
-	echo "   > extracting subtitles"
 	export SUBTITLE_FILE="${TMP}/ripped.ass"
+	if [[ $SUBTITLE_INDEX -lt 0 ]]; then
+		echo "   > skipping subtitles (TODO: tidy cleanup)"
+		return
+	else
+		echo "   > extracting subtitles"
+	fi
 	extractIndex="$1"
 	"$FFMPEG" -i "${INPUT_VIDEO}" -map 0:"${extractIndex}" -vn -an -c:s copy -c:a copy \
 		"$SUBTITLE_FILE" 2>/dev/null
@@ -522,7 +528,26 @@ while true; do
 		"-h" | "--help")
 			# TODO: Display help
 			shift # all arguments parsed
-			echo "TODO: HELP!" # Display HELP
+			echo "$(basename $0) [args] <input> <output>"
+			#echo "TODO: HELP!" # Display HELP
+			cat << _EOT
+  ----------------------------------------------------------------------------
+    -k          auto-klobber when ffmpeg asks
+
+    -t <t>      ffmpeg encoding time limit
+    --crf <#>   override CRF setting
+    --soft      force software decode and encode
+    --psoft     use software encoding (allow hardware decode when available)
+    --audiofix  transcode audio
+    --audio     transcode audio, and low-pass filter as well
+
+    -l          list subtitles (no encoding)
+    -s <#>      select specific subtitle track number
+                #TODO: verify legal subtitle track number convention
+
+    -d          debug (no cleanup)
+    --dry       dry run (no encoding)
+_EOT
 			exit
 		;;
 		*)
@@ -536,7 +561,7 @@ done
 # Now parse POSITIONAL ARGUMENTS
 if [[ $# -ne 2 ]]; then
 	echo "ERROR: Incorrect number of positional arguments. Expected 2, got $#"
-	echo "       $0 [args] <input> <output>"
+	echo "       $(basename $0) [args] <input> <output>"
 	exit
 else
 	INPUT_VIDEO="$(readlink -f "$1")"

From 89fdfc7e7885b685683d3a77cd6e65c1c41ffd77 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Thu, 7 Nov 2019 07:44:32 -0800
Subject: [PATCH 07/29] v0.10.0

---
 burnSubs | 35 ++++++++++++++++++++++++++++++++---
 1 file changed, 32 insertions(+), 3 deletions(-)

diff --git a/burnSubs b/burnSubs
index f019935..561b8bb 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,13 +5,16 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.9.2
+# version 0.10.0
 #################3
 # Wishlist:
 #   queue encodes
 #   finish TODOs
 #	finish help flag
 #	audio recode flag
+#
+# Changes
+#   automatically select JPN audio if more than one audio channel found.
 ################################################################################
 
 function machineSetup() {
@@ -281,6 +284,7 @@ function parseStreams() {
 	# 
 	STREAMS_ALL="${TMP}/streams.json"
 	export STREAMS_SUB="${TMP}/subs.json"
+	export STREAMS_AUDIO="${TMP}/audio.json"
 	"$FFPROBE" -v error -of json -show_streams "${INPUT_VIDEO}" 2>/dev/null > "${STREAMS_ALL}"
 	
 	# Extract subtitles
@@ -288,6 +292,10 @@ function parseStreams() {
 	# shellcheck disable=SC2016
 	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
 	export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
+	# shellcheck disable=SC2016
+	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_type == "audio") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_AUDIO}"
+	export AUDIO_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
+	
 	
 }
 
@@ -340,6 +348,24 @@ function selectSubs() {
 		SUBTITLE_NAME=$($JQ '.['$SUBTITLE_ARRAY_INDEX'].t' "$STREAMS_SUB")
 		echo "   > subtitles: [${SUBTITLE_INDEX}] ${SUBTITLE_NAME}"
 	fi
+	
+	if [[ $AUDIO_COUNT -gt 1 ]]; then
+		LANG_TEST=$($JQ '[.[].lang] | index("jpn")' "$STREAMS_AUDIO")
+		echo "   > WARNING: Multiple audio streams!"
+		printf "       Using default selection rules... "
+		if [[ "$LANG_TEST" == "null" ]]; then
+			echo "Japanese audio not found!"
+			echo "    ==> Reverting to first subtitle file."
+			export AUDIO_INDEX=$($JQ '.[0].i' "$STREAMS_AUDIO")
+		else
+			# we found english
+			echo "Japanese audio found"
+			export AUDIO_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_AUDIO")
+		fi
+		export FF_AUDIO="-map 0:$AUDIO_INDEX -map 0:v:0"
+	else
+		export FF_AUDIO=""
+	fi
 }
 
 
@@ -362,7 +388,7 @@ function doTranscode() {
 	# shellcheck disable=SC2086
 	echo "$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
 		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
-		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT}	\
+		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO}	\
 		"${OUTPUT_VIDEO}"
 	if [[ "$OPTS_DRYRUN" == true ]]; then
 		return
@@ -370,7 +396,7 @@ function doTranscode() {
 	# shellcheck disable=SC2086
 	"$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
 		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
-		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT}	\
+		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO}	\
 		"${OUTPUT_VIDEO}"
 	export FINAL_STATUS=$?
 }
@@ -426,6 +452,7 @@ OPTS_DRYRUN=false
 OPTS_DEBUG=false
 OPTS_LPF_AUDIO=false
 OPTS_TRANS_AUDIO=false
+OPTS_derived_NO_OUTPUT=false
 unset OPT_CRF
 # this is the --icon flag passed to notify-send at the end of the transcode
 NOTIFY_ICON="face-tired"
@@ -465,6 +492,7 @@ while true; do
 		;;
 		"-l")
 			OPTS_LISTSUBS=true
+			OPTS_derived_NO_OUTPUT=true
 			echo ">> !! list subtitles and exit"
 			shift
 			continue
@@ -591,6 +619,7 @@ if [[ "$OPTS_LISTSUBS" == "true" ]]; then
 	exit	
 fi
 # ask the user for the subtitle file if more than one is available
+# Also selects audio stream.
 selectSubs
 # extract the selected subtitle file
 extractSubs $SUBTITLE_INDEX

From 0b604be35a7165949a47be1966be581f697a2e75 Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Thu, 7 Nov 2019 07:44:32 -0800
Subject: [PATCH 08/29] v0.10.0

---
 burnSubs | 35 ++++++++++++++++++++++++++++++++---
 1 file changed, 32 insertions(+), 3 deletions(-)

diff --git a/burnSubs b/burnSubs
index f019935..561b8bb 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,13 +5,16 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.9.2
+# version 0.10.0
 #################3
 # Wishlist:
 #   queue encodes
 #   finish TODOs
 #	finish help flag
 #	audio recode flag
+#
+# Changes
+#   automatically select JPN audio if more than one audio channel found.
 ################################################################################
 
 function machineSetup() {
@@ -281,6 +284,7 @@ function parseStreams() {
 	# 
 	STREAMS_ALL="${TMP}/streams.json"
 	export STREAMS_SUB="${TMP}/subs.json"
+	export STREAMS_AUDIO="${TMP}/audio.json"
 	"$FFPROBE" -v error -of json -show_streams "${INPUT_VIDEO}" 2>/dev/null > "${STREAMS_ALL}"
 	
 	# Extract subtitles
@@ -288,6 +292,10 @@ function parseStreams() {
 	# shellcheck disable=SC2016
 	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
 	export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
+	# shellcheck disable=SC2016
+	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_type == "audio") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_AUDIO}"
+	export AUDIO_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
+	
 	
 }
 
@@ -340,6 +348,24 @@ function selectSubs() {
 		SUBTITLE_NAME=$($JQ '.['$SUBTITLE_ARRAY_INDEX'].t' "$STREAMS_SUB")
 		echo "   > subtitles: [${SUBTITLE_INDEX}] ${SUBTITLE_NAME}"
 	fi
+	
+	if [[ $AUDIO_COUNT -gt 1 ]]; then
+		LANG_TEST=$($JQ '[.[].lang] | index("jpn")' "$STREAMS_AUDIO")
+		echo "   > WARNING: Multiple audio streams!"
+		printf "       Using default selection rules... "
+		if [[ "$LANG_TEST" == "null" ]]; then
+			echo "Japanese audio not found!"
+			echo "    ==> Reverting to first subtitle file."
+			export AUDIO_INDEX=$($JQ '.[0].i' "$STREAMS_AUDIO")
+		else
+			# we found english
+			echo "Japanese audio found"
+			export AUDIO_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_AUDIO")
+		fi
+		export FF_AUDIO="-map 0:$AUDIO_INDEX -map 0:v:0"
+	else
+		export FF_AUDIO=""
+	fi
 }
 
 
@@ -362,7 +388,7 @@ function doTranscode() {
 	# shellcheck disable=SC2086
 	echo "$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
 		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
-		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT}	\
+		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO}	\
 		"${OUTPUT_VIDEO}"
 	if [[ "$OPTS_DRYRUN" == true ]]; then
 		return
@@ -370,7 +396,7 @@ function doTranscode() {
 	# shellcheck disable=SC2086
 	"$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
 		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
-		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT}	\
+		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO}	\
 		"${OUTPUT_VIDEO}"
 	export FINAL_STATUS=$?
 }
@@ -426,6 +452,7 @@ OPTS_DRYRUN=false
 OPTS_DEBUG=false
 OPTS_LPF_AUDIO=false
 OPTS_TRANS_AUDIO=false
+OPTS_derived_NO_OUTPUT=false
 unset OPT_CRF
 # this is the --icon flag passed to notify-send at the end of the transcode
 NOTIFY_ICON="face-tired"
@@ -465,6 +492,7 @@ while true; do
 		;;
 		"-l")
 			OPTS_LISTSUBS=true
+			OPTS_derived_NO_OUTPUT=true
 			echo ">> !! list subtitles and exit"
 			shift
 			continue
@@ -591,6 +619,7 @@ if [[ "$OPTS_LISTSUBS" == "true" ]]; then
 	exit	
 fi
 # ask the user for the subtitle file if more than one is available
+# Also selects audio stream.
 selectSubs
 # extract the selected subtitle file
 extractSubs $SUBTITLE_INDEX

From 2ec1960ebe5df72f061dcb7e4a78499025c9466e Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Thu, 7 Nov 2019 07:44:50 -0800
Subject: [PATCH 09/29] v0.10.1

---
 burnSubs | 76 +++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 56 insertions(+), 20 deletions(-)

diff --git a/burnSubs b/burnSubs
index 561b8bb..2835f6c 100755
--- a/burnSubs
+++ b/burnSubs
@@ -294,7 +294,7 @@ function parseStreams() {
 	export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
 	# shellcheck disable=SC2016
 	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_type == "audio") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_AUDIO}"
-	export AUDIO_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
+	export AUDIO_COUNT=$("$JQ" 'length' "${STREAMS_AUDIO}")
 	
 	
 }
@@ -306,8 +306,8 @@ function listSubtitles() {
 	printf "Num: %4s %s\n" "LANG" "Subtitle Title String"
 	echo "---------------------------------"
 	for iSUB in $(seq 0 $(($SUB_COUNT-1))); do
-		X_TITLE=$($JQ '.['$iSUB'].t' $STREAMS_SUB)
-		X_INDEX=$($JQ '.['$iSUB'].i' $STREAMS_SUB)
+		X_TITLE=$($JQ '.['$iSUB'].t' $STREAMS_SUB | tr -d '"')
+		X_INDEX=$($JQ '.['$iSUB'].i' $STREAMS_SUB | tr -d '"')
 		X_LANG=$($JQ '.['$iSUB'].lang' $STREAMS_SUB)
 		printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE" 
 	done
@@ -315,6 +315,22 @@ function listSubtitles() {
 	return
 }
 
+function listAudioTracks() {
+	# TODO: 
+	echo ""
+	echo "available audio tracks:"
+	printf "Num: %4s %s\n" "LANG" "Title String"
+	echo "---------------------------------"
+	for iAUDIO in $(seq 0 $(($AUDIO_COUNT-1))); do
+		X_TITLE=$($JQ '.['$iAUDIO'].t' $STREAMS_AUDIO | tr -d '"')
+		X_INDEX=$($JQ '.['$iAUDIO'].i' $STREAMS_AUDIO | tr -d '"')
+		X_LANG=$($JQ '.['$iAUDIO'].lang' $STREAMS_AUDIO)
+		printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE" 
+	done
+	echo ""
+	return
+}
+
 function selectSubs() {
 	# TODO: handle multiple subtitle files
 	# TODO: verify the the subtitle index is legal
@@ -350,17 +366,21 @@ function selectSubs() {
 	fi
 	
 	if [[ $AUDIO_COUNT -gt 1 ]]; then
-		LANG_TEST=$($JQ '[.[].lang] | index("jpn")' "$STREAMS_AUDIO")
-		echo "   > WARNING: Multiple audio streams!"
-		printf "       Using default selection rules... "
-		if [[ "$LANG_TEST" == "null" ]]; then
-			echo "Japanese audio not found!"
-			echo "    ==> Reverting to first subtitle file."
-			export AUDIO_INDEX=$($JQ '.[0].i' "$STREAMS_AUDIO")
+		if [[ $OPTS_SELAUDIO -lt 0 ]]; then
+			LANG_TEST=$($JQ '[.[].lang] | index("jpn")' "$STREAMS_AUDIO")
+			echo "   > WARNING: Multiple audio streams!"
+			printf "       Using default selection rules... "
+			if [[ "$LANG_TEST" == "null" ]]; then
+				echo "Japanese audio not found!"
+				echo "    ==> Reverting to first subtitle file."
+				export AUDIO_INDEX=$($JQ '.[0].i' "$STREAMS_AUDIO")
+			else
+				# we found english
+				echo "Japanese audio found"
+				export AUDIO_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_AUDIO")
+			fi
 		else
-			# we found english
-			echo "Japanese audio found"
-			export AUDIO_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_AUDIO")
+			AUDIO_INDEX=$OPTS_SELAUDIO
 		fi
 		export FF_AUDIO="-map 0:$AUDIO_INDEX -map 0:v:0"
 	else
@@ -443,6 +463,7 @@ function alertUser() {
 OPTS_KLOBBER=false
 OPTS_LISTSUBS=false
 OPTS_SELSUB=-1
+OPTS_SELAUDIO=-1
 OPTS_LISTSUBS=false
 OPTS_FORCESOFT=false
 OPTS_FORCEPARTSOFT=false
@@ -505,13 +526,21 @@ while true; do
 			shift 2
 			continue
 		;;
+		"-a")
+			OPTS_SELAUDIO="$2"
+			#TODO: verify legal subtitle track number convention"
+			echo "TODO: verify legal audio track number convention" 
+			echo ">> !! Selecting audio track #${OPTS_SELAUDIO}"
+			shift 2
+			continue
+		;;
 		"--psoft")
 			OPTS_FORCEPARTSOFT=true
 			echo ">> !! forcing software encoding."
 			shift
 			continue
 		;;
-		"--audio")
+		"--lpf")
 			OPTS_LPF_AUDIO=true
 			OPTS_TRANS_AUDIO=true
 			echo ">> !! low pass filter audio to AAC."
@@ -567,11 +596,13 @@ while true; do
     --soft      force software decode and encode
     --psoft     use software encoding (allow hardware decode when available)
     --audiofix  transcode audio
-    --audio     transcode audio, and low-pass filter as well
+    --lpf     transcode audio, and low-pass filter as well
 
-    -l          list subtitles (no encoding)
+    -l          list subtitles and audio tracks (no encoding)
     -s <#>      select specific subtitle track number
                 #TODO: verify legal subtitle track number convention
+    -a <#>      select specific subtitle track number
+                #TODO: verify legal subtitle track number convention
 
     -d          debug (no cleanup)
     --dry       dry run (no encoding)
@@ -587,13 +618,17 @@ _EOT
 done
 
 # Now parse POSITIONAL ARGUMENTS
-if [[ $# -ne 2 ]]; then
+if [[ $# -eq 2 || $OPTS_derived_NO_OUTPUT == true ]]; then
+	INPUT_VIDEO="$(readlink -f "$1")"
+	if [[ $# -eq 2 ]]; then
+		OUTPUT_VIDEO="$2"
+	else
+		OUTPUT_VIDEO="/dev/null"
+	fi
+else
 	echo "ERROR: Incorrect number of positional arguments. Expected 2, got $#"
 	echo "       $(basename $0) [args] <input> <output>"
 	exit
-else
-	INPUT_VIDEO="$(readlink -f "$1")"
-	OUTPUT_VIDEO="$2"
 fi
 
 ###############
@@ -616,6 +651,7 @@ parseStreams
 # Now! If OPTS_LISTSUBS is defined, then we branch to list subs and exit.
 if [[ "$OPTS_LISTSUBS" == "true" ]]; then
 	listSubtitles
+	listAudioTracks
 	exit	
 fi
 # ask the user for the subtitle file if more than one is available

From cb96ff8b3cd81fe66a6b015db66159993665840c Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Thu, 7 Nov 2019 07:44:50 -0800
Subject: [PATCH 10/29] v0.10.1

---
 burnSubs | 76 +++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 56 insertions(+), 20 deletions(-)

diff --git a/burnSubs b/burnSubs
index 561b8bb..2835f6c 100755
--- a/burnSubs
+++ b/burnSubs
@@ -294,7 +294,7 @@ function parseStreams() {
 	export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
 	# shellcheck disable=SC2016
 	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_type == "audio") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_AUDIO}"
-	export AUDIO_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
+	export AUDIO_COUNT=$("$JQ" 'length' "${STREAMS_AUDIO}")
 	
 	
 }
@@ -306,8 +306,8 @@ function listSubtitles() {
 	printf "Num: %4s %s\n" "LANG" "Subtitle Title String"
 	echo "---------------------------------"
 	for iSUB in $(seq 0 $(($SUB_COUNT-1))); do
-		X_TITLE=$($JQ '.['$iSUB'].t' $STREAMS_SUB)
-		X_INDEX=$($JQ '.['$iSUB'].i' $STREAMS_SUB)
+		X_TITLE=$($JQ '.['$iSUB'].t' $STREAMS_SUB | tr -d '"')
+		X_INDEX=$($JQ '.['$iSUB'].i' $STREAMS_SUB | tr -d '"')
 		X_LANG=$($JQ '.['$iSUB'].lang' $STREAMS_SUB)
 		printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE" 
 	done
@@ -315,6 +315,22 @@ function listSubtitles() {
 	return
 }
 
+function listAudioTracks() {
+	# TODO: 
+	echo ""
+	echo "available audio tracks:"
+	printf "Num: %4s %s\n" "LANG" "Title String"
+	echo "---------------------------------"
+	for iAUDIO in $(seq 0 $(($AUDIO_COUNT-1))); do
+		X_TITLE=$($JQ '.['$iAUDIO'].t' $STREAMS_AUDIO | tr -d '"')
+		X_INDEX=$($JQ '.['$iAUDIO'].i' $STREAMS_AUDIO | tr -d '"')
+		X_LANG=$($JQ '.['$iAUDIO'].lang' $STREAMS_AUDIO)
+		printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE" 
+	done
+	echo ""
+	return
+}
+
 function selectSubs() {
 	# TODO: handle multiple subtitle files
 	# TODO: verify the the subtitle index is legal
@@ -350,17 +366,21 @@ function selectSubs() {
 	fi
 	
 	if [[ $AUDIO_COUNT -gt 1 ]]; then
-		LANG_TEST=$($JQ '[.[].lang] | index("jpn")' "$STREAMS_AUDIO")
-		echo "   > WARNING: Multiple audio streams!"
-		printf "       Using default selection rules... "
-		if [[ "$LANG_TEST" == "null" ]]; then
-			echo "Japanese audio not found!"
-			echo "    ==> Reverting to first subtitle file."
-			export AUDIO_INDEX=$($JQ '.[0].i' "$STREAMS_AUDIO")
+		if [[ $OPTS_SELAUDIO -lt 0 ]]; then
+			LANG_TEST=$($JQ '[.[].lang] | index("jpn")' "$STREAMS_AUDIO")
+			echo "   > WARNING: Multiple audio streams!"
+			printf "       Using default selection rules... "
+			if [[ "$LANG_TEST" == "null" ]]; then
+				echo "Japanese audio not found!"
+				echo "    ==> Reverting to first subtitle file."
+				export AUDIO_INDEX=$($JQ '.[0].i' "$STREAMS_AUDIO")
+			else
+				# we found english
+				echo "Japanese audio found"
+				export AUDIO_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_AUDIO")
+			fi
 		else
-			# we found english
-			echo "Japanese audio found"
-			export AUDIO_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_AUDIO")
+			AUDIO_INDEX=$OPTS_SELAUDIO
 		fi
 		export FF_AUDIO="-map 0:$AUDIO_INDEX -map 0:v:0"
 	else
@@ -443,6 +463,7 @@ function alertUser() {
 OPTS_KLOBBER=false
 OPTS_LISTSUBS=false
 OPTS_SELSUB=-1
+OPTS_SELAUDIO=-1
 OPTS_LISTSUBS=false
 OPTS_FORCESOFT=false
 OPTS_FORCEPARTSOFT=false
@@ -505,13 +526,21 @@ while true; do
 			shift 2
 			continue
 		;;
+		"-a")
+			OPTS_SELAUDIO="$2"
+			#TODO: verify legal subtitle track number convention"
+			echo "TODO: verify legal audio track number convention" 
+			echo ">> !! Selecting audio track #${OPTS_SELAUDIO}"
+			shift 2
+			continue
+		;;
 		"--psoft")
 			OPTS_FORCEPARTSOFT=true
 			echo ">> !! forcing software encoding."
 			shift
 			continue
 		;;
-		"--audio")
+		"--lpf")
 			OPTS_LPF_AUDIO=true
 			OPTS_TRANS_AUDIO=true
 			echo ">> !! low pass filter audio to AAC."
@@ -567,11 +596,13 @@ while true; do
     --soft      force software decode and encode
     --psoft     use software encoding (allow hardware decode when available)
     --audiofix  transcode audio
-    --audio     transcode audio, and low-pass filter as well
+    --lpf     transcode audio, and low-pass filter as well
 
-    -l          list subtitles (no encoding)
+    -l          list subtitles and audio tracks (no encoding)
     -s <#>      select specific subtitle track number
                 #TODO: verify legal subtitle track number convention
+    -a <#>      select specific subtitle track number
+                #TODO: verify legal subtitle track number convention
 
     -d          debug (no cleanup)
     --dry       dry run (no encoding)
@@ -587,13 +618,17 @@ _EOT
 done
 
 # Now parse POSITIONAL ARGUMENTS
-if [[ $# -ne 2 ]]; then
+if [[ $# -eq 2 || $OPTS_derived_NO_OUTPUT == true ]]; then
+	INPUT_VIDEO="$(readlink -f "$1")"
+	if [[ $# -eq 2 ]]; then
+		OUTPUT_VIDEO="$2"
+	else
+		OUTPUT_VIDEO="/dev/null"
+	fi
+else
 	echo "ERROR: Incorrect number of positional arguments. Expected 2, got $#"
 	echo "       $(basename $0) [args] <input> <output>"
 	exit
-else
-	INPUT_VIDEO="$(readlink -f "$1")"
-	OUTPUT_VIDEO="$2"
 fi
 
 ###############
@@ -616,6 +651,7 @@ parseStreams
 # Now! If OPTS_LISTSUBS is defined, then we branch to list subs and exit.
 if [[ "$OPTS_LISTSUBS" == "true" ]]; then
 	listSubtitles
+	listAudioTracks
 	exit	
 fi
 # ask the user for the subtitle file if more than one is available

From 28c2c7fddd2792d87c5c49519be755d95e3c8b12 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Thu, 7 Nov 2019 07:45:04 -0800
Subject: [PATCH 11/29] v0.10.2

---
 burnSubs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/burnSubs b/burnSubs
index 2835f6c..f0c0f85 100755
--- a/burnSubs
+++ b/burnSubs
@@ -211,7 +211,7 @@ function dumpFonts() {
 	# Dumb font dump
 	cd "$FONTDIR"
 	"$FFMPEG" -dump_attachment:t "" -i "${INPUT_VIDEO}" -vn -an \
-		-f null /dev/null 2>/dev/null || /bin/true
+		-f null -y /dev/null 2>/dev/null || /bin/true
 	cd "$WD"
 }
 

From 82e028bc14a8aa6b14353324dc2c79c023583dc3 Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Thu, 7 Nov 2019 07:45:04 -0800
Subject: [PATCH 12/29] v0.10.2

---
 burnSubs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/burnSubs b/burnSubs
index 2835f6c..f0c0f85 100755
--- a/burnSubs
+++ b/burnSubs
@@ -211,7 +211,7 @@ function dumpFonts() {
 	# Dumb font dump
 	cd "$FONTDIR"
 	"$FFMPEG" -dump_attachment:t "" -i "${INPUT_VIDEO}" -vn -an \
-		-f null /dev/null 2>/dev/null || /bin/true
+		-f null -y /dev/null 2>/dev/null || /bin/true
 	cd "$WD"
 }
 

From be2c3c111e0bd732da3a2aa8d9978d45aefb49f3 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Thu, 7 Nov 2019 07:46:13 -0800
Subject: [PATCH 13/29] v0.10.4, formal git setup.

---
 .gitignore | 2 ++
 burnSubs   | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)
 create mode 100644 .gitignore

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6ef5c6d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.directory
+
diff --git a/burnSubs b/burnSubs
index f0c0f85..2f2cbf7 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.10.0
+# version 0.10.4
 #################3
 # Wishlist:
 #   queue encodes

From d40f7b8b63042569b527c3112f196f187fbbb46a Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Thu, 7 Nov 2019 07:46:13 -0800
Subject: [PATCH 14/29] v0.10.4, formal git setup.

---
 .gitignore | 2 ++
 burnSubs   | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)
 create mode 100644 .gitignore

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6ef5c6d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.directory
+
diff --git a/burnSubs b/burnSubs
index f0c0f85..2f2cbf7 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.10.0
+# version 0.10.4
 #################3
 # Wishlist:
 #   queue encodes

From 85aa98c6200109748d0dd909ba9153425c3b2f9c Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Sun, 12 Apr 2020 18:06:45 -0700
Subject: [PATCH 15/29] Added flags for strings to capture specific languages.

---
 burnSubs | 35 +++++++++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/burnSubs b/burnSubs
index 2f2cbf7..4b504ca 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.10.4
+# version 0.11.0
 #################3
 # Wishlist:
 #   queue encodes
@@ -23,6 +23,7 @@ function machineSetup() {
 	export FF_HW=""
 	export FILT_PFX=""
 	export FILT_SFX=""
+	
 	CRF=${OPTS_CRF:-23}
 	export FF_STD="-preset myTranscoderPreset -crf $CRF -tune animation \
 		-preset medium -movflags +faststart"
@@ -341,7 +342,8 @@ function selectSubs() {
 		export SUBTITLE_INDEX=-1
 	else
 		if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then
-			LANG_TEST=$($JQ '[.[].lang] | index("eng")' "$STREAMS_SUB")
+			OPTS_SELSUB_LANG="${OPTS_SELSUB_LANG:-eng}"
+			LANG_TEST=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
 			echo "   > WARNING: Multiple subtitles!"
 			printf "       Using default selection rules... "
 			if [[ "$LANG_TEST" == "null" ]]; then
@@ -367,7 +369,8 @@ function selectSubs() {
 	
 	if [[ $AUDIO_COUNT -gt 1 ]]; then
 		if [[ $OPTS_SELAUDIO -lt 0 ]]; then
-			LANG_TEST=$($JQ '[.[].lang] | index("jpn")' "$STREAMS_AUDIO")
+			OPTS_SELAUDIO_LANG="${OPTS_SELAUDIO_LANG:-jpn}"
+			LANG_TEST=$($JQ '[.[].lang] | index("'${OPTS_SELAUDIO_LANG}'")' "$STREAMS_AUDIO")
 			echo "   > WARNING: Multiple audio streams!"
 			printf "       Using default selection rules... "
 			if [[ "$LANG_TEST" == "null" ]]; then
@@ -493,7 +496,7 @@ FINAL_STATUS=1
 
 ######
 # Reformat and organize the input strings
-OPT_STRING=$(getopt -o 'hkls:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix' -- "$@")
+OPT_STRING=$(getopt -o 'hkls:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:' -- "$@")
 # reassign them as positional arguments
 eval set -- "$OPT_STRING"
 
@@ -526,6 +529,14 @@ while true; do
 			shift 2
 			continue
 		;;
+		"--slocale")
+			OPTS_SELSUB_LANG="$2"
+			#TODO: verify legal subtitle track number convention"
+			echo "TODO: verify legal subtitle track number convention" 
+			echo ">> !! Selecting subtitle track #${OPTS_SELSUB_LANG}"
+			shift 2
+			continue
+		;;
 		"-a")
 			OPTS_SELAUDIO="$2"
 			#TODO: verify legal subtitle track number convention"
@@ -534,6 +545,14 @@ while true; do
 			shift 2
 			continue
 		;;
+		"--alocale")
+			OPTS_SELAUDIO_LANG="$2"
+			#TODO: verify legal subtitle track number convention"
+			echo "TODO: verify legal audio track number convention" 
+			echo ">> !! Selecting audio track with locale #${OPTS_SELAUDIO_LANG}"
+			shift 2
+			continue
+		;;
 		"--psoft")
 			OPTS_FORCEPARTSOFT=true
 			echo ">> !! forcing software encoding."
@@ -601,8 +620,12 @@ while true; do
     -l          list subtitles and audio tracks (no encoding)
     -s <#>      select specific subtitle track number
                 #TODO: verify legal subtitle track number convention
-    -a <#>      select specific subtitle track number
-                #TODO: verify legal subtitle track number convention
+    -a <#>      select specific audio track number
+                #TODO: verify legal audio track number convention
+    --slocale 'eng'     select specific subtitle track number
+                        #TODO: verify legal subtitle track number convention
+    --alocale 'jpn'     select specific audio track number
+                        #TODO: verify legal audio track number convention
 
     -d          debug (no cleanup)
     --dry       dry run (no encoding)

From 84cbd0bc744cea86fcd72e97ff27edfc7b185069 Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Sun, 12 Apr 2020 18:06:45 -0700
Subject: [PATCH 16/29] Added flags for strings to capture specific languages.

---
 burnSubs | 35 +++++++++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/burnSubs b/burnSubs
index 2f2cbf7..4b504ca 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.10.4
+# version 0.11.0
 #################3
 # Wishlist:
 #   queue encodes
@@ -23,6 +23,7 @@ function machineSetup() {
 	export FF_HW=""
 	export FILT_PFX=""
 	export FILT_SFX=""
+	
 	CRF=${OPTS_CRF:-23}
 	export FF_STD="-preset myTranscoderPreset -crf $CRF -tune animation \
 		-preset medium -movflags +faststart"
@@ -341,7 +342,8 @@ function selectSubs() {
 		export SUBTITLE_INDEX=-1
 	else
 		if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then
-			LANG_TEST=$($JQ '[.[].lang] | index("eng")' "$STREAMS_SUB")
+			OPTS_SELSUB_LANG="${OPTS_SELSUB_LANG:-eng}"
+			LANG_TEST=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
 			echo "   > WARNING: Multiple subtitles!"
 			printf "       Using default selection rules... "
 			if [[ "$LANG_TEST" == "null" ]]; then
@@ -367,7 +369,8 @@ function selectSubs() {
 	
 	if [[ $AUDIO_COUNT -gt 1 ]]; then
 		if [[ $OPTS_SELAUDIO -lt 0 ]]; then
-			LANG_TEST=$($JQ '[.[].lang] | index("jpn")' "$STREAMS_AUDIO")
+			OPTS_SELAUDIO_LANG="${OPTS_SELAUDIO_LANG:-jpn}"
+			LANG_TEST=$($JQ '[.[].lang] | index("'${OPTS_SELAUDIO_LANG}'")' "$STREAMS_AUDIO")
 			echo "   > WARNING: Multiple audio streams!"
 			printf "       Using default selection rules... "
 			if [[ "$LANG_TEST" == "null" ]]; then
@@ -493,7 +496,7 @@ FINAL_STATUS=1
 
 ######
 # Reformat and organize the input strings
-OPT_STRING=$(getopt -o 'hkls:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix' -- "$@")
+OPT_STRING=$(getopt -o 'hkls:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:' -- "$@")
 # reassign them as positional arguments
 eval set -- "$OPT_STRING"
 
@@ -526,6 +529,14 @@ while true; do
 			shift 2
 			continue
 		;;
+		"--slocale")
+			OPTS_SELSUB_LANG="$2"
+			#TODO: verify legal subtitle track number convention"
+			echo "TODO: verify legal subtitle track number convention" 
+			echo ">> !! Selecting subtitle track #${OPTS_SELSUB_LANG}"
+			shift 2
+			continue
+		;;
 		"-a")
 			OPTS_SELAUDIO="$2"
 			#TODO: verify legal subtitle track number convention"
@@ -534,6 +545,14 @@ while true; do
 			shift 2
 			continue
 		;;
+		"--alocale")
+			OPTS_SELAUDIO_LANG="$2"
+			#TODO: verify legal subtitle track number convention"
+			echo "TODO: verify legal audio track number convention" 
+			echo ">> !! Selecting audio track with locale #${OPTS_SELAUDIO_LANG}"
+			shift 2
+			continue
+		;;
 		"--psoft")
 			OPTS_FORCEPARTSOFT=true
 			echo ">> !! forcing software encoding."
@@ -601,8 +620,12 @@ while true; do
     -l          list subtitles and audio tracks (no encoding)
     -s <#>      select specific subtitle track number
                 #TODO: verify legal subtitle track number convention
-    -a <#>      select specific subtitle track number
-                #TODO: verify legal subtitle track number convention
+    -a <#>      select specific audio track number
+                #TODO: verify legal audio track number convention
+    --slocale 'eng'     select specific subtitle track number
+                        #TODO: verify legal subtitle track number convention
+    --alocale 'jpn'     select specific audio track number
+                        #TODO: verify legal audio track number convention
 
     -d          debug (no cleanup)
     --dry       dry run (no encoding)

From 1eea92094b7a899b04557678851cf1cc705cfb76 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Fri, 8 May 2020 19:39:06 -0700
Subject: [PATCH 17/29] Fixed short -a flag bug

---
 burnSubs | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/burnSubs b/burnSubs
index 4b504ca..40a27c2 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.11.0
+# version 0.11.1
 #################3
 # Wishlist:
 #   queue encodes
@@ -291,7 +291,7 @@ function parseStreams() {
 	# Extract subtitles
 	#$item.codec_type == "subtitle" \&\& 
 	# shellcheck disable=SC2016
-	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
+	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass" or $item.codec_name == "dvd_subtitle") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition, codec:($item.codec_name)}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition,codec:$item.codec}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
 	export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
 	# shellcheck disable=SC2016
 	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_type == "audio") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_AUDIO}"
@@ -310,7 +310,11 @@ function listSubtitles() {
 		X_TITLE=$($JQ '.['$iSUB'].t' $STREAMS_SUB | tr -d '"')
 		X_INDEX=$($JQ '.['$iSUB'].i' $STREAMS_SUB | tr -d '"')
 		X_LANG=$($JQ '.['$iSUB'].lang' $STREAMS_SUB)
+		X_CODEC=$($JQ '.['$iSUB'].codec' $STREAMS_SUB)
 		printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE" 
+		if [[ "$X_CODEC" != "ass" ]]; then
+			printf "      format: %s\n" $X_CODEC
+		fi
 	done
 	echo ""
 	return
@@ -496,7 +500,7 @@ FINAL_STATUS=1
 
 ######
 # Reformat and organize the input strings
-OPT_STRING=$(getopt -o 'hkls:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:' -- "$@")
+OPT_STRING=$(getopt -o 'hkls:a:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:' -- "$@")
 # reassign them as positional arguments
 eval set -- "$OPT_STRING"
 

From 328fcb23185dae3f3d9606d72520819b5b55c17f Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Fri, 8 May 2020 19:39:06 -0700
Subject: [PATCH 18/29] Fixed short -a flag bug

---
 burnSubs | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/burnSubs b/burnSubs
index 4b504ca..40a27c2 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.11.0
+# version 0.11.1
 #################3
 # Wishlist:
 #   queue encodes
@@ -291,7 +291,7 @@ function parseStreams() {
 	# Extract subtitles
 	#$item.codec_type == "subtitle" \&\& 
 	# shellcheck disable=SC2016
-	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
+	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass" or $item.codec_name == "dvd_subtitle") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition, codec:($item.codec_name)}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition,codec:$item.codec}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
 	export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
 	# shellcheck disable=SC2016
 	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_type == "audio") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_AUDIO}"
@@ -310,7 +310,11 @@ function listSubtitles() {
 		X_TITLE=$($JQ '.['$iSUB'].t' $STREAMS_SUB | tr -d '"')
 		X_INDEX=$($JQ '.['$iSUB'].i' $STREAMS_SUB | tr -d '"')
 		X_LANG=$($JQ '.['$iSUB'].lang' $STREAMS_SUB)
+		X_CODEC=$($JQ '.['$iSUB'].codec' $STREAMS_SUB)
 		printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE" 
+		if [[ "$X_CODEC" != "ass" ]]; then
+			printf "      format: %s\n" $X_CODEC
+		fi
 	done
 	echo ""
 	return
@@ -496,7 +500,7 @@ FINAL_STATUS=1
 
 ######
 # Reformat and organize the input strings
-OPT_STRING=$(getopt -o 'hkls:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:' -- "$@")
+OPT_STRING=$(getopt -o 'hkls:a:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:' -- "$@")
 # reassign them as positional arguments
 eval set -- "$OPT_STRING"
 

From 5a1809437770ec619b3711d3fecbd3cfe8ff42fc Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Fri, 15 May 2020 17:59:56 -0700
Subject: [PATCH 19/29] Improved default filter selection rules.

---
 burnSubs | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/burnSubs b/burnSubs
index 40a27c2..39f9a20 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,8 +5,8 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.11.1
-#################3
+# version 0.11.2
+#################
 # Wishlist:
 #   queue encodes
 #   finish TODOs
@@ -346,18 +346,29 @@ function selectSubs() {
 		export SUBTITLE_INDEX=-1
 	else
 		if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then
-			OPTS_SELSUB_LANG="${OPTS_SELSUB_LANG:-eng}"
-			LANG_TEST=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
 			echo "   > WARNING: Multiple subtitles!"
 			printf "       Using default selection rules... "
-			if [[ "$LANG_TEST" == "null" ]]; then
+			OPTS_SELSUB_LANG="${OPTS_SELSUB_LANG:-eng}"
+			LANG_TEST=$($JQ '[.[].lang | match("'${OPTS_SELSUB_LANG}'")] | length' "$STREAMS_SUB")
+			if [[ "$LANG_TEST" == "0" ]]; then
 				echo "English not found!"
 				echo "    ==> Reverting to first subtitle file."
 				export SUBTITLE_INDEX=$($JQ '.[0].i' "$STREAMS_SUB")
-			else
+			elif [[ "$LANG_TEST" == "1" ]]; then
+				LANG_SINGLE_SELECT=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
 				# we found english
 				echo "English found"
-				export SUBTITLE_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_SUB")
+				export SUBTITLE_INDEX=$($JQ '.['$LANG_SINGLE_SELECT'].i' "$STREAMS_SUB")
+			else
+				# try to avoid a signs and lyrics track
+				LANG_SINGLE_SELECT=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
+				echo "Multiple english tracks found."
+				export SUBTITLE_INDEX=$($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and ( ($trk.t | test("lyrics";"i") or ($trk.t | test("signs";"i")) ) | not ) ) then [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].i' "$STREAMS_SUB")
+				# And display rejected subtitles too.
+				SUBTITLE_REJECT_LIST=($($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and ( ($trk.t | test("lyrics";"i") or ($trk.t | test("signs";"i")) ) ) ) then [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].t' "$STREAMS_SUB"))
+				for REJECT_SUB in ${SUBTITLE_REJECT_LIST[@]}; do
+					echo "       > rejecting ${REJECT_SUB}"
+				done
 			fi
 		else
 			if [[ $SUB_COUNT -eq 1 ]]; then
@@ -379,12 +390,14 @@ function selectSubs() {
 			printf "       Using default selection rules... "
 			if [[ "$LANG_TEST" == "null" ]]; then
 				echo "Japanese audio not found!"
-				echo "    ==> Reverting to first subtitle file."
+				echo "    ==> Reverting to first audio stream."
 				export AUDIO_INDEX=$($JQ '.[0].i' "$STREAMS_AUDIO")
 			else
 				# we found english
 				echo "Japanese audio found"
 				export AUDIO_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_AUDIO")
+				AUDIO_STREAM_TITLE=$($JQ '.['$LANG_TEST'].t' "$STREAMS_AUDIO")
+				echo "    ==> stream has title ${AUDIO_STREAM_TITLE}"
 			fi
 		else
 			AUDIO_INDEX=$OPTS_SELAUDIO

From 464f3e31ca60fe249c19946a53ad77a5aea5a594 Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Fri, 15 May 2020 17:59:56 -0700
Subject: [PATCH 20/29] Improved default filter selection rules.

---
 burnSubs | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/burnSubs b/burnSubs
index 40a27c2..39f9a20 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,8 +5,8 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.11.1
-#################3
+# version 0.11.2
+#################
 # Wishlist:
 #   queue encodes
 #   finish TODOs
@@ -346,18 +346,29 @@ function selectSubs() {
 		export SUBTITLE_INDEX=-1
 	else
 		if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then
-			OPTS_SELSUB_LANG="${OPTS_SELSUB_LANG:-eng}"
-			LANG_TEST=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
 			echo "   > WARNING: Multiple subtitles!"
 			printf "       Using default selection rules... "
-			if [[ "$LANG_TEST" == "null" ]]; then
+			OPTS_SELSUB_LANG="${OPTS_SELSUB_LANG:-eng}"
+			LANG_TEST=$($JQ '[.[].lang | match("'${OPTS_SELSUB_LANG}'")] | length' "$STREAMS_SUB")
+			if [[ "$LANG_TEST" == "0" ]]; then
 				echo "English not found!"
 				echo "    ==> Reverting to first subtitle file."
 				export SUBTITLE_INDEX=$($JQ '.[0].i' "$STREAMS_SUB")
-			else
+			elif [[ "$LANG_TEST" == "1" ]]; then
+				LANG_SINGLE_SELECT=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
 				# we found english
 				echo "English found"
-				export SUBTITLE_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_SUB")
+				export SUBTITLE_INDEX=$($JQ '.['$LANG_SINGLE_SELECT'].i' "$STREAMS_SUB")
+			else
+				# try to avoid a signs and lyrics track
+				LANG_SINGLE_SELECT=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
+				echo "Multiple english tracks found."
+				export SUBTITLE_INDEX=$($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and ( ($trk.t | test("lyrics";"i") or ($trk.t | test("signs";"i")) ) | not ) ) then [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].i' "$STREAMS_SUB")
+				# And display rejected subtitles too.
+				SUBTITLE_REJECT_LIST=($($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and ( ($trk.t | test("lyrics";"i") or ($trk.t | test("signs";"i")) ) ) ) then [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].t' "$STREAMS_SUB"))
+				for REJECT_SUB in ${SUBTITLE_REJECT_LIST[@]}; do
+					echo "       > rejecting ${REJECT_SUB}"
+				done
 			fi
 		else
 			if [[ $SUB_COUNT -eq 1 ]]; then
@@ -379,12 +390,14 @@ function selectSubs() {
 			printf "       Using default selection rules... "
 			if [[ "$LANG_TEST" == "null" ]]; then
 				echo "Japanese audio not found!"
-				echo "    ==> Reverting to first subtitle file."
+				echo "    ==> Reverting to first audio stream."
 				export AUDIO_INDEX=$($JQ '.[0].i' "$STREAMS_AUDIO")
 			else
 				# we found english
 				echo "Japanese audio found"
 				export AUDIO_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_AUDIO")
+				AUDIO_STREAM_TITLE=$($JQ '.['$LANG_TEST'].t' "$STREAMS_AUDIO")
+				echo "    ==> stream has title ${AUDIO_STREAM_TITLE}"
 			fi
 		else
 			AUDIO_INDEX=$OPTS_SELAUDIO

From a3817c90fb20eaa989c57f07d82beb4f415153d9 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Sat, 11 Jul 2020 11:49:19 -0700
Subject: [PATCH 21/29] added option to display default CRF in help.

---
 burnSubs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/burnSubs b/burnSubs
index 39f9a20..7807ecc 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.11.2
+# version 0.11.3
 #################
 # Wishlist:
 #   queue encodes
@@ -17,6 +17,8 @@ set -o errexit
 #   automatically select JPN audio if more than one audio channel found.
 ################################################################################
 
+DEFAULTS_OPTS_CRF=20
+
 function machineSetup() {
 	# Default setup
 	export FF_ENC="libx264"
@@ -24,7 +26,7 @@ function machineSetup() {
 	export FILT_PFX=""
 	export FILT_SFX=""
 	
-	CRF=${OPTS_CRF:-23}
+	CRF=${OPTS_CRF:-$DEFAULTS_OPTS_CRF}
 	export FF_STD="-preset myTranscoderPreset -crf $CRF -tune animation \
 		-preset medium -movflags +faststart"
 	export FF_EXT="-profile:v high -level 4.0"
@@ -629,6 +631,7 @@ while true; do
 
     -t <t>      ffmpeg encoding time limit
     --crf <#>   override CRF setting
+                    default: $DEFAULTS_OPTS_CRF
     --soft      force software decode and encode
     --psoft     use software encoding (allow hardware decode when available)
     --audiofix  transcode audio

From 4dff106fdb457abb096d5487bf72c2547a1b1f51 Mon Sep 17 00:00:00 2001
From: Luke Renaud <luke@dabblee.com>
Date: Sat, 11 Jul 2020 11:49:19 -0700
Subject: [PATCH 22/29] added option to display default CRF in help.

---
 burnSubs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/burnSubs b/burnSubs
index 39f9a20..7807ecc 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.11.2
+# version 0.11.3
 #################
 # Wishlist:
 #   queue encodes
@@ -17,6 +17,8 @@ set -o errexit
 #   automatically select JPN audio if more than one audio channel found.
 ################################################################################
 
+DEFAULTS_OPTS_CRF=20
+
 function machineSetup() {
 	# Default setup
 	export FF_ENC="libx264"
@@ -24,7 +26,7 @@ function machineSetup() {
 	export FILT_PFX=""
 	export FILT_SFX=""
 	
-	CRF=${OPTS_CRF:-23}
+	CRF=${OPTS_CRF:-$DEFAULTS_OPTS_CRF}
 	export FF_STD="-preset myTranscoderPreset -crf $CRF -tune animation \
 		-preset medium -movflags +faststart"
 	export FF_EXT="-profile:v high -level 4.0"
@@ -629,6 +631,7 @@ while true; do
 
     -t <t>      ffmpeg encoding time limit
     --crf <#>   override CRF setting
+                    default: $DEFAULTS_OPTS_CRF
     --soft      force software decode and encode
     --psoft     use software encoding (allow hardware decode when available)
     --audiofix  transcode audio

From e7d6ce8de372c3f898faf5b01451351f95b500d0 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Sun, 19 Jul 2020 12:25:26 -0700
Subject: [PATCH 23/29] Changed FFMPEG default verbosity. Only shows progress
 now.

---
 burnSubs | 82 +++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 57 insertions(+), 25 deletions(-)

diff --git a/burnSubs b/burnSubs
index 7807ecc..9448490 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.11.3
+# version 0.12.0
 #################
 # Wishlist:
 #   queue encodes
@@ -428,17 +428,21 @@ function extractSubs() {
 function doTranscode() {
 	echo "=> Starting transcode:"
 	# shellcheck disable=SC2086
-	echo "$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
+	echo "$FFMPEG" ${FF_VERBOSITY} ${FF_HW} -i "${INPUT_VIDEO}" \
+		-sn ${LIM_TIME} \
 		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
-		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO}	\
+			${FILT_AUDIO} \
+		-c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO}	\
 		"${OUTPUT_VIDEO}"
 	if [[ "$OPTS_DRYRUN" == true ]]; then
 		return
 	fi
 	# shellcheck disable=SC2086
-	"$FFMPEG" ${FF_HW} -i "${INPUT_VIDEO}" -sn ${LIM_TIME} \
+	"$FFMPEG" ${FF_VERBOSITY} ${FF_HW} -i "${INPUT_VIDEO}" \
+		-sn ${LIM_TIME} \
 		-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
-		${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO}	\
+			${FILT_AUDIO} \
+		-c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO}	\
 		"${OUTPUT_VIDEO}"
 	export FINAL_STATUS=$?
 }
@@ -496,6 +500,7 @@ OPTS_DEBUG=false
 OPTS_LPF_AUDIO=false
 OPTS_TRANS_AUDIO=false
 OPTS_derived_NO_OUTPUT=false
+OPTS_VERBOSITY=1
 unset OPT_CRF
 # this is the --icon flag passed to notify-send at the end of the transcode
 NOTIFY_ICON="face-tired"
@@ -515,7 +520,8 @@ FINAL_STATUS=1
 
 ######
 # Reformat and organize the input strings
-OPT_STRING=$(getopt -o 'hkls:a:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:' -- "$@")
+OPT_STRING=$(getopt -o 'hkls:a:dt:vq' \
+	--long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:,verbose,quiet' -- "$@")
 # reassign them as positional arguments
 eval set -- "$OPT_STRING"
 
@@ -616,6 +622,22 @@ while true; do
 			shift
 			continue
 		;;
+		"--verbose"|"-v")
+			if [[ ${OPTS_VERBOSITY} -ne 0 ]]; then
+				OPTS_VERBOSITY=2
+				echo ">> !! print ffmpeg header."
+			else
+				echo ">> !! Ignoring verbosity flag. Quiet takes precidence."
+			fi
+			shift
+			continue
+		;;
+		"--quiet"|"-q")
+			OPTS_VERBOSITY=0
+			echo ">> !! print ffmpeg header."
+			shift
+			continue
+		;;
 		"--")
 			shift # all arguments parsed
 			break
@@ -627,28 +649,30 @@ while true; do
 			#echo "TODO: HELP!" # Display HELP
 			cat << _EOT
   ----------------------------------------------------------------------------
-    -k          auto-klobber when ffmpeg asks
+    -k              auto-klobber when ffmpeg asks
 
-    -t <t>      ffmpeg encoding time limit
-    --crf <#>   override CRF setting
-                    default: $DEFAULTS_OPTS_CRF
-    --soft      force software decode and encode
-    --psoft     use software encoding (allow hardware decode when available)
-    --audiofix  transcode audio
-    --lpf     transcode audio, and low-pass filter as well
+    -t <t>          ffmpeg encoding time limit
+    --crf <#>       override CRF setting
+                        default: $DEFAULTS_OPTS_CRF
+    --soft          force software decode and encode
+    --psoft         use software encoding (allow hardware decode when available)
+    --audiofix      transcode audio
+    --lpf           transcode audio, and low-pass filter as well
 
-    -l          list subtitles and audio tracks (no encoding)
-    -s <#>      select specific subtitle track number
-                #TODO: verify legal subtitle track number convention
-    -a <#>      select specific audio track number
-                #TODO: verify legal audio track number convention
-    --slocale 'eng'     select specific subtitle track number
-                        #TODO: verify legal subtitle track number convention
-    --alocale 'jpn'     select specific audio track number
-                        #TODO: verify legal audio track number convention
+    -l              list subtitles and audio tracks (no encoding)
+    -s <#>          select specific subtitle track number
+                    #TODO: verify legal subtitle track number convention
+    -a <#>          select specific audio track number
+                    #TODO: verify legal audio track number convention
+    --slocale 'eng' select specific subtitle track number
+                    #TODO: verify legal subtitle track number convention
+    --alocale 'jpn' select specific audio track number
+                    #TODO: verify legal audio track number convention
 
-    -d          debug (no cleanup)
-    --dry       dry run (no encoding)
+    -d              debug (no cleanup)
+    --dry           dry run (no encoding)
+    -q, --quiet     make ffmpeg shutup
+    -v, --verbose   show all FFMPEG details (except that ruddy header)
 _EOT
 			exit
 		;;
@@ -674,6 +698,14 @@ else
 	exit
 fi
 
+# Actual verbosity parsing
+if [[ ${OPTS_VERBOSITY} -le 0 ]]; then
+	FF_VERBOSITY="-hide_banner -loglevel error"
+elif [[ ${OPTS_VERBOSITY} -eq 1 ]]; then
+	FF_VERBOSITY="-hide_banner -loglevel error -stats"
+else # ie [[ ${OPTS_VERBOSITY} -ge 2 ]]; then
+	FF_VERBOSITY="-hide_banner"
+fi
 ###############
 # Configure the encoder based upon the hostname
 machineSetup

From c1e70ce736c80ca87ee84405a2d4cca7aa30a711 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Sat, 19 Sep 2020 16:11:11 -0700
Subject: [PATCH 24/29] added surround sound mixdown default.

---
 burnSubs | 68 ++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 51 insertions(+), 17 deletions(-)

diff --git a/burnSubs b/burnSubs
index 9448490..9838b67 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.12.0
+# version 0.13.0
 #################
 # Wishlist:
 #   queue encodes
@@ -66,20 +66,6 @@ function machineSetup() {
 			export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
 		fi
 	fi
-	
-	# Configure audio filtergraph if needed.
-	if [[ "${OPTS_TRANS_AUDIO}" == true ]]; then
-		FILT_AUDIO="-c:a aac"
-		if [[ "${OPTS_LPF_AUDIO}" == true ]]; then
-			FILT_AUDIO="-filter:a highpass=f=7 ${FILT_AUDIO}"
-		fi
-
-		if [[ "$(hostname)" == "Ram-the-Red" ]]; then
-			FILT_AUDIO="${FILT_AUDIO} -strict -2"
-		fi
-	else
-		FILT_AUDIO="-c:a copy"
-	fi
 
 	# Configure the encoder based upon the specific encoder chain required
 	## NVIDIA GPU Encode/Decode
@@ -296,10 +282,45 @@ function parseStreams() {
 	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass" or $item.codec_name == "dvd_subtitle") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition, codec:($item.codec_name)}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition,codec:$item.codec}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
 	export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
 	# shellcheck disable=SC2016
-	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_type == "audio") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${STREAMS_ALL}" > "${STREAMS_AUDIO}"
+	"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_type == "audio") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition, chan:{channels:$item.channels, channel_layout:$item.channel_layout}}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition, chan:$item.chan}])' "${STREAMS_ALL}" > "${STREAMS_AUDIO}"
 	export AUDIO_COUNT=$("$JQ" 'length' "${STREAMS_AUDIO}")
 	
 	
+}
+
+function setupAudioTranscode() {
+	if [[ $AUDIO_COUNT -ne 1 ]]; then
+		CHANNEL_COUNT=$($JQ '.[] | select(.i == '$AUDIO_INDEX') | .chan.channels' "$STREAMS_AUDIO")
+		CHANNEL_LAYOUT=$($JQ '.[] | select(.i == '$AUDIO_INDEX') | .chan.channel_layout' "$STREAMS_AUDIO")
+	else
+		CHANNEL_COUNT=$($JQ '.[] | .chan.channels' "$STREAMS_AUDIO")
+		CHANNEL_LAYOUT=$($JQ '.[] | .chan.channel_layout' "$STREAMS_AUDIO")
+	fi
+
+	if [[ "${CHANNEL_COUNT}" == "6" && "${CHANNEL_LAYOUT}" == '"5.1"' ]]; then
+		# check if we're 5.1 and if so flag transcode.
+		export OPTS_TRANS_AUDIO=true
+	elif [[ "${CHANNEL_COUNT}" != "2" && "${CHANNEL_LAYOUT}" != '"5.1"' ]]; then
+		echo "ERROR: Trying to enocde non 5.1 and non-stereo audio stream."
+		exit 1
+	fi
+	
+	# Configure audio filtergraph if needed.
+	if [[ "${OPTS_TRANS_AUDIO}" == true ]]; then
+		FILT_AUDIO="-c:a aac"
+		if [[ "${OPTS_LPF_AUDIO}" == true ]]; then
+			FILT_AUDIO="-filter:a highpass=f=7 ${FILT_AUDIO}"
+		fi
+		if [[ "${OPTS_SURROUND_PRESERVE}" == false ]]; then
+			# From https://superuser.com/questions/852400/properly-downmix-5-1-to-stereo-using-ffmpeg
+			# Nightmode Formula
+			FILT_AUDIO="-filter:a pan=stereo|FL<FC+0.30*FL+0.30*BL|FR<FC+0.30*FR+0.30*BR ${FILT_AUDIO}"
+		fi
+	else
+		FILT_AUDIO="-c:a copy"
+	fi
+
+
 }
 
 function listSubtitles() {
@@ -501,6 +522,7 @@ OPTS_LPF_AUDIO=false
 OPTS_TRANS_AUDIO=false
 OPTS_derived_NO_OUTPUT=false
 OPTS_VERBOSITY=1
+OPTS_SURROUND_PRESERVE=false
 unset OPT_CRF
 # this is the --icon flag passed to notify-send at the end of the transcode
 NOTIFY_ICON="face-tired"
@@ -521,7 +543,7 @@ FINAL_STATUS=1
 ######
 # Reformat and organize the input strings
 OPT_STRING=$(getopt -o 'hkls:a:dt:vq' \
-	--long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:,verbose,quiet' -- "$@")
+	--long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:,verbose,quiet,keep-surround' -- "$@")
 # reassign them as positional arguments
 eval set -- "$OPT_STRING"
 
@@ -597,6 +619,13 @@ while true; do
 			shift
 			continue
 		;;
+		"--keep-surround")
+			OPTS_SURROUND_PRESERVE=true
+			OPTS_TRANS_AUDIO=true
+			echo ">> !! preserving 5.1/7.1 surround sound if available."
+			shift
+			continue
+		;;
 		"--soft")
 			OPTS_FORCESOFT=true
 			echo ">> !! forcing software decoding/encoding."
@@ -657,6 +686,7 @@ while true; do
     --soft          force software decode and encode
     --psoft         use software encoding (allow hardware decode when available)
     --audiofix      transcode audio
+	--keep-surround	try to preserve surround sound rather than downmixing to stereo.
     --lpf           transcode audio, and low-pass filter as well
 
     -l              list subtitles and audio tracks (no encoding)
@@ -734,6 +764,10 @@ fi
 selectSubs
 # extract the selected subtitle file
 extractSubs $SUBTITLE_INDEX
+
+# Configure the audio straem
+setupAudioTranscode
+
 # Set the bitrate if that function wasn't disabled
 runExtraProc "h264_vaapi"
 

From 3547c0eaebe9b1161eb4a88d1ba68c9dad8bc562 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Sat, 19 Sep 2020 16:13:13 -0700
Subject: [PATCH 25/29] machine name tweak.

---
 burnSubs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/burnSubs b/burnSubs
index 9838b67..7aee242 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.13.0
+# version 0.13.1
 #################
 # Wishlist:
 #   queue encodes
@@ -57,7 +57,7 @@ function machineSetup() {
 			#export FFMPEG="/opt/ffmpeg-nvenc/bin/ffmpeg"
 			export LD_LIBRARY_PATH="/opt/ffmpeg-nvenc/lib"
 			export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
-		elif [[ "$(hostname)" == "grad-heo-lappy" ]]; then
+		elif [[ "$(hostname)" == "random-vaapi-intel-laptop" ]]; then
 			echo "   > Hostname: $(hostname)"
 			export OPTS_ENC="vaapi"
 		else

From 49fead9c6cdbd9758cbb6ee60cf47db061f922cd Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Sat, 19 Sep 2020 16:32:51 -0700
Subject: [PATCH 26/29] added readme, removed defunct dependancies.

---
 README.md | 42 ++++++++++++++++++++++++++++++++++++++++++
 burnSubs  |  8 ++++----
 2 files changed, 46 insertions(+), 4 deletions(-)
 create mode 100644 README.md

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..64907eb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,42 @@
+# burnSubs
+You found my burnSubs tool. The goal for this tool is to ease the
+conversion of arbitrary video files with soft-subtitles in the SSA/ASS
+Substation Alpha format into simple stereo hard-subtitled video files.
+
+## Features
+* softsub to hardsub conversion
+* embedded font files
+* selecting specific audio streams
+* selecting specific video streams
+* automatic selection of language preferences
+* default surround sound to stereo down-mixing
+  * opt out CLI flag available
+* anti-clobbering default behavior
+* auto-cleanup on error
+
+## Prerequisite Tools
+Firstly, the tool will yell at you if the tools it needs don't exist.
+Feel free to just run the tool, and it will let you know what you're
+missing.
+
+* [`ffmpeg`](https://ffmpeg.org/) - the one and only
+  * `ffprobe` - usually comes with ffmpeg
+* [`jq`](https://stedolan.github.io/jq/) - file/pipe based JSON processor
+
+
+## How does it work?
+`burnSubs` takes an input video file and tries to figure out what
+audio streams and subtitle streams exist within the file. It stores
+metadata in `/tmp` while it runs. When running it will pull the
+streams within the input file, and try to select Japanese language
+audio streams, and a non-signs subtitle stream (i.e. a full language
+subtitle stream) to add to the output video.
+
+Before transcoding it will then extract the subtitle file to pass into
+`ffmpeg`'s subtitle burn in filter, and will try to down-mix any
+surround sound input streams to stereo. Downmixing, track selection,
+clobbering behavior, and verbosity can all be controlled to a limited
+extent by CLI flags.
+
+## Can you make it do *XYZ*.
+Give me an enhancement request in github and I'll take a look.
\ No newline at end of file
diff --git a/burnSubs b/burnSubs
index 7aee242..3c29365 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.13.1
+# version 0.13.2
 #################
 # Wishlist:
 #   queue encodes
@@ -139,9 +139,9 @@ function setupBins() {
 	setAndValidateBin "ffmpeg" "FFMPEG"
 	setAndValidateBin "ffprobe" "FFPROBE"
 	setAndValidateBin "jq" "JQ"
-	setAndValidateBin "python3" "PYTHON3"
-	setAndValidateBin "awk" "AWK"
-	setAndValidateBin "date" "DATE"
+	#setAndValidateBin "python3" "PYTHON3"
+	#setAndValidateBin "awk" "AWK"
+	#setAndValidateBin "date" "DATE"
 }
 
 # Used by the above function to evaluate overrides if they are set, and

From 6945d8dba947706eb081633bfb6495ba9b9b12b6 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Sat, 18 Jun 2022 14:28:20 -0700
Subject: [PATCH 27/29] Bugfix for rejecting dubs and logical parenth error in
 multi-english sub configuration.

---
 burnSubs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/burnSubs b/burnSubs
index 3c29365..045b828 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.13.2
+# version 0.13.3
 #################
 # Wishlist:
 #   queue encodes
@@ -386,9 +386,9 @@ function selectSubs() {
 				# try to avoid a signs and lyrics track
 				LANG_SINGLE_SELECT=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
 				echo "Multiple english tracks found."
-				export SUBTITLE_INDEX=$($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and ( ($trk.t | test("lyrics";"i") or ($trk.t | test("signs";"i")) ) | not ) ) then [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].i' "$STREAMS_SUB")
+				export SUBTITLE_INDEX=$($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and (( ($trk.t | test("lyrics";"i")) or ($trk.t | test("signs";"i")) or ($trk.t | test("dub";"i")) )|not) ) then [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].i' "$STREAMS_SUB")
 				# And display rejected subtitles too.
-				SUBTITLE_REJECT_LIST=($($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and ( ($trk.t | test("lyrics";"i") or ($trk.t | test("signs";"i")) ) ) ) then [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].t' "$STREAMS_SUB"))
+				SUBTITLE_REJECT_LIST=($($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and (( ($trk.t | test("lyrics";"i")) or ($trk.t | test("signs";"i")) or ($trk.t | test("dub";"i")) )) ) techo hen [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].t' "$STREAMS_SUB"))
 				for REJECT_SUB in ${SUBTITLE_REJECT_LIST[@]}; do
 					echo "       > rejecting ${REJECT_SUB}"
 				done

From 2d05add839d5a1df8a5af147594c1208c9273714 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Sat, 19 Sep 2020 16:49:02 -0700
Subject: [PATCH 28/29] Added deubg making the output verbose.

---
 burnSubs | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/burnSubs b/burnSubs
index 3c29365..0713272 100755
--- a/burnSubs
+++ b/burnSubs
@@ -5,7 +5,7 @@ set -o errexit
 
 ################################################################################
 # burnSubs
-# version 0.13.2
+# version 0.13.3
 #################
 # Wishlist:
 #   queue encodes
@@ -13,8 +13,6 @@ set -o errexit
 #	finish help flag
 #	audio recode flag
 #
-# Changes
-#   automatically select JPN audio if more than one audio channel found.
 ################################################################################
 
 DEFAULTS_OPTS_CRF=20
@@ -175,6 +173,7 @@ function doCleanup() {
 		echo "=> Cleaning up."
 		rm -r "$TMP"
 	else
+		set +x
 		echo "tmp dir: $TMP"
 	fi
 	
@@ -314,7 +313,7 @@ function setupAudioTranscode() {
 		if [[ "${OPTS_SURROUND_PRESERVE}" == false ]]; then
 			# From https://superuser.com/questions/852400/properly-downmix-5-1-to-stereo-using-ffmpeg
 			# Nightmode Formula
-			FILT_AUDIO="-filter:a pan=stereo|FL<FC+0.30*FL+0.30*BL|FR<FC+0.30*FR+0.30*BR ${FILT_AUDIO}"
+			FILT_AUDIO="-filter:a pan=stereo|FL=FC+0.30*FL+0.30*BL|FR=FC+0.30*FR+0.30*BR ${FILT_AUDIO}"
 		fi
 	else
 		FILT_AUDIO="-c:a copy"
@@ -736,6 +735,11 @@ elif [[ ${OPTS_VERBOSITY} -eq 1 ]]; then
 else # ie [[ ${OPTS_VERBOSITY} -ge 2 ]]; then
 	FF_VERBOSITY="-hide_banner"
 fi
+
+if [[ "$OPTS_DEBUG" == "true" ]]; then
+	set -x
+fi
+
 ###############
 # Configure the encoder based upon the hostname
 machineSetup

From 30ea62f6497e90947e7d737bb60d22d092fd71d0 Mon Sep 17 00:00:00 2001
From: Luke <git@luke.fastmail.us>
Date: Tue, 18 Apr 2023 17:01:54 -0700
Subject: [PATCH 29/29] default CRF to 18

---
 burnSubs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/burnSubs b/burnSubs
index 7738dd3..0feca62 100755
--- a/burnSubs
+++ b/burnSubs
@@ -15,7 +15,7 @@ set -o errexit
 #
 ################################################################################
 
-DEFAULTS_OPTS_CRF=20
+DEFAULTS_OPTS_CRF=18
 
 function machineSetup() {
 	# Default setup