#!/bin/bash # Check if running on a mac if [ "$(uname)" == "Darwin" ];then isMac=true else isMac=false fi # Check if bash is version 4+ bashVersion=$(bash --version | grep -Eo "version [0-9]+\." | grep -o "[0-9]*") if [[ $bashVersion -lt 4 ]]; then echo "Your bash is out of date, you must use at least version 4."; echo "Your current bash major version is $bashVersion. Please update." if $isMac; then echo "Since you are on mac, it is recommened you use homebrew or fink to update your bash. See https://github.com/FallingSnow/h265ize/issues/6#issuecomment-158991841 for more info." fi exit 102 fi # All of these preset do not need to be chanded, the script will work (in almost all cases) with all defaults declare -A defaults defaults+=([destination]="/hd/01/media/encoding") # (NO TRAILING SLASH) Folder to output files to; default: $HOME/h265 defaults+=([quality]=25) # 0-51; Sets the qp quality target; default: 19 defaults+=([vbr]=0) # Sets the video bitrate, set to 0 to use qp instead of a target bitrate; default: 0 defaults+=([preset]="medium") # x265 encoder preset; Options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo; default: fast defaults+=([nativeLangCode]="eng") # Examples: eng, fre, spa, dut, et cetera; default: eng defaults+=([accurateTimestamps]=0) # Accurate Timestamps (substantially increases file size but sometimes fixes timestamps); default: 0 defaults+=([tempName]="encoding-in-process") # Temporary name of the new unfinished file; default: encoding-in-process defaults+=([outputFormat]="mkv") # Container format to output; Options: mkv, mp4, m4v; default: mkv; NOTE: If you use mp4 and intend to encode to larger than 4GB, you must add the --large-file option to the QUERY variable. defaults+=([tempDir]="/hd/01/media/encoding") # (NO TRAILING SLASH) Directory where new unfinished file is stored; default: $HOME/h265 defaults+=([previewLength]=30) # Seconds to be encoded in preview mode; default: 30 defaults+=([timeDiffLimit]=1) # Seconds in length that the output is allowed to differ from the original source; A 3 hour movie usually differs by about .2 seconds; Integers ONLY; default: 1 (Allows output up to 1 second longer or shorter) defaults+=([extraOptions]="") # Extra options; default: [empty] defaults+=([heaudio]=0) # 0-1; High Efficiency audio mode; default 0 defaults+=([copyaudio]=0) # 0-1; Don't encode the audio streams, just copy them; default 0 defaults+=([handbrakecli]="HandBrakeCLI") # handbrakecli command; this is the default handbrake installation; this encodes in 8bit only; default: HandBrakeCLI defaults+=([handbrakecli10bit]="HandBrakeCLI10bit") # handbrakecli command for 10bit encodes; default: HandBrakeCLI10bit defaults+=([verbose]=0) # 0-1; Verbose Mode; Prints extra output; I actually spent a lot of time on this feature, you should use it; default: 0 defaults+=([preview]=0) # 0-1; Preview Mode; Only outputs a video sigment with the specified previewLength; default: 0 defaults+=([override]=0) # 0-1; Override Mode; Allows videos already encoded with the h265 codec to be re-encoded; default: 0 defaults+=([upconvert]=1) # 0-1; Upconvert Mode; Converts vob/dvd subs to srt format; Only works with mkv's; default: 1 defaults+=([stats]=1) # 0-1; Generate stats file located at [destination]/h265ize-[date-time].stats; default: 0 defaults+=([debug]=0) # 0-1; Debug Mode; Print extra debugging information; default: 0 defaults+=([smart]=0) # 0-1; Smart Mode; This isn't ready and doesnt work, please don't enable this feature; default: 0 defaults+=([delete]=1) # 0-1; Delete source after encoding is complete; STRONGLY NOT RECOMMENED; defualt: 0 defaults+=([depth]=4) # 0-INFINITY; How deap the search for files should go in subdirectories; default: 2 defaults+=([parallel]=0) # https://github.com/FallingSnow/h265ize#commercial-interests defaults+=([logFile]="") # Do not change unless you are on a mac getopt="getopt" # Contains the location of the getopt command; ex: /usr/local/XXXXX/gnu-getopt/1.1.6/bin/getopt # DO NOT TOUCH BELOW THIS LINE UNLESS YOU KNOW BASH ################################################################################ ################################################################################ declare -A options # Colors green='\033[0;32m' red='\033[0;31m' yellow='\033[0;33m' lightb='\033[1;34m' cyan='\033[0;36m' NC='\033[0m' # No Color # End Colors package="h265ize" description="Convert videos into x265 videos." version="0.2.3" commandDesciptions=" -a :Accurate Timestamps (substantially increases file size but sometimes fixes timestamps) -d :(NO TRAILING SLASH) Folder to output files to; default: $HOME/h265 -f :Container format to output; Options: mkv, mp4, m4v; default: mkv; NOTE: If you use mp4 and intend to encode to larger than 4GB, you must add the --large-file option to the QUERY variable. -g :(NO TRAILING SLASH) Directory where new unfinished file is stored -l :Seconds to be encoded in preview mode; default: 30 -m :x265 encoder preset; Options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo; default: fast -n :Your native language; Examples: eng, fre, spa, dut; default: eng -o :Override mode; Allows conversion of videos that are already encoded by the hevc codec -p :Preview mode; Only processes the first ${defaults[previewLength]} seconds -q :0-51; Sets the qp quality target; default: 19 -t :Temporary name of the new unfinished file -u :Disable Upconvert; Stop converting Vobsub subs to srt; Only works with mkv's -v :Verbose mode; Display extra output -x :Extra x265 options -h :Help; Shows this help page --delete : Delete source after encoding is complete; STRONGLY NOT RECOMMENED --depth :How deap the search for files should go in subdirectories; default: 2 --debug :Debug mode; Print extra debugging information --video-bitrate :Sets the video bitrate, set to 0 to use qp instead of a target bitrate --he-audio :High Efficiency audio mode --copy-audio :Don't encode the audio streams, just copy them --aspreset :My personal presets; Possible values are listed below; I'll be adding more as time goes on > finalCut: Uses the slow preset and allows QP to shift between 19 and 23 > animeHigh: Changes some advanced options to provide the lowest possible file size while still maintaining quality; Caution these are high latency encodes --help :Help; Shows this help page " how="Usage: $0 [-h(help)] [-d ] [-q <0|51>] [-m ] [-n {3}] [-t ] [-f {3}] [-g ] [-l ] [-a] [-o] [-p] [-u] [-v] [--debug] [--aspreset ] [--depth ] [--video-bitrate ] [--delete] )" exclusivelySupportedExtensions="'*.mts' -o -iname '*.m2ts' -o -iname '*.mkv' -o -iname '*.m4v'" # Each extension should be seperated by -o -iname; example: '*.mkv' -o -iname '*.mp4' usage() { echo $how 1>&2 exit 0 } helps() { echo "$package Version: $version" echo "Description: $description" 1>&2 echo $how 1>&2 echo "Commands:$commandDesciptions" 1>&2 exit 0 } debug(){ if [ ${options[debug]} -eq 1 ]; then echo -e "${green}[$package]${NC}: ${cyan}[Debug]${NC} $1"; fi } warn() { echo -e "${yellow}[$package]$NC: $1" } verbose(){ if [ ${options[verbose]} -eq 1 ]; then echo -e "${1}[$package]${NC}: ${lightb}[Verbose]${NC} $2"; fi } error() { echo -e "${red}[$package]$NC: $1" } log() { echo -e "${green}[$package]$NC: $1" } replace() { echo -ne "\r\e[K$1" } # Test getopt if [ $($getopt --test; echo $?) -ne 4 ]; then error "You have a bad getopt version! Please update getopt." exit 74 fi # Check if ffmpeg is installed hash "ffmpeg" 2>/dev/null || { error "ffmpeg is not installed. ffprobe must be installed!"; exit 5; } # Check if ffmpeg is installed hash "ffprobe" 2>/dev/null || { error "ffprobe is not installed. ffprobe must be installed!"; exit 6; } # Check if bc is installed hash "bc" 2>/dev/null || { error "bc is not installed. bc must be installed!"; exit 9; } flags=$($getopt -o :d:q:m:n:t:f:g:l:x:vphaou -l aspreset:,stats,video-bitrate:,depth:,he-audio,copy-audio,debug,delete,help -- "$@") eval set -- "$flags" while [ $# -gt 0 ]; do case "$1" in -a) options[accurateTimestamps]=1 ;; -d) options[destination]="$2"; shift ;; -f) options[outputFormat]="$2"; shift ;; -g) options[tempDir]="$2"; shift ;; -l) options[previewLength]="$2"; shift ;; -m) options[preset]="$2"; shift ;; -n) options[nativeLangCode]="$2"; shift ;; -o) options[override]=1 ;; -p) options[preview]=1 ;; -q) options[quality]="$2"; shift ;; # -s) # options[smart]=1 # ;; -t) options[tempName]="$2"; shift ;; -u) options[upconvert]=0 ;; -v) options[verbose]=1 ;; -x) options[extraOptions]="$2"; shift ;; --aspreset) options[aspreset]="$2"; shift ;; --logfile) options[logFile]="$2"; shift ;; --stats) options[stats]=1 ;; --delete) warn "What are you crazy? What if this encode turns out like trash? You sure you really want to delete the source after you have finished encoding? STRONGLY NOT RECOMMENED [y/N]: " while true; do read yn case $yn in [Yy]* ) options[delete]=1; break;; [Nn]* ) options[delete]=0; break;; * ) options[delete]=0; break;; esac done ;; --debug) options[debug]=1 ;; --depth) options[depth]="$2"; shift ;; --video-bitrate) options[vbr]="$2"; shift ;; --he-audio) options[heaudio]=1 ;; --copy-audio) options[copyaudio]=1 ;; # --smart) # options[smart]=1 # ;; # --multi-pass) # options[multipass]=1 # ;; -h) helps ;; --help) helps ;; \?) usage ;; --) shift; break;; *) break;; esac shift done # Check if a aspreset has been enabled if [ -n "${options[aspreset]}" ]; then case "${options[aspreset]}" in "finalCut") defaults[quality]=21;defaults[preset]="medium";defaults[extraOptions]="aq-mode=1:aq-strength=2.0" # Allows for a Constant QP shift between 19-23 ;; "animeHigh") defaults[quality]=19;defaults[preset]="medium";defaults[extraOptions]="weightb=1:bframes=11:bframe-bias=90:rc-lookahead=60:me=dia:max-merge=5:rect:ctu=64:b-adapt=2:tu-inter-depth=4:tu-intra-depth=4:ipratio=0.8" #ref=6:subme=4 ;; "animeLow") defaults[quality]=25;defaults[preset]="medium";defaults[extraOptions]="aq-mode=1:aq-strength=1.5:bframes=8:weightb=1:bframe-bias=95:rc-lookahead=60:keyint=1200:me=star:max-merge=5:rect:ctu=64:b-adapt=2:tu-inter-depth=4:tu-intra-depth=4:ipratio=0.6" ;; "perfect") defaults[quality]=19;defaults[preset]="medium";defaults[extraOptions]="bframes=14" ;; "testing") defaults[quality]=21;defaults[preset]="medium";defaults[extraOptions]="aq-mode=1:aq-strength=1.5:allow-non-conformance:ref=16:bframes=16:b-adapt=2:bframe-bias=100:rc-lookahead=240:scenecut=150:max-merge=5:subme=5:me=hex:limit-refs=2:b-intra:weightb:rd=6" ;; *) error "Unknown aspreset ${options[aspreset]}." exit 101 esac fi # Merge options with defaults for index in "${!defaults[@]}";do if [ ! -n "${options[$index]}" ];then options[$index]="${defaults[$index]}" fi done # Debug debug "Options $(eval "printf '%0.s-' {1..$(($(tput cols) - 28))}")" for index in "${!options[@]}"; do debug " key : $index" debug "value : ${options[$index]}" done if [[ $EUID -eq 0 ]] && [[ ${options[delete]} -eq 1 ]]; then error "This script may NOT run as root with the delete option enabled." exit 403 fi # Make sure output directory exists if [[ ! -d "${options[destination]}" ]]; then warn "Output destination \"${options[destination]}\" does not exist. Would you like to create it? [y/N]: " while true; do read yn case $yn in [Yy]* ) mkdir -p "${options[destination]}"; break;; [Nn]* ) exit 2;; * ) exit 2;; esac done fi # Make sure temp output directory exists if [[ ! -d "${options[tempDir]}" ]]; then warn "Temporary output destination \"${options[tempDir]}\" does not exist. Would you like to create it? [y/N]: " while true; do read yn case $yn in [Yy]* ) mkdir -p "${options[tempDir]}"; break;; [Nn]* ) exit 2;; * ) exit 2;; esac done fi options[watchInterval]=$(echo "scale=3; x=${options[watchInterval]}/4; if(x<1) print 0; x" | bc) declare -A ICO3TONAME ICO3TONAME=(["aar"]="Afar" ["abk"]="Abkhazian" ["afr"]="Afrikaans" ["aka"]="Akan" ["alb"]="Albanian" ["amh"]="Amharic" ["ara"]="Arabic" ["arg"]="Aragonese" ["arm"]="Armenian" ["asm"]="Assamese" ["ava"]="Avaric" ["ave"]="Avestan" ["aym"]="Aymara" ["aze"]="Azerbaijani" ["bak"]="Bashkir" ["bam"]="Bambara" ["baq"]="Basque" ["bel"]="Belarusian" ["ben"]="Bengali" ["bih"]="Bihari languages" ["bis"]="Bislama" ["bos"]="Bosnian" ["bre"]="Breton" ["bul"]="Bulgarian" ["bur"]="Burmese" ["cat"]="Catalan; Valencian" ["cha"]="Chamorro" ["che"]="Chechen" ["chi"]="Chinese" ["chu"]="Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic" ["chv"]="Chuvash" ["cor"]="Cornish" ["cos"]="Corsican" ["cre"]="Cree" ["cze"]="Czech" ["dan"]="Danish" ["div"]="Divehi; Dhivehi; Maldivian" ["dut"]="Dutch; Flemish" ["dzo"]="Dzongkha" ["eng"]="English" ["epo"]="Esperanto" ["est"]="Estonian" ["ewe"]="Ewe" ["fao"]="Faroese" ["fij"]="Fijian" ["fin"]="Finnish" ["fre"]="French" ["fry"]="Western Frisian" ["ful"]="Fulah" ["geo"]="Georgian" ["ger"]="German" ["gla"]="Gaelic; Scottish Gaelic" ["gle"]="Irish" ["glg"]="Galician" ["glv"]="Manx" ["gre"]="Greek, Modern (1453-)" ["grn"]="Guarani" ["guj"]="Gujarati" ["hat"]="Haitian; Haitian Creole" ["hau"]="Hausa" ["heb"]="Hebrew" ["her"]="Herero" ["hin"]="Hindi" ["hmo"]="Hiri Motu" ["hrv"]="Croatian" ["hun"]="Hungarian" ["ibo"]="Igbo" ["ice"]="Icelandic" ["ido"]="Ido" ["iii"]="Sichuan Yi; Nuosu" ["iku"]="Inuktitut" ["ile"]="Interlingue; Occidental" ["ina"]="Interlingua (International Auxiliary Language Association)" ["ind"]="Indonesian" ["ipk"]="Inupiaq" ["ita"]="Italian" ["jav"]="Javanese" ["jpn"]="Japanese" ["kal"]="Kalaallisut; Greenlandic" ["kan"]="Kannada" ["kas"]="Kashmiri" ["kau"]="Kanuri" ["kaz"]="Kazakh" ["khm"]="Central Khmer" ["kik"]="Kikuyu; Gikuyu" ["kin"]="Kinyarwanda" ["kir"]="Kirghiz; Kyrgyz" ["kom"]="Komi" ["kon"]="Kongo" ["kor"]="Korean" ["kua"]="Kuanyama; Kwanyama" ["kur"]="Kurdish" ["lao"]="Lao" ["lat"]="Latin" ["lav"]="Latvian" ["lim"]="Limburgan; Limburger; Limburgish" ["lin"]="Lingala" ["lit"]="Lithuanian" ["ltz"]="Luxembourgish; Letzeburgesch" ["lub"]="Luba-Katanga" ["lug"]="Ganda" ["mac"]="Macedonian" ["mah"]="Marshallese" ["mal"]="Malayalam" ["mao"]="Maori" ["mar"]="Marathi" ["may"]="Malay" ["mlg"]="Malagasy" ["mlt"]="Maltese" ["mon"]="Mongolian" ["nau"]="Nauru" ["nav"]="Navajo; Navaho" ["nbl"]="Ndebele, South; South Ndebele" ["nde"]="Ndebele, North; North Ndebele" ["ndo"]="Ndonga" ["nep"]="Nepali" ["nno"]="Norwegian Nynorsk; Nynorsk, Norwegian" ["nob"]="Bokmål, Norwegian; Norwegian Bokmål" ["nor"]="Norwegian" ["nya"]="Chichewa; Chewa; Nyanja" ["oci"]="Occitan (post 1500); Provençal" ["oji"]="Ojibwa" ["ori"]="Oriya" ["orm"]="Oromo" ["oss"]="Ossetian; Ossetic" ["pan"]="Panjabi; Punjabi" ["per"]="Persian" ["pli"]="Pali" ["pol"]="Polish" ["por"]="Portuguese" ["pus"]="Pushto; Pashto" ["que"]="Quechua" ["roh"]="Romansh" ["rum"]="Romanian; Moldavian; Moldovan" ["run"]="Rundi" ["rus"]="Russian" ["sag"]="Sango" ["san"]="Sanskrit" ["sin"]="Sinhala; Sinhalese" ["slo"]="Slovak" ["slv"]="Slovenian" ["sme"]="Northern Sami" ["smo"]="Samoan" ["sna"]="Shona" ["snd"]="Sindhi" ["som"]="Somali" ["sot"]="Sotho, Southern" ["spa"]="Spanish; Castilian" ["srd"]="Sardinian" ["srp"]="Serbian" ["ssw"]="Swati" ["sun"]="Sundanese" ["swa"]="Swahili" ["swe"]="Swedish" ["tah"]="Tahitian" ["tam"]="Tamil" ["tat"]="Tatar" ["tel"]="Telugu" ["tgk"]="Tajik" ["tgl"]="Tagalog" ["tha"]="Thai" ["tib"]="Tibetan" ["tir"]="Tigrinya" ["ton"]="Tonga (Tonga Islands)" ["tsn"]="Tswana" ["tso"]="Tsonga" ["tuk"]="Turkmen" ["tur"]="Turkish" ["twi"]="Twi" ["uig"]="Uighur; Uyghur" ["ukr"]="Ukrainian" ["urd"]="Urdu" ["uzb"]="Uzbek" ["ven"]="Venda" ["vie"]="Vietnamese" ["vol"]="Volapük" ["wel"]="Welsh" ["wln"]="Walloon" ["wol"]="Wolof" ["xho"]="Xhosa" ["yid"]="Yiddish" ["yor"]="Yoruba" ["zha"]="Zhuang; Chuang" ["zul"]="Zulu" ["unk"]="Unknown" ["und"]="Unknown") DIRECTORY=${1%/} PARENTDIRECTORY="$(eval dirname ${DIRECTORY// /\\ })" log "Finding videos..." if $isMac; then FILES=$(find "$DIRECTORY" -maxdepth ${options[depth]} -type f -exec file -N -I -- {} + 2>/dev/null | sed -n 's!: video/[^:]*$!!p') else FILES=$(find "$DIRECTORY" -maxdepth ${options[depth]} -type f -exec file -N -i -- {} + 2>/dev/null | sed -n 's!: video/[^:]*$!!p') fi EXCLUSIVEFILES=$(find "$DIRECTORY" -maxdepth ${options[depth]} -type f \( -iname $exclusivelySupportedExtensions \) 2>/dev/null) FILES=( "${FILES[@]}" "${EXCLUSIVEFILES[@]}" ) IFS=$'\n' FILES=($(sort -du <<<"${FILES[*]}")) # ISOs are currently not supported #FFPROBE="ffprobe -loglevel quiet -show_streams -probesize 10000000 -analyzeduration 10000000" if [ ! -n "$FILES" ]; then error "No video files found." exit 3; else log "Found:" echo "$(printf ' - %s\n' "${FILES[@]}")" fi # http://stackoverflow.com/questions/9256644/identifying-received-signal-name-in-bash-shell-script trap_with_arg() { func="$1" ; shift for sig ; do trap "$func $sig" "$sig" done } cleanup() { warn "$package did not end cleanly. Deleting temp file and extra files." if [ ${options[debug]} -eq 1 ]; then debug "Temporary output at ${tempVars[tempOutput]} has not been deleted because you are in debug mode." else rm -f "${tempVars[tempOutput]}" fi rm -f ${tempVars[cleanupFiles]} exit 4 } mapSubtitles() { if [ ${options[parallel]} = 0 ]; then tempVars+=([ffmpegQuery]="ffmpeg -loglevel error -hide_banner -i \"${tempVars[tempOutput]}\" ${tempVars[additionalInputs]} -c copy -y ${tempVars[ffmpegVideoStreamMap]} ${tempVars[ffmpegAudioStreamMap]} ${tempVars[ffmpegSubititleStreamMap]} ${tempVars[ffmpegOtherStreamMap]} ${tempVars[ffmpegSubtitlesMap]} \"${tempVars[output]}\" < /dev/null &") log "Setting subtitle names..." else # Mapping after a parallel merge tempVars+=([ffmpegQuery]="ffmpeg -loglevel error -hide_banner ${tempVars[parallelPreviewMergeTiming]} -i ${tempVars[fileEscaped]} -i \"${tempVars[tempOutput]}\" -async 1000 -c copy ${tempVars[audio]} -y -map 1:0 ${tempVars[ffmpegAudioStreamMap]} ${tempVars[ffmpegSubititleStreamMap]} ${tempVars[ffmpegOtherStreamMap]} ${tempVars[ffmpegSubtitlesMap]} \"${tempVars[output]}\" < /dev/null &") verbose "Finishing up merge..." fi debug "${red}[Query]$NC ${tempVars[ffmpegQuery]}" eval "${tempVars[ffmpegQuery]}" wait $! error="$?" if [ "$error" -ne 0 ]; then error "Ut oh. Ffmpeg exited with error code $error." cleanup fi if [ -n "${tempVars[setNewDefault]}" ]; then log "Removing any left over subtitle defaults..." for b in $(seq $((${tempVars[onSubtitleNumber]}))); do local subtitles+="--edit track:s$b --set flag-default=0 " done local query="mkvpropedit ${tempVars[output]} $subtitles" debug "${red}[Query]$NC $query" eval "$query" log "Setting new default subtitle..." local query="mkvpropedit ${tempVars[output]} --edit track:s${tempVars[setNewDefault]} --set flag-default=1" debug "${red}[Query]$NC $query" eval "$query" fi rm -f ${tempVars[cleanupFiles]} } # Takes ${tempVars[query]} handbrakeFile() { debug "${red}[Query]$NC $1" echo -e "${green}[h265ize]${NC}: Handbraking... -----------------------------------------------------" #while read line; do # echo "LINE: $line" # if [ -z "$($line | grep -E 'Encoding: task [0-9]* of [0-9]*, [0-9]{1,3}\.[0-9]{1,3} %' | awk -F' % ' '{print $1}' | awk -F', ' '{print $2}')"]; then # echo "FOUND YOU!"; # fi #done < <(eval "$1") #$(strace -p1234 -s9999 -e write) #while [ ps -p $! > /dev/null ]; do #done eval "$1 &" wait $! error=$? if [ ! $? -eq 0 ]; then error "Ut oh. Handbrake exited with error code $error." cleanup fi echo -e "${green}[h265ize]${NC}: Handbraking Complete -----------------------------------------------" } # Takes ${tempVars[tempOutput]} upconvertSubtitles() { if [ ${#vobSubtitlesTrackIds[@]} -lt 1 ] || [ ${options[upconvert]} -eq 0 ]; then return fi # Check if mkvextract is installed hash "mkvextract" 2>/dev/null || { warn "mkvextract(mkvtoolnix-cli) is not installed. mkvextract(mkvtoolnix-cli) must be installed to upconvert subtitles! Skipped upconverting vobsubs."; return; } hash "vobsub2srt" 2>/dev/null || { warn "vobsub2srt is not installed. vobsub2srt must be installed to upconvert subtitles! Skipped upconverting vobsubs."; return; } if [ "${options[preview]}" = 1 ]; then warn "Upconverted subtitle triming for previews is currently not supported. The whole subtitle will be added to the preview." # return fi log "Coverting vobsub subtitles to srt subtitles..." local tracks="" for c in "${vobSubtitlesTrackIds[@]}"; do tracks="$tracks $c:${1%.*}-vobsub$c" done verbose $green "$red[Query]$NC mkvextract tracks \"$FILE\"$tracks" eval "mkvextract tracks \"$FILE\"$tracks" wait $! for c in "${vobSubtitlesTrackIds[@]}"; do eval "vobsub2srt ${1%.*}-vobsub$c" wait $! rm -f "${1%.*}-vobsub$c.sub" rm -f "${1%.*}-vobsub$c.idx" done for c in "${vobSubtitlesTrackIds[@]}"; do tempVars+=([cleanupFiles]="${1%.*}-vobsub$c.srt ${1%.*}-vobsub$c.sub ${1%.*}-vobsub$c.idx ") tempVars+=([additionalInputs]="-i ${1%.*}-vobsub$c.srt ") done } trap_with_arg cleanup INT TERM startTime=$(date +"%b %d %r") # Time since h265ize script was invoked scriptStart=$(date) verbose $green "Folder processing started on $yellow$startTime$NC" filesNotAcceptable=0 while read -r FILE; do # Make sure temp variables are clear unset tempVars declare -A tempVars tempVars+=([filenameWithExtention]=$(basename "$FILE")) debug "Preprocessing ${tempVars[filenameWithExtention]}..." debug $(eval "printf '%0.s-' {1..$(($(tput cols) - 19))}") if $isMac; then tempVars[fileSize]=$(stat -n -f"%z" "$FILE") else tempVars[fileSize]=$(stat --printf="%s" "$FILE") fi tempVars[fileSize]=$(echo "scale=0; ${tempVars[fileSize]}/1000000" | bc) tempVars+=([fileExtension]="${FILE##*.}") tempVars+=([filename]=$(basename "${FILE%.*}")) tempVars+=([fileDir]="${FILE%/*}") toBeRemoved=$(printf '%q' "$PARENTDIRECTORY") tempVars+=([relativeDir]="${tempVars[fileDir]#$toBeRemoved}") debug "File directory: ${tempVars[fileDir]}" debug "Parent directory: $PARENTDIRECTORY" debug "Resulting relative directory: ${tempVars[relativeDir]}" tempVars+=([fileWithDir]="${FILE%.*}") tempVars+=([fileEscaped]=$(printf '%q' "$FILE")) tempVars+=([filenameEscaped]=$(printf '%q' "${tempVars[filename]}")) if [ "${options[preview]}" -eq 1 ]; then tempVars+=([previewSuffix]="-preview") fi debug "Destination: ${options[destination]}" tempVars+=([outputDest]=${options[destination]%/}) tempVars+=([relativeDest]="${tempVars[relativeDir]#/}") #echo -e "${lightb} RelativeDest: ${tempVars[relativeDest]}" tempVars+=([outputFile]="${tempVars[filename]//\`/\'}${tempVars[previewSuffix]}.${options[outputFormat]}") if [ -n "${tempVars[relativeDest]}" ]; then tempVars+=([output]="${tempVars[outputDest]}/${tempVars[relativeDest]}/${tempVars[outputFile]}") else tempVars+=([output]="${tempVars[outputDest]}/${tempVars[outputFile]}") fi debug "Output: ${tempVars[output]}" tempVars+=([tempDirEscaped]=$(printf '%q' "${options[tempDir]%/}")) tempVars+=([tempOutput]="${tempVars[tempDirEscaped]}/${options[tempName]}-$RANDOM.${options[outputFormat]}") tempVars+=([onSubtitleNumber]=0) tempVars+=([onAudioNumber]=0) log "Processing ${tempVars[filenameWithExtention]}..." echo $(eval "printf '%0.s-' {1..$(tput cols)}") # Check if output file already exists if [ -e "${tempVars[output]}" ] && [ ! "${options[preview]}" -eq 1 ]; then warn "${tempVars[output]} already exists. Skipping..." continue fi # Get number of streams tempVars+=([numStreams]=$(ffprobe -loglevel quiet -show_streams -probesize 10000000 -analyzeduration 10000000 "$FILE" | grep -o '\[STREAM\]' | wc -l | awk '{print $1}')) tempVars+=([numStreamsFromIndex]=$(expr ${tempVars[numStreams]} - 1)) # Get number of audio tracks tempVars+=([numAudioTracks]=$(ffprobe -loglevel quiet -show_streams -probesize 10000000 -analyzeduration 10000000 "$FILE" | grep -o 'codec_type=audio' | wc -l)) tempVars+=([audioTracks]=$(seq -s, $counter 1 ${tempVars[numAudioTracks]})) tempVars+=([AudioCodecs]="") # Get duration and size eval $(ffprobe -loglevel quiet -show_format "$FILE" | grep -E 'duration|size') tempVars+=([duration]="$duration") unset duration tempVars+=([size]=$(echo "$size / 1000000" | bc)) # Size in MB unset size tempVars+=([formatedDuration]=$(echo "scale=2; ${tempVars[duration]} / 60" | bc)) # Time in minutes tempVars+=([startDate]=$(date)) # Time encoding of file began #divisor=`echo "$duration / 60 * .7" | bc` #echo $divisor #factor=`echo "$size / $divisor" | bc` #echo $factor #quality=`echo "scale=2; $factor" | bc` #echo $quality tempVars+=([formatedStartDate]=$(date -d"${tempVars[startDate]}" +"%m/%d %r")) verbose $green "File encoding started at $yellow${tempVars[formatedStartDate]}$NC" verbose $green "Duration: ${tempVars[formatedDuration]} Minutes (${tempVars[duration]} seconds)" verbose $green "Size: ${tempVars[size]} MB" addAudioCodecToVar(){ if [ -z "${tempVars[audioCodecs]}" ]; then tempVars+=([audioCodecs]="$1") else tempVars[audioCodecs]="${tempVars[audioCodecs]},$1" fi } addAudioNameToVar() { if [ -z "${tempVars[audioNames]}" ]; then tempVars+=([audioNames]="$1") else temp=${tempVars[audioNames]} unset tempVars[audioNames] tempVars+=([audioNames]="$temp,$1") unset temp fi } addAudioCodec(){ # $1=format $2=bit_rate (flac only) if [ "${options[copyaudio]}" -eq 1 ]; then addAudioCodecToVar "copy" elif [ $1 = "flac" ]; then addAudioCodecToVar "flac$2" elif [ $1 = "dts" ]; then addAudioCodecToVar "copy:dts" elif [ $1 = "dtshd" ]; then addAudioCodecToVar "copy:dtshd" elif [ "${options[heaudio]}" -eq 1 ]; then addAudioCodecToVar "vorbis" #tempVars[audiobitrate]="-B 65" elif [ $1 = "aac" ]; then addAudioCodecToVar "fdk_aac" elif [ $1 = "ac3" ]; then addAudioCodecToVar "ac3" elif [ $1 = "vorbis" ]; then addAudioCodecToVar "vorbis" elif [ $1 = "mp3" ]; then if [ "${options[upconvert]}" = 0 ]; then addAudioCodecToVar "mp3" else addAudioCodecToVar "fdk_haac" fi else addAudioCodecToVar "copy" fi } declare -a subtitleNames declare -a subtitleLanguages vobSubtitlesTrackIds=() tempVars+=([vobSubtitleNumber]=0) for i in $(seq 0 ${tempVars[numStreamsFromIndex]}); do eval $(ffprobe -loglevel quiet -show_streams -probesize 10000000 -analyzeduration 10000000 -select_streams $i "$FILE" | grep -E "codec_name|codec_type|bits_per_raw_sample|width|height|avg_frame_rate") #echo `[[$codec_type -eq "video"]]` #debug "$(ffprobe -loglevel quiet -show_streams -probesize 10000000 -analyzeduration 10000000 -select_streams $i "$FILE")" language=$(ffprobe -loglevel quiet -show_streams -probesize 10000000 -analyzeduration 10000000 -select_streams $i "$FILE" | grep "TAG:language" | sed -n -e 's/^.*language=//p') title=$(ffprobe -loglevel quiet -show_streams -probesize 10000000 -analyzeduration 10000000 -select_streams $i "$FILE" | grep "TAG:title" | sed -n -e 's/^.*title=//p') if [ -z "$language" ]; then language="unk" fi debug "Stream Index: $i Title: $title Language: $language Codec: $codec_type" if [ "$codec_type" = "video" ] && [ -n "${tempVars[videoSet]}" ]; then warn "Multiple videos within single container detected. Handbrake only supports the first video within a container, so the rest will be skipped." elif [ "$codec_type" = "video" ]; then if [ -z "$width" ] || [ -z "$height" ]; then warn "No width and/or height was detected for the input video. Your ffmpeg is most likely out of date. I recommend ffmpeg version 2.8.2." fi tempVars[videoSet]=1 tempVars+=([sourceWidth]="$width") tempVars+=([sourceHeight]="$height") # Floor frame rate to get keyInt (Bluray compliant and saves timings and faster seek times but increases size) tempVars+=([keyInt]=$(echo "scale=0; $avg_frame_rate" | bc)) tempVars+=([frameRate]=$avg_frame_rate) if [ $codec_name = "h265" ] || [ $codec_name = "hevc" ]; then if [ ${options[override]} = 0 ]; then warn "Video is already encoded in h265. Skipping..." continue 2 fi fi tempVars+=([ffmpegVideoStreamMap]="-map 0:$i ") elif [ "$codec_type" = "audio" ]; then #echo -e "${green}$TITLE" tempVars[onAudioNumber]=$((${tempVars[onAudioNumber]} + 1)) if [ ${tempVars[onAudioNumber]} = 1 ]; then tempVars+=([defaultAudioLang]="$language") fi if [ -z "$codec_name" ]; then error "A codec was not provided for stream $i. Your ffmpeg (current: $(ffmpeg -version | head -n 1 | awk '{print $3}'), recommended: 2.8.2) is most likely out of date." exit 999 fi addAudioNameToVar "$title" addAudioCodec $codec_name $bits_per_raw_sample $language tempVars+=([ffmpegAudioStreamMap]="-map 0:$i ") elif [ "$codec_type" = "subtitle" ]; then subtitleLanguages+=("$language") subtitleNames+=("$title") tempVars[onSubtitleNumber]=$((${tempVars[onSubtitleNumber]} + 1)) debug "Subtitle #${tempVars[onSubtitleNumber]}" debug "Language: $language Default Audio: ${tempVars[defaultAudioLang]} NativeLangCode: ${options[nativeLangCode]}" if [ "$language" = "${options[nativeLangCode]}" ] || [ "$language" = "unk" ] || [ "$language" = "und" ]; then if [[ ! "${tempVars[defaultAudioLang]}" =~ ${options[nativeLangCode]} ]]; then if [ -n "${tempVars[defaultSubtitle]}" ]; then verbose $yellow "Multiple potential default subtitles with language $language found. Using first potential subtitle." else verbose $green "Default audio language does not match native language and native language subtitle provided. Default subtitle set to native language or first unknown language subtitle." tempVars+=([defaultSubtitle]="--subtitle-default=${tempVars[onSubtitleNumber]}") tempVars+=([defaultSubtitleNum]=${tempVars[onSubtitleNumber]}) fi fi fi # Discover all subs in vobsub(dvdsub) format if [ $codec_name = "dvdsub" ]; then tempVars[vobSubtitleNumber]=$((${tempVars[vobSubtitleNumber]} + 1)) vobSubtitlesTrackIds+=($i) #if [ "${options[preview]}" -ne 1 ]; then tempVars+=([ffmpegSubititleStreamMap]="-map ${tempVars[vobSubtitleNumber]} ") #fi if [ "${tempVars[defaultSubtitleNum]}" = "${tempVars[onSubtitleNumber]}" ]; then if [ "${options[outputFormat]}" = "mkv" ]; then if [ -z $(hash "mkvpropedit") ]; then tempVars+=([setNewDefault]=${tempVars[defaultSubtitleNum]}) else warn "mkvpropedit(mkvtoolnix-cli) is not installed. mkvpropedit(mkvtoolnix-cli) must be installed to set default subtitles! Skipped setting default subtitle." fi else warn "Yikes! It looks like the default subtitle is a vobsub I can fix that if you convert to an mkv instead!" fi fi else tempVars+=([ffmpegSubititleStreamMap]="-map 0:$i ") fi else # Other codecs are just copied tempVars+=([ffmpegOtherStreamMap]="-map 0:$i ") fi unset language unset title unset codec_name unset codec_type unset bits_per_raw_sample unset width unset height unset avg_frame_rate done verbose $green "Resolution: ${tempVars[sourceWidth]}x${tempVars[sourceHeight]}" verbose $green "Audio Codecs: ${tempVars[audioCodecs]}" verbose $green "Audio Tracks: ${tempVars[audioNames]}" for i in "${!subtitleNames[@]}"; do if [ -n "${subtitleNames[$i]}" ]; then debug "Subtitle name was not empty." tempVars[ffmpegSubtitlesMap]="${tempVars[ffmpegSubtitlesMap]} -metadata:s:s:$i title=\"${subtitleNames[$i]}\"" else ico3=${subtitleLanguages[$i]} tempVars[ffmpegSubtitlesMap]="${tempVars[ffmpegSubtitlesMap]} -metadata:s:s:$i title=\"${ICO3TONAME[$ico3]}\"" subtitleNames[$i]=${ICO3TONAME[$ico3]} warn "Subtitle title was empty. Subtitle title changed to ${subtitleNames[$i]}." fi done subtitleOutput=$(printf ",%s " "${subtitleNames[@]}") verbose $green "Subtitle Tracks: ${subtitleOutput:1}" unset subtitleOutput unset subtitleLanguages unset subtitleNames if [ ! -z "${tempVars[audioNames]}" ]; then tempVars[audioNames]="-A\"${tempVars[audioNames]}\"" else tempVars[audioNames]="" fi # Get number of subtitle tracks tempVars+=([numSubtitleTracks]=$(ffprobe -loglevel quiet -show_streams -probesize 10000000 -analyzeduration 10000000 "$FILE" | grep -o 'codec_type=subtitle' | wc -l)) if [ "${tempVars[numSubtitleTracks]}" -gt 0 ]; then tempVars+=([subtitleTracks]="--subtitle `seq -s, $counter 1 ${tempVars[numSubtitleTracks]}`") else tempVars+=([subtitleTracks]="") fi verbose $green "Subtitles: ${tempVars[subtitleTracks]}" # Find out if bit depth 10 or 8 should be used tempVars+=([bitDepth10]=$(ffprobe -loglevel quiet -show_streams -probesize 10000000 -analyzeduration 10000000 "$FILE" | grep -o 'profile=High 10')) if [ -n "${tempVars[bitDepth10]}" ]; then tempVars+=([profile]="main10") tempVars+=([handbrake]=${options[handbrakecli10bit]}) else tempVars+=([profile]="main") tempVars+=([handbrake]=${options[handbrakecli]}) fi # Check if handbrake is installed hash "${tempVars[handbrake]}" 2>/dev/null || { error "${tempVars[handbrake]} is not installed. ${tempVars[handbrake]} must be installed!"; exit 7; } if [ ${options[accurateTimestamps]} -eq 1 ]; then if [ -n "${options[extraOptions]}" ]; then tempVars+=([extraOptions]="-x ${options[extraOptions]}:keyint=${tempVars[keyInt]}") else tempVars+=([extraOptions]="-x keyint=${tempVars[keyInt]}") fi elif [ -n "${options[extraOptions]}" ]; then tempVars+=([extraOptions]="-x ${options[extraOptions]}") fi tempVars[halfDuration]=$(echo "scale=6; ${tempVars[duration]} / 2" | bc) if [ $(echo "scale=6; ${options[previewLength]} > ${tempVars[halfDuration]}" | bc) -eq 1 ] && [ ${options[preview]} -eq 1 ]; then tempVars[previewLength]="${tempVars[halfDuration]}" warn "Preview length can not be greater than half of the video's duration. Preview length temporarily set to ${tempVars[previewLength]}." else tempVars[previewLength]=${options[previewLength]} fi if [ ${options[preview]} -eq 1 ]; then tempVars+=([startStop]="--start-at duration:$(echo "scale=0; ${tempVars[duration]} / 2" | bc) --stop-at duration:${tempVars[previewLength]}"); fi verbose $green "Encoding Profile: ${tempVars[profile]}" if [ ${options[smart]} -eq 1 ]; then # Make a copy of stdout exec 5>&1 # Get ssim rating SSIM=$(eval "${tempVars[handbrake]} -i ${tempVars[fileEscaped]} -o ${tempVars[tempOutput]} -f mkv -w ${tempVars[sourceWidth]} -l ${tempVars[sourceHeight]} --previews 25 -e x265 -q ${options[quality]} --vfr -a ${tempVars[audioTracks]} --gain 0 --audio-fallback ac3 -E ${tempVars[audioCodecs]} ${tempVars[audioNames]} --subtitle ${tempVars[subtitleTracks]} ${tempVars[defaultSubtitle]} --native-language=${options[nativeLangCode]} --encoder-tune=ssim --encoder-profile=${tempVars[profile]} --encoder-preset=${options[preset]} --start-at duration:$(echo "scale=0; ${tempVars[duration]} / 2" | bc) --stop-at duration:30 --modulus 2 -m -x aq-strength=1.0:${options[extraOptions]} --verbose=0 2>&1" | tee >(cat - >&5) | grep -E 'x265 \[info\]: global :.*SSIM Mean: [0-9]\.([0-9]{6})') rm -f ${tempVars[tempOutput]} tempVars+=([ssim]==$(echo "$SSIM" | sed -r "s/.*([0-9]{6}).*/\1/")) unset SSIM tempVars[output]="${tempVars[output]}.${tempVars[ssim]}" fi #for i in ${19...25}; do # echo "hi!" #done mkdir -p "${tempVars[output]%/*}" if [ ${options[vbr]} -eq 0 ]; then tempVars[videoQuality]="-q ${options[quality]}"; else tempVars[videoQuality]="-b ${options[vbr]}"; fi #tempVars+=([query]="${tempVars[handbrake]} -i ${tempVars[fileEscaped]} -o ${tempVars[tempOutput]} -f mkv -w ${tempVars[sourceWidth]} -l ${tempVars[sourceHeight]} --previews 50 -e x265 -q ${options[quality]} --vfr -a ${tempVars[audioTracks]} --gain 0 --audio-fallback ac3 -E ${tempVars[audioCodecs]} ${tempVars[audioNames]} --subtitle ${tempVars[subtitleTracks]} ${tempVars[defaultSubtitle]} --native-language=${options[nativeLangCode]} --encoder-profile=${tempVars[profile]} --encoder-preset=${options[preset]} ${tempVars[startStop]} --modulus 2 -m ${tempVars[extraOptions]} --verbose=0 < /dev/null") tempVars+=([query]="${tempVars[handbrake]} -i ${tempVars[fileEscaped]} -o ${tempVars[tempOutput]} -w ${tempVars[sourceWidth]} -l ${tempVars[sourceHeight]} --previews 50 -e x265 ${tempVars[videoQuality]} --vfr -a ${tempVars[audioTracks]} --gain 0 --audio-fallback ac3 -6 dpl2 ${tempVars[audiobitrate]} -E ${tempVars[audioCodecs]} ${tempVars[audioNames]} ${tempVars[subtitleTracks]} ${tempVars[defaultSubtitle]} --encoder-profile=${tempVars[profile]} --encoder-preset=${options[preset]} ${tempVars[startStop]} --modulus 2 -m ${tempVars[extraOptions]} --verbose=0 < /dev/null") if [ "${options[parallel]}" -eq 1 ]; then log "Parallel encoding ${tempVars[filenameWithExtention]}." ffmpegParallel mergeSegments else handbrakeFile "${tempVars[query]}" fi tempVars+=([newDuration]=$(ffprobe -loglevel quiet -show_format "${tempVars[tempOutput]}" | grep 'duration' | sed 's/.*duration=\(.*\).*/\1/')) if [ "${tempVars[newDuration]}" = "N/A" ] || [ ! -n "${tempVars[newDuration]}" ]; then tempVars[newDuration]=0 fi tempVars+=([newDurationMax]=$(echo "${tempVars[duration]} + ${options[timeDiffLimit]}" | bc)) tempVars+=([newDurationMin]=$(echo "${tempVars[duration]} - ${options[timeDiffLimit]}" | bc)) tempVars+=([passMax]=$(echo "${tempVars[newDuration]} < ${tempVars[newDurationMax]}" | bc)) tempVars+=([passMin]=$(echo "${tempVars[newDuration]} > ${tempVars[newDurationMin]}" | bc)) verbose $green "Original Duration: ${lightb}${tempVars[duration]}$NC New Duration: ${lightb}${tempVars[newDuration]}$NC" if [ -n "${tempVars[duration]}" ] && [ 1 -eq "${tempVars[passMax]}" ] && [ 1 -eq "${tempVars[passMin]}" ] && [ "${options[preview]}" -ne 1 ]; then if [ "${options[parallel]}" -eq 1 ]; then mapSubtitles rm -f "${tempVars[tempOutput]}" elif [ -n "${tempVars[ffmpegSubtitlesMap]}" ]; then upconvertSubtitles "${tempVars[tempOutput]}" mapSubtitles rm -f "${tempVars[tempOutput]}" else debug "Moving ${tempVars[tempOutput]} to ${tempVars[output]}" mv "${tempVars[tempOutput]}" "${tempVars[output]}" fi log "${tempVars[filename]} encoded successfully." elif [ "${options[preview]}" -eq 1 ] && [ $(echo "${tempVars[newDuration]}" | awk '{printf("%d\n",$0+=$0<0?0:0.9999999999999)}') -ne 0 ]; then mkdir -p "${tempVars[output]%/*}" if [ "${options[parallel]}" -eq 1 ]; then mapSubtitles rm -f "${tempVars[tempOutput]}" elif [ ${tempVars[onSubtitleNumber]} -gt 0 ]; then upconvertSubtitles "${tempVars[tempOutput]}" mapSubtitles rm -f "${tempVars[tempOutput]}" else mv "${tempVars[tempOutput]}" "${tempVars[output]}" fi log "Preview output in: ${tempVars[output]}" else badfile=1 filesNotAcceptable=$(($filesNotAcceptable + 1)) error "Ut oh. Duration of ${tempVars[filename]} is not within acceptable limits of the original file." error "Bad file stored at ${tempVars[tempOutput]}." fi tempVars+=([endDate]=$(date)) # Time encoding of file ended tempVars+=([formatedEndDate]=$(date -d"${tempVars[endDate]}" +"%m/%d %r")) verbose $green "File encoding ended at $yellow${tempVars[formatedEndDate]}$NC" tempVars+=([startSeconds]=$(date -d"${tempVars[startDate]}" +%s)) tempVars+=([endSeconds]=$(date -d"${tempVars[endDate]}" +%s)) tempVars+=([timeDifference]=$((${tempVars[endSeconds]}-${tempVars[startSeconds]}))) verbose $green "File encoding time: $yellow$((${tempVars[timeDifference]} / 60))$NC minute(s) and $yellow$((${tempVars[timeDifference]} % 60))$NC second(s)" if [ -z "$badfile" ]; then if $isMac; then tempVars[encodedFileSize]=$(stat -n -f"%z" "${tempVars[output]}") else tempVars[encodedFileSize]=$(stat --printf="%s" "${tempVars[output]}") fi if [ ${options[delete]} -eq 1 ] && [ ${options[preview]} -eq 0 ]; then debug "Removing $FILE" rm "$FILE" debug "Moving ${tempVars[output]} to ${FILE%.*}" mv "${tempVars[output]}" "${FILE%.*}.${options[outputFormat]}" fi tempVars[encodedFileSize]=$(echo "scale=0; ${tempVars[encodedFileSize]}/1000000" | bc) tempVars[sizeChange]=$(echo "scale=2; ${tempVars[encodedFileSize]}/${tempVars[fileSize]} * 100" | bc) verbose $green "File size: Old: $yellow${tempVars[fileSize]}${NC}MB New: $yellow${tempVars[encodedFileSize]}${NC}MB ($yellow${tempVars[sizeChange]}${NC}%)" if [ "${options[stats]}" = 1 ]; then #tempVars[statsFile]=$(date -d"${tempVars[scriptStart]}" +"%m-%d-%H:%M:%S") if [ -z "${options[logFile]}" ]; then verbose $green "Writing stats to ${tempVars[outputDest]}/h265ize.stats" $(echo "${tempVars[formatedEndDate]},${tempVars[filenameWithExtention]},${tempVars[fileSize]}MB,${tempVars[encodedFileSize]}MB,${tempVars[sizeChange]}%" >> "${tempVars[outputDest]}/h265ize.stats") else verbose $green "Writing stats to ${options[statsFile]}" $(echo "${tempVars[formatedEndDate]},${tempVars[filenameWithExtention]},${tempVars[fileSize]}MB,${tempVars[encodedFileSize]}MB,${tempVars[sizeChange]}%" >> "${options[statsFile]}") fi fi fi # Clear stdin because ffmpeg leaves input that will corrupt next command #http://superuser.com/questions/276531/clear-stdin-before-reading #read -t 1 -n 10000 discard unset vobSubtitlesTrackIds unset badfile done <<< "${FILES[*]}" endTime=$(date +"%b %d %r") # Time after all file have been encoded verbose $green "Folder processing ended on $yellow$endTime$NC" if [ $filesNotAcceptable != 0 ]; then error "$filesNotAcceptable files had encoding errors/were encoded incorrectly, and therefore were not placed in the destination." fi log "Completed!" # Terminal, if you would ring please echo -e "\a"