#!/usr/bin/env bash
#
# Simple script to encode to H.264 video using FFmpeg and libx264.
# Author: Grozdan "microchip" Nikolov
# Version: 2.7.3
# Date: 2016-03-29
# License: GNU GPLv2+

green() { echo -e "\e[1;32m$1\e[0;39;49m"; }
brown() { echo -e "\e[0;33m$1\e[0;39;49m"; }
error() { echo -e "\e[1;31m$1\e[0;39;49m"; }

version="2.7.3"

case "$1" in
	-v)
	echo "$version"
	exit 0
	;;
esac

CFG="$HOME/.ffx264"
cfgversion="14"

genconfig_func() {
cat<<EOF>>"$CFG"
################# Config for ffx264 #################

# Version: $cfgversion - do not modify this line!

# Output directory
OUTDIR="$HOME"

# Container extension
CON="mkv"

# Nice value (0-19)
NICE="19"

# CRF value (0-51)
CRF="18"

# Set to "n" to disable cropping
AUTOCROP="y"

# Leave empty to encode at full (cropped)
# resolution, or set a software scaler
# fast_bilinear
# bilinear
# bicubic
# neighbor
# area
# bicublin
# gauss
# sinc
# lanczos
# spline
#
SCALER=""

# Set to "y" to enable deinterlacing
DEINTERLACE="n"

# Max amount of audio tracks to support
# Any non-zero value will do. Set it
# to "auto" to ask for as many as
# detected audio tracks
MAX_AUD_TRKS="2"

# Set to "n" to disable subtitles
# Note: not all containers support
# all types of subs!
SUBS="y"

# Max amount of subtitles to support
# Any non-zero value will do. Set it
# to "auto" to ask for as many as
# detected subtitles
MAX_SUBS="2"

# Set to "y" to enable metadata copy
METADATA="n"

# Set to "n" to disable chapters copy
CHAPS="y"

# Set MPlayer options
MPLAYERPARAMS="-noconfig all -loop 1"

# libx264 parameters. Modify, if needed,
# to fit your needs
X264PARAMS="force-cfr=1:bframes=6:keyint=240:min-keyint=24:ref=4:trellis=2:merange=24:direct-pred=auto:chroma-me=1:mbtree=1:me=umh:subme=10:b-adapt=2:aq-mode=1:aq-strength=1.05:psy=1:psy-rd=1.05,0.15:rc-lookahead=60:weightb=1:weightp=2:mixed-refs=1:b-pyramid=normal:fast-pskip=0:deblock=-1,-1:8x8dct=1:cabac=1:partitions=p8x8,b8x8,i8x8,i4x4:colorprim=bt709:transfer=bt709:colormatrix=bt709:threads=auto"

# Leave empty to auto-detect mplayer/ffmpeg/ffprobe
# or set your custom ones (eg, /path/to/bin/ffmpeg)
MPLAYER=""
FFMPEG=""
FFPROBE=""

#####################################################
EOF
}

brown "  __  __      ____   __   _  _ "
brown " / _|/ _|_  _|___ \ / /_ | || |"
brown "| |_| |_\ \/ / __) | '_ \| || |_"
brown "|  _|  _|>  < / __/| (_) |__   _|"
brown "|_| |_| /_/\_\_____|\___/   |_|"
brown "v$version"
echo

if [ -f "$CFG" ]; then
	cver=$(grep '^# Version' "$CFG" | awk '{print $3}')
	if [ "$cver" != "$cfgversion" ]; then
		rm -f "$CFG"
		genconfig_func
		error "Important notice! The configuration file of ffx264 located in"
		error "'$CFG' has been updated to the latest version!"
		echo
		error "If you have made any prior changes to the configuration file,"
		error "please commit those changes again and restart ffx264!"
		error "If no changes are made, just restart ffx264 to get rid of"
		error "this notice."
		echo
		exit 1
	else
		source "$CFG"
	fi
else
	genconfig_func
	green "-> Config file generated in '$CFG'"
	green "-> Please set things up in there, if needed"
	echo
	source "$CFG"
fi

if [ ! -z "$MPLAYER" ]; then
	if [ ! -x "$MPLAYER" ]; then
		error "-> MPlayer is missing from your system!"
		error "-> Check the config in '$CFG'"
		exit 1
	fi
else
	MPLAYER="$(which mplayer 2>/dev/null)"
	if [ ! -x "$MPLAYER" ]; then
		error "-> MPlayer is missing from your system!"
		exit 1
	fi
fi

if [ ! -z "$FFMPEG" ]; then
	if [ ! -x "$FFMPEG" ]; then
		error "-> ffmpeg is missing from your system!"
		error "-> Check the config in '$CFG'"
		exit 1
	fi
else
	FFMPEG="$(which ffmpeg 2>/dev/null)"
	if [ ! -x "$FFMPEG" ]; then
		error "-> ffmpeg is missing from your system!"
		exit 1
	fi
fi

if [ ! -z "$FFPROBE" ]; then
	if [ ! -x "$FFPROBE" ]; then
		error "-> ffprobe is missing from your system!"
		error "-> Check the config in '$CFG'"
		exit 1
	fi
else
	FFPROBE="$(which ffprobe 2>/dev/null)"
	if [ ! -x "$FFPROBE" ]; then
		error "-> ffprobe is missing from your system!"
		exit 1
	fi
fi

printf "Specify the Input File: "
read -e input
if [ ! -f "$input" ]; then
	error "-> No such file: '$input'"
	exit 1
fi

printf "Specify a Name for the Output: "
read -e output
if [ -z "$output" ]; then
	error "-> You have to provide a name for the output!"
	exit 1
else
	if [ ! -z "$(echo "$output" | grep '/')" ]; then
		mkdir -p "$(dirname "$output")" 2>/dev/null
		if [ $? != 0 ]; then
			error "-> Could not create output directory!"
			exit 1
		fi
		OUTPUT="$output.$CON"
		OUTDIR="$(dirname "$OUTPUT")"
	else
		OUTPUT="$OUTDIR/$output.$CON"
	fi
	METATITLE="-metadata title=\"$(basename "${OUTPUT%.*}")\""
fi

printf "Specify a Genre for the Content [press 'Enter' to skip]: "
read genre
if [ ! -z "$genre" ]; then
	METAGENRE="-metadata genre=\"$genre\""
fi

printf "Specify the Year of the Content [press 'Enter' to skip]: "
read year
if [ ! -z "$year" ]; then
	METAYEAR="-metadata year=\"$year\""
fi

printf "Specify the hqdn3d Video Denoise values (example: 3:2:4) [press 'Enter' to skip]: "
read denoise
if [ ! -z "$denoise" ]; then
	hqdn3d="hqdn3d=$denoise,"
fi

if [ "$DEINTERLACE" = "y" ]; then
	printf "Deinterlace the Input File? [y/N]: "
	read deint
	if [ "$deint" = "y" -o "$deint" = "Y" ]; then
		yadif="yadif,"
	fi
fi

if [ "$AUTOCROP" = "y" ]; then
	echo
	green "-> Detecting crop values..."
	CROPVAL="$($MPLAYER "$input" $MPLAYERPARAMS -vf cropdetect=24:4 -ao null -vo null -frames 500 -ss $((500/2)) -nocache 2>/dev/null | tr '\r' '\n' | grep "crop=" | tail -n 1 | awk '{print $9}' | sed 's|crop=||g; s|).||g')"
	green "-> Found crop values: $CROPVAL"
	echo
	
	printf "Specify the Crop Values [default is $CROPVAL]: "
	read cropval
	if [ -z "$cropval" ]; then
		crop="crop=$CROPVAL,"
	else
		crop="crop=$cropval,"
	fi
fi

if [ ! -z "$SCALER" ]; then
	printf "Specify the Desired Resolution [WxH - press 'Enter' to skip]: "
	read res
	if [ ! -z "$res" ]; then
		scale="scale=$res,"
		sws="-sws_flags $SCALER"
	fi
fi

################ Subs stuff #################

if [ "$SUBS" = "y" ]; then
	echo
	green "-> Detecting subtitles..."
	echo
	sleep 1
	SUBSINFO="$OUTDIR/.ffsubs$$"
	$FFPROBE -i "$input" > "$SUBSINFO" 2>&1
	SUBSDETECT="$(grep 'Stream #' "$SUBSINFO" | grep 'Subtitle' | sed 's|    ||g')"
	rm -f "$SUBSINFO"
	if [ ! -z "$SUBSDETECT" ]; then
		SUBT="$(echo "$SUBSDETECT" | wc -l)"
		green "$SUBSDETECT"
		echo
		error "-> Note: not all containers support all types of subs!"
	else
		error "-> Could not detect any subtitles!"
		SUBT="0"
	fi
	echo
	if [ "$MAX_SUBS" = "auto" ]; then
		if [ "$SUBT" = "0" ]; then
			MAX_SUBS="1"
		else
			MAX_SUBS="$SUBT"
		fi
	fi
	for i in $(eval echo "{1..$MAX_SUBS}"); do
		submapval[i]="$(($i-1))"
		printf "Sub $i: Specify a Subtitle to Copy (example: 0:2) [press 'Enter' to skip]: "
		read subnr[i]
		if [ ! -z "${subnr[i]}" ]; then
			printf "Sub $i: Specify the 3-letter Language Code for Metadata [press 'Enter' to skip]: "
			read slang[i]
			if [ ! -z "${slang[i]}" ]; then
				sublang[i]="-metadata:s:s:${submapval[i]} language=${slang[i]}"
			fi
			subcopy[i]="-map ${subnr[i]} -c:s:${submapval[i]} copy ${sublang[i]}"
		fi
	done
fi

test -z "${subcopy[*]}" && NOSUBS="-sn"

################ Audio stuff ################

echo
green "-> Detecting audio tracks..."
echo
sleep 1
AUDINFO="$OUTDIR/.ffaudtrks$$"
$FFPROBE -i "$input" > "$AUDINFO" 2>&1
AUDTRKS="$(grep 'Stream #' "$AUDINFO" | grep 'Audio' | sed 's|    ||g')"
rm -f "$AUDINFO"
if [ ! -z "$AUDTRKS" ]; then
	green "$AUDTRKS"
	ATRKS="$(echo "$AUDTRKS" | wc -l)"
else
	error "-> Could not detect any audio tracks!"
	ATRKS="0"
fi
echo

if [ "$MAX_AUD_TRKS" = "auto" ]; then
	if [ "$ATRKS" = "0" ]; then
		MAX_AUD_TRKS="1"
	else
		MAX_AUD_TRKS="$ATRKS"
	fi
fi

for i in $(eval echo "{1..$MAX_AUD_TRKS}"); do
	audmapval[i]="$(($i-1))"
	if [ "$i" = "1" -a "$ATRKS" != "0" ]; then
		defaudtrk="default is 0:1"
	else
		defaudtrk="press 'Enter' to skip"
	fi
	printf "Track $i: Specify the Audio Track to Encode or Copy [$defaudtrk]: "
	read astrm[i]
	if [ -z "${astrm[1]}" -a "$ATRKS" != "0" ]; then
		astrm[1]="0:1"
	fi
	if [ ! -z "${astrm[i]}" ]; then
		printf "Track $i: Specify the 3-letter Language Code for Metadata [press 'Enter' to skip]: "
		read alang[i]
		if [ ! -z "${alang[i]}" ]; then
			audlang[i]="-metadata:s:a:${audmapval[i]} language=${alang[i]}"
		fi
	fi
done

for i in $(eval echo "{1..$MAX_AUD_TRKS}"); do
	if [ ! -z "${astrm[i]}" ]; then
		echo
		audmap[i]="-map ${astrm[i]}"
		
		printf "Track $i: Specify the Audio Codec [AC3/DTS/AAC/OPUS/VORBIS/MP3/FLAC/COPY - default is AC3]: "
		read acodec[i]
		acodec[i]="$(echo "${acodec[i]}" | tr '[:upper:]' '[:lower:]')"
		case "${acodec[i]}" in
			ac3|"")
			acdc[i]="ac3"
			ameta[i]="AC-3"
			abropts[i]="192/384/448/640"
			abrdef[i]="640"
			;;
			dts)
			strict="-strict -2"
			acdc[i]="dca"
			ameta[i]="DTS"
			abropts[i]="755/1509"
			abrdef[i]="1509"
			;;
			aac)
			strict="-strict -2"
			acdc[i]="aac"
			ameta[i]="LC-AAC"
			abropts[i]="32-320"
			abrdef[i]="320"
			;;
			mp3)
			acdc[i]="libmp3lame"
			ameta[i]="MP3"
			abropts[i]="32-320"
			abrdef[i]="320"
			;;
			opus)
			acdc[i]="libopus"
			ameta[i]="Opus"
			abropts[i]="6-512"
			abrdef[i]="192"
			;;
			vorbis)
			acdc[i]="libvorbis"
			ameta[i]="Vorbis"
			abropts[i]="6-500"
			abrdef[i]="224"
			;;
			flac)
			acdc[i]="flac"
			ameta[i]="FLAC"
			;;
			copy)
			acdc[i]="copy"
			;;
			*)
			error "-> Unsupported audio codec: ${acodec[i]}"
			exit 1
			;;
		esac
		
		if [ "$(echo "$CON" | tr '[:upper:]' '[:lower:]')" != "mkv" ]; then
			case "${acodec[i]}" in
				opus|flac)
				error "-> $(echo "${acodec[i]}" | tr '[:lower:]' '[:upper:]') is only supported by the MKV container!"
				error "-> Check your config file in '$CFG'"
				exit 1
				;;
			esac
		fi
		
		audcodec[i]="-c:a:${audmapval[i]} ${acdc[i]}"
		
		if [ "${acodec[i]}" != "copy" ]; then
			audmeta[i]="-metadata:s:a:${audmapval[i]} title=${ameta[i]}"
			
			if [ "${acodec[i]}" != "flac" ]; then
				printf "Track $i: Specify the Audio Bitrate in kbps [${abropts[i]} - default is ${abrdef[i]}]: "
				read abr[i]
				if [ -z "${abr[i]}" ]; then
					abitrate[i]="${abrdef[i]}k"
				else
					abitrate[i]="${abr[i]}k"
				fi
				audbtr[i]="-b:a:${audmapval[i]} ${abitrate[i]}"
			fi
			
			if [ "${acodec[i]}" = "mp3" ]; then
				printf "Track $i: How many Channels to Decode? [1/2 - default is 2]: "
				read achan[i]
				if [ -z "${achan[i]}" ]; then
					ach[i]="2"
					chlayout[i]="stereo"
				else
					ach[i]="${achan[i]}"
					case "${ach[i]}" in
						1)	chlayout[i]="mono" ;;
						2)	chlayout[i]="stereo" ;;
						*)
						error "-> ${ach[i]} channels not supported!"
						exit 1
						;;
					esac
				fi
			else
				printf "Track $i: How many Channels to Decode? [1-8 - default is 6]: "
				read achan[i]
				if [ -z "${achan[i]}" ]; then
					ach[i]="6"
				else
					ach[i]="${achan[i]}"
				fi
				case "${ach[i]}" in
					1)	chlayout[i]="mono" ;;
					2)	chlayout[i]="stereo" ;;
					3)	chlayout[i]="3.0" ;;
					4)	chlayout[i]="quad(side)" ;;
					5)	chlayout[i]="5.0(side)" ;;
					6)	chlayout[i]="5.1(side)" ;;
					7)	chlayout[i]="6.1(front)" ;;
					8)	chlayout[i]="7.1(wide-side)" ;;
					*)
					error "-> ${ach[i]} channels not supported!"
					exit 1
					;;
				esac
				case "${acodec[i]}" in
					ac3|"")
					if [ "${ach[i]}" -gt "6" ]; then
						error "-> AC-3 supports only 1-6 channels!"
						exit 1
					fi
					;;
					dts)
					case "${ach[i]}" in
						1|2|4|5|6) true ;;
						*)
						error "-> DTS encoder in ffmpeg supports only 1, 2, 4, 5 and 6 channels!"
						exit 1
						;;
					esac
					;;
				esac
			fi
			audchan[i]="-ac:a:${audmapval[i]} ${ach[i]} -channel_layout:a:${audmapval[i]} \"${chlayout[i]}\""
			
			printf "Track $i: Normalize the Audio Volume? [y/N]: "
			read anorm[i]
			if [ "${anorm[i]}" = "y" -o "${anorm[i]}" = "Y" ]; then
				anormalize[i]="dynaudnorm=n=0,"
			fi
			
			if [ -z "${anormalize[i]}" ]; then
				printf "Track $i: Specify the Audio Volume filter value in dB [-100-100 - default is 4.0]: "
				read avol[i]
				if [ -z "${avol[i]}" ]; then
					avolume[i]="volume=4dB,"
				else
					avolume[i]="volume=${avol[i]}dB,"
				fi
			fi
			
			printf "Track $i: Resample the Audio? [y/N]: "
			read ares[i]
			if [ "${ares[i]}" = "y" -o "${ares[i]}" = "Y" ]; then
				printf "Track $i: Specify the Audio Resample Rate in Hertz [default is 48000]: "
				read arate[i]
				if [ -z "${arate[i]}" ]; then
					aresample[i]="aresample=48000,"
				else
					aresample[i]="aresample=${arate[i]},"
				fi
			fi
			
			audfilters[i]="-filter:a:${audmapval[i]} $(echo "${aresample[i]}${avolume[i]}${anormalize[i]}" | sed 's|,$||')"
		fi
		
		audparams[i]="${audmap[i]} ${audcodec[i]} ${audbtr[i]} ${audchan[i]} ${audfilters[i]} ${audlang[i]} ${audmeta[i]}"
	fi
done

if [ "$METADATA" = "n" ]; then
	MDATA="-map_metadata -1"
fi

if [ "$CHAPS" = "n" ]; then
	CHPS="-map_chapters -1"
fi

vidfilters="$(echo "$yadif$crop$hqdn3d$scale" | sed 's|,$||')"

if [ ! -z "$vidfilters" ]; then
	vfilters="-vf $vidfilters"
fi

# Small counter :)
counter_func() {
	for i in 5 4 3 2 1 0; do
		sleep 1 && echo -n "$i "
	done
}

# Set counter color
color_func() {
	BLUE="\033[01;34m"
	NORMAL="\e[0;0m"
	case "$1" in
		blue) printf "$BLUE" ;;
		normal) echo -ne "$NORMAL" ;;
	esac
}

echo
green "-> Starting to encode in CRF @ $CRF mode..."
green "-> Outputting to: $OUTPUT"
echo
color_func blue && printf "Starting to encode in: " && counter_func
color_func normal && echo

encoder_func() {
	echo "nice -n $NICE $FFMPEG -i \"$input\" -map 0:0 $NOSUBS $MDATA $CHPS $METATITLE $METAGENRE $METAYEAR $vfilters $sws -c:v libx264 -x264opts crf=$CRF:$X264PARAMS ${audparams[*]} ${subcopy[*]} $strict \"$OUTPUT\""
}

SHOUT="$(dirname "$OUTPUT")/$(echo "$(basename "${OUTPUT%.*}")" | sed 's| |_|g').sh"

echo "#!/usr/bin/env bash" > "$SHOUT"
echo "" >> "$SHOUT"
encoder_func >> "$SHOUT"
echo "" >> "$SHOUT"
chmod +x "$SHOUT"

source "$SHOUT"

exit $?
