#!/bin/bash # Script for mass video encoding with ffmpeg DEF='\e[0m'; RED='\e[31m'; GRN='\e[32m'; WHT='\e[37m'; BLD='\e[1m'; ScriptName=$(echo "$0" | awk -F '/' '{print $NF;}') ScriptDir=$(dirname $(readlink -e "$0")) ExtRegex='.*\(mkv\|mp4\|wmv\|avi\)' RateAbbr="ntsc|pal|qntsc|qpal|sntsc|spal|film|ntsc-film" CopyACodec="FLAC|vorbis|flac" FFmpeg=$(which 'ffmpeg') Wrapper=$(which 'x264-10bit') Priority="" Nice="" Jobs="" WriteLog="" LogDir="Logs" Log="/dev/null" Resolution="" OutputDir="" InputDir="" BitDepth="" Bitrate="" Audio="" VCodec="" Format="" Fps="" view_help () { echo "Usage: ./$ScriptName [KEY]... [ARG]..." echo "This is script for mass video encoding to libx264/mkv with custom bitdepth, bitrate, resolution and fps." echo "-i, --input set the input directory (required)" echo "-o, --output set output directory (required)" echo "-d, --depth set bit depth (8 or 10)" echo "-r, --resolution set resolution (x)" echo "-f, --fps set fps (\"http://ffmpeg.org/ffmpeg-all.html#Video-rate\")" echo "-b, --bitrate set bitrate (integer value with \"k\" or \"m\" postfix)" echo "-p, --priority set priority (integer value from -19 to 20. Transferred to \"nice -n\")" echo "-l, --log creates a directory with logs in output directory" echo "-h, --help shows this page and exit" exit 1 } check_args () { local Errors="" # InputDir if [[ -z "$InputDir" ]]; then echo -e "${BLD}${RED}Error:${DEF} InputDir not set."; Errors="True" elif [[ -d "$InputDir" ]]; then InputDir=$(readlink -e "$InputDir") if [[ $(find "$InputDir" -maxdepth 1 -type f -regex "$ExtRegex" | wc -l) == '0' ]]; then echo -e "${BLD}${RED}Error:${DEF} $InputDir does not contain any video files."; Errors="True" fi else echo -e "${BLD}${RED}Error:${DEF} $Input not a directory."; Errors="True" fi # OutputDir if [[ -z "$OutputDir" ]]; then echo -e "${BLD}${RED}Error:${DEF} OutputDir not set."; Errors="True" elif [[ -d "$OutputDir" ]]; then OutputDir=$(readlink -e "$OutputDir") elif ! mkdir -p "$OutputDir"; then echo -e "${BLD}${RED}Error:${DEF} Can't create ${OutputDir}!"; Errors="True" else OutputDir=$(readlink -e "$OutputDir") fi # BitDepth if [[ (! -z "$BitDepth") && ("$BitDepth" != '8' && "$BitDepth" != '10') ]]; then echo -e "${BLD}${RED}Error:${DEF} BitDepth must be '8' or '10', not \"${BitDepth}\"."; Errors="True" fi # Resolution if ! echo "$Resolution" | grep -Po '^[0-9]+x[0-9]+$' &> /dev/null && [[ ! -z $Resolution ]]; then echo -e "${BLD}${RED}Error:${DEF} Unknown resolution format: \"$Resolution\".\n\tMust be: x"; Errors="True" fi # Fps if ! echo "$Fps" | grep -Po "^([0-9]+(/[0-9]+)?|${RateAbbr})$" &> /dev/null && [[ ! -z $Fps ]]; then echo -e "${BLD}${RED}Error:${DEF} Unknown fps format: \"$Fps\".\n\tIt has to be a string in the format frame_rate_num/frame_rate_den, an integer number, a float number or a valid video frame rate abbreviation"; Errors="True" fi # Bitrate if ! echo "$Bitrate" | grep -Po '^[0-9]+(k|m)?$' &> /dev/null && [[ ! -z $Bitrate ]]; then echo -e "${BLD}${RED}Error:${DEF} Unknown bitrate format: \"$Bitrate\".\n\tMust be integer value (with 'k' or 'm' postfix)."; Errors="True" fi # Priority if [[ ! -z "$Priority" ]]; then if ! echo "$Priority" | grep -P '[-0-9]{1,3}' &> /dev/null; then echo -e "${BLD}${RED}Error:${DEF} Unknown priority: \"$Priority\".\n\tMust be integer value from -19 to 20."; Errors="True" elif [[ "$Priority" -lt "-19" || "$Priority" -gt "20" ]]; then echo -e "${BLD}${RED}Error:${DEF} Priority must be from -19 to 20."; Errors="True" else Nice="nice -n $Priority" fi fi # Jobs #if [[ ! -z "$Jobs" ]]; then # if ! echo "$Jobs" | grep -P '^[0-9]+$' &> /dev/null; then # echo -e "${BLD}${RED}Error:${DEF} Unknown jobs count: \"$Jobs\".\n\tMust be integer value from 1 to 99."; Errors="True" # elif [[ "$Jobs" -lt "1" ]]; then # echo -e "${BLD}${RED}Error:${DEF} Jobs count must be integer value from 1 to 99."; Errors="True" # else # Jobs="-threads $Jobs" # fi #fi # VCodec/Format VCodec="libx264" Format="matroska" if [[ ! -z $Errors ]]; then exit 3; fi } auto_exec () { local Info="$1" local Depth="8" local Exec="$FFmpeg" if [[ -z "$BitDepth" && ! -z $(echo "$Info" | grep -Po 'High 10') ]]; then Depth='10' else Depth="$BitDepth" fi if [[ "$Depth" == "10" ]]; then Exec="$Wrapper $Exec" fi echo "$Exec" } auto_resolution () { local Info="$1" if [[ -z "$Resolution" ]]; then echo "-s "$(echo "$Info" | sed -r 's/.+ ([0-9]+x[0-9]+)[, ].+/\1/g') else echo "-s $Resolution" fi } auto_fps () { local Info="$1" if [[ -z "$Fps" ]] && echo "$Info" | grep -P '[0-9\.] fps' &> /dev/null; then Fps=$(echo "$Info" | sed -r 's/.+ ([0-9\.]+) fps.+/\1/g') fi if [[ ! -z "$Fps" ]]; then Fps="-r $Fps" fi echo "$Fps" } auto_bitrate () { local Info="$1" if [[ -z "$Bitrate" ]]; then echo "-b:v "$(echo "$Info" | sed -r 's/.+bitrate: ([0-9]+) ([kmg]?).+/\1\2/g') else echo "-b:v $Bitrate" fi } auto_audio () { local Info="$1" local Arg="-acodec" local Codec="codec" local Bitrate="" if ! echo "$Info" | grep -Po "($CopyACodec)"; then Codec="libvorbis" Bitrate=$(echo "$Info" | sed -r 's/.+ ([0-9]+) kb\/s.*/\1/g' | grep -Po '[0-9]+ kb/s') if ! [[ -z $Bitrate ]]; then if [[ $Bitrate -lt 128 ]]; then Bitrate="128" fi Bitrate="-b:a ${Bitrate}k" fi fi echo "$Arg $Codec $Bitrate" } # Check FFmpeg and Wrapper if [[ -z "$FFmpeg" ]]; then echo -e "${BLD}${RED}Error:${DEF} ffmpeg not found."; exit 1 fi if [[ -z "$Wrapper" ]]; then echo -e "${BLD}${RED}Error:${DEF} 10bit wrapper not found."; exit 1 fi # Processing arguments while [[ $# -gt 0 ]]; do case $1 in -i|--input ) InputDir="$2" ; shift; shift;; -o|--output ) OutputDir="$2" ; shift; shift;; -d|--depth ) BitDepth="$2" ; shift; shift;; -r|--resolution ) Resolution="$2" ; shift; shift;; -f|--fps ) Fps="$2" ; shift; shift;; -b|--bitrate ) Bitrate="$2" ; shift; shift;; -p|--priority ) Priority="$2" ; shift; shift;; -l|--log ) WriteLog="True" ; shift;; -h|--help ) view_help ; shift;; # -j|--jobs ) Jobs="$2" ; shift; shift;; # -a|--acodec ) ACodec="$2" ; shift; shift;; # -v|--vcodec ) VCodec="$2" ; shift; shift;; *) echo "Unknown argument: \"$1\""; exit 2;; esac; done; check_args LogDir="$OutputDir/$LogDir" if [[ ! -z $WriteLog && ! -d "$LogDir" ]]; then mkdir "$LogDir" fi # Main Index='1' Count=$(find "$InputDir" -maxdepth 1 -type f -regex "$ExtRegex" | wc -l) while IFS= read -r -d $'\n' InputFile <&3 do InputFileName=$(echo "$InputFile" | awk -F '/' '{print $NF;}') InputFileNameCrop=$(echo "$InputFileName" | sed -r 's/\.[a-Z0-9]+$//g') printf "${BLD}[----] %#3d/%-3d${DEF} %s" "$Index" "$Count" "$InputFileNameCrop" OutputFile="$OutputDir/$InputFileNameCrop.mkv" if [[ ! -z $WriteLog ]]; then Log="$LogDir/$InputFileNameCrop.log" fi InfoP1=$("$FFmpeg" -i "$InputFile" 2>&1 | grep 'Video:') InfoP2=$("$FFmpeg" -i "$InputFile" 2>&1 | grep 'bitrate') InfoP3=$("$FFmpeg" -i "$InputFile" 2>&1 | grep -P 'Stream #0:1.+Audio:') Exec=$(auto_exec "$InfoP1") # Uncomment for fast debug: # VCodec="copy" # For metadata: # -metadata:s:v language="jpn" -metadata:s:a language="jpn" \ if $Nice $Jobs $Exec \ -i "$InputFile" \ -f "$Format" \ -vcodec "$VCodec" \ $(auto_audio "$InfoP3") \ $(auto_resolution "$InfoP1") \ $(auto_bitrate "$InfoP2") \ $(auto_fps "$InfoP3") \ -y "$OutputFile" &> "$Log"; then printf "\r${BLD}[${GRN}DONE${DEF}${BLD}]\n" else printf "\r${BLD}[${RED}FAIL${DEF}${BLD}]\n" #break fi ((Index++)) done 3< <(find "$InputDir" -maxdepth 1 -type f -regex "$ExtRegex")