FFEncoder/ffencoder.sh
2018-09-19 02:14:15 +03:00

280 lines
7.7 KiB
Bash

#!/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 (<Width>x<Height>)"
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: <widht>x<height>"; 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="copy"
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")