From 19c97a0fa5cff81e92c6544c103b193f480539d9 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Tue, 18 Sep 2018 02:21:33 +0300 Subject: [PATCH] Create ffencoder.sh First version --- ffencoder.sh | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 ffencoder.sh diff --git a/ffencoder.sh b/ffencoder.sh new file mode 100644 index 0000000..e9892ec --- /dev/null +++ b/ffencoder.sh @@ -0,0 +1,273 @@ +#!/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" + +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 OnlyCopy=$(echo "$Info" | grep -Po '($CopyACodec)') + local ABitrate=$(echo "$Info" | sed -r 's/.+ ([0-9]+) kb\/s.*/\1/g') + + if [[ $ABitrate -lt 128 ]]; then + ABitrate="128" + fi + + if [[ -z "$OnlyCopy" ]]; then + echo "-acodec libvorbis -b:a ${ABitrate}k" + else + echo "-acodec copy" + fi +} + +# 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" + + 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")