From fdaf3278cabfa3575bfc9c530649370a2eb26a33 Mon Sep 17 00:00:00 2001 From: Y Date: Mon, 6 Nov 2017 23:14:39 +0100 Subject: [PATCH] =?UTF-8?q?wip=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DivxPlusHD.sh | 135 +++++++++++++++++++++++++++++++------------------- 1 file changed, 83 insertions(+), 52 deletions(-) diff --git a/DivxPlusHD.sh b/DivxPlusHD.sh index 4b2d4e7..bd9429a 100755 --- a/DivxPlusHD.sh +++ b/DivxPlusHD.sh @@ -4,6 +4,7 @@ # Mandatory: # -i [-i …] Input files # Optional: +# -d Debug. # -h Print this help, and information about the inputs. # -n Do not actually run the commands (print them). # -y Accept the default answers to this program’s @@ -50,8 +51,9 @@ function usage() { abort=true } -while getopts i:o:t:A:SV:Q:P:T:W:H:Chny opt; do case $opt in +while getopts di:o:t:A:SV:Q:P:T:W:H:Chny opt; do case $opt in h) usage ;; +d) debug=true ;; n) fake=true ;; y) defaults=true ;; i) inputs[${#inputs[*]}]="$OPTARG" ;; @@ -85,16 +87,63 @@ if [[ "$x264qual" =~ pass ]]; then x264qual="--stats $tmppfx.stats $x264qual" fi +[ -n "$debug" ] && set -x + +## SOME USEFUL FUNCTIONS + +function split() { + # needed because `read` merges adjacent field separators + fields="$1"; shift + while IFS=$'\n' read field; do + [ $# -gt 0 ] && eval "${1}=\"$(sed 's/[\"$]/\\\0/g' <<<"$field")\""; shift + done < <(tr '\t' '\n' <<<"$fields") +} +function run() { + local err=0 + for item in "$@"; do printf ' %q' "$item"; done; printf '\n' + if [ -z "$fake" ]; then + "$@" + err=$? + if [ $err -gt 1 ] || [ $err -gt 0 -a "$1" != mkvmerge ]; then + echo "ERROR: $1 exited with code ${err}." >&2 + error=$((error+1)) + fi + fi +} +function on_exit() { + if [ $error -eq 0 ]; then + run rm -f ${tmppfx}* + else + echo 'Errors were encountered. These files were not deleted:' >&2 + printf '%s\n' ${tmppfx}* >&2 + fi +} +trap on_exit EXIT + ## ANALYSE INPUTS # fields: str:file path, bool:use chapters, bool:use some subtitles, -# int:number of chapters +# int:number of chapters, string:original file path (≠file path if VOB) +exec 3>&1 IFS=$'\n' read -d '' -a f_info < <( - for f in "${inputs[@]}"; do - printf "$f\t0\t0\t" - ffprobe -loglevel error -show_chapters "$f" | grep -Fx '[CHAPTER]' | wc -l + for ((i=0; i<${#inputs[*]}; i++)); do + f="${inputs[$i]}" + realf="$f" + # Replace VOB files + # https://stackoverflow.com/questions/19200790/converting-dvd-image-with-subtitles-to-mkv-using-avconv + if [[ "$f" =~ \.vob$ ]] || [[ "$f" =~ \.VOB$ ]]; then + f=$tmppfx.$i.proxy.mkv + cmd=( ffmpeg -fflags +genpts -analyzeduration 3600M -probesize 4G + -i "$realf" -map 0:v -map 0:a -map 0:s -c copy "$f" ) + echo "${cmd[*]}" >&3 + "${cmd[@]}" >&2 + fi + printf "%s\t0\t0\t%s\t%s\n" "$f" \ + $(ffprobe -loglevel error -show_chapters "$f" | grep -Fx '[CHAPTER]' | wc -l) \ + "$realf" done ) +exec 3>&- # fields: int:file index, int:stream index, str:codec, str:profile, int:width, # int:height, [str:transformation], [int:new width], [int:new height], @@ -103,23 +152,25 @@ IFS=$'\n' read -d '' -a f_info < <( exec 3>&1 IFS=$'\n' read -d '' -a v_info < <( for ((i=0; i<${#inputs[*]}; i++)); do - ffprobe -loglevel error -show_streams -select_streams v "${inputs[$i]}" \ + split "${f_info[$i]}" path usechap usesub nbchap origpath + ffprobe -loglevel error -show_streams -select_streams v "$path" \ | awk -F= -vfile=$i -vmw=$x264maxW -vmh=$x264maxH \ - -vffcrop=$autocrop -vfffile="${inputs[$i]}" -vOFS=$'\t' ' + -vffcrop=$autocrop -vfffile="$path" -vOFS=$'\t' ' BEGIN{ gsub(/["\$]/,"\\\\&",fffile) + fps=25 } /^\[STREAM/ { idx=0; codec=""; profile=""; fullw=768; fullh=432 - sar="1:1"; fps=25; start=0; lang="und"; title="" + sar="1:1"; start=0; lang="und"; title="" } $1=="index" { idx=$2 } $1=="codec_name" { codec=$2 } $1=="profile" { profile=$2 } - $1=="width" { fullw=$2 } - $1=="height" { fullh=$2 } + $1=="width" { fullw=$2 } + $1=="height" { fullh=$2 } $1=="sample_aspect_ratio" { sar=$2 } - $1=="avg_frame_rate" { fps=$2 } + $1=="avg_frame_rate" && $2!~"/0$" { fps=$2 } $1=="start_time" { start=$2 } $1=="TAG:language" { lang=$2 } $1=="TAG:title" { title=$2 } @@ -177,7 +228,8 @@ exec 3>&- # bool:use this stream as default audio, bool:force this audio stream IFS=$'\n' read -d '' -a a_info < <( for ((i=0; i<${#inputs[*]}; i++)); do - ffprobe -loglevel error -show_streams -select_streams a "${inputs[$i]}" \ + split "${f_info[$i]}" path usechap usesub nbchap origpath + ffprobe -loglevel error -show_streams -select_streams a "$path" \ | awk -F= -vfile=$i -vabr=${akbps:-64} -vOFS=$'\t' ' /^\[STREAM/ { idx=0; codec=""; profile=""; sampfreq=0; channels=2; layout="2.0" @@ -209,7 +261,8 @@ IFS=$'\n' read -d '' -a a_info < <( # bool:force these subtitles IFS=$'\n' read -d '' -a s_info < <( for ((i=0; i<${#inputs[*]}; i++)); do - ffprobe -loglevel error -show_streams -select_streams s "${inputs[$i]}" \ + split "${f_info[$i]}" path usechap usesub nbchap origpath + ffprobe -loglevel error -show_streams -select_streams s "$path" \ | awk -F= -vfile=$i -vOFS=$'\t' ' /^\[STREAM/ { idx=0; codec=""; start=0; def=0; force=0; hearhelp=0; lang="und"; title="" @@ -233,6 +286,10 @@ IFS=$'\n' read -d '' -a s_info < <( # $1:array, $2:list-program (awk) function print_info() { local -n arr=$1 + if [ -n "$debug" ]; then + echo "= print_info $1 =" + for l in "${arr[@]}"; do tr '\t' '\n' <<<"$l" | nl -s: -w2 -ba | tr '\n' ' '; echo; done + fi printf '%s\n' "${arr[@]}" | awk -F$'\t' -vOFS=$'\t' "$2" } @@ -265,7 +322,7 @@ function set_flags() { if [ ${#f_info[*]} -gt 0 ]; then echo FILES: - print_info f_info '{ print NR-1, $1, $4==0?"":("(with " $4 " chapters)") }' + print_info f_info '{ print NR-1, $5, $4==0?"":("(with " $4 " chapters)") }' if [ -z "$abort" ]; then set_flags f_info 'File from which chapters should be read (if any): ' \ "$(printf '%s\n' "${f_info[@]}" \ @@ -324,35 +381,6 @@ fi ## ENCODE -function split() { - # needed because `read` merges adjacent field separators - fields="$1"; shift - while IFS=$'\n' read field; do - [ $# -gt 0 ] && eval "${1}=\"$(sed 's/[\"$]/\\\0/g' <<<"$field")\""; shift - done < <(tr '\t' '\n' <<<"$fields") -} -function run() { - local err=0 - for item in "$@"; do printf ' %q' "$item"; done; printf '\n' - if [ -z "$fake" ]; then - "$@" - err=$? - if [ $err -gt 1 ] || [ $err -gt 0 -a "$1" != mkvmerge ]; then - echo "ERROR: $1 exited with code ${err}." >&2 - error=$((error+1)) - fi - fi -} -function on_exit() { - if [ $error -eq 0 ]; then - run rm -f ${tmppfx}* - else - echo 'Errors were encountered. These files were not deleted:' >&2 - printf '%s\n' ${tmppfx}* >&2 - fi -} -trap on_exit EXIT - #⇒ Video #⇒ http://labs.divx.com/node/16598 @@ -361,6 +389,7 @@ for v in "${v_info[@]}"; do file idx codec profile w h stdop cropL cropT cropR cropB stdw stdh \ sar fps start lang title use [ $use -eq 1 ] || continue + split "${f_info[$file]}" path usechap usesub nbchap origpath if [ $(bc <<<"$fps") -gt 25 ]; then fps=25 fi @@ -372,12 +401,12 @@ for v in "${v_info[@]}"; do if [[ "$x264qual" =~ pass ]]; then run nice -n 10 x264 $vopt $vopt_divx $x264qual 1 \ - ${x264slow:+--slow-firstpass} -o $tmppfx.$file.$idx.mkv "${inputs[$file]}" + ${x264slow:+--slow-firstpass} -o $tmppfx.$file.$idx.mkv "$path" run nice -n 10 x264 $vopt $vopt_divx $x264qual 2 \ - -o $tmppfx.$file.$idx.mkv "${inputs[$file]}" + -o $tmppfx.$file.$idx.mkv "$path" else run nice -n 10 x264 $vopt $vopt_divx $x264qual \ - -o $tmppfx.$file.$idx.mkv "${inputs[$file]}" + -o $tmppfx.$file.$idx.mkv "$path" fi done @@ -389,12 +418,13 @@ for a in "${a_info[@]}"; do file idx codec profile sampfreq channels kbps layout start def force \ seehelp lang title use setdef setforce [ $use -eq 1 ] || continue + split "${f_info[$file]}" path usechap usesub nbchap origpath if [ "$codec" == 'aac' ]; then - run ffmpeg -loglevel warning -i "${inputs[$file]}" -vn -map 0:$idx \ - -movflags +faststart -c:a copy $tmppfx.$file.$idx.aac + run ffmpeg -loglevel warning -i "$path" -vn -sn -map 0:$idx \ + -c:a copy $tmppfx.$file.$idx.aac else - run ffmpeg -loglevel warning -i "${inputs[$file]}" -vn -map 0:$idx \ - -movflags +faststart -c:a aac -b:a ${kbps}k $tmppfx.$file.$idx.aac + run ffmpeg -loglevel warning -i "$path" -vn -sn -map 0:$idx \ + -c:a libfdk_aac -b:a ${kbps}k $tmppfx.$file.$idx.aac fi done @@ -409,8 +439,8 @@ mkvopt=( # subtitles & chapters -for f in "${f_info[@]}"; do - split "$f" path usechap usesub nbchap +for ((i=0; i<${#f_info[*]}; i++)); do + split "${f_info[$i]}" path usechap usesub nbchap origpath [ $usechap -eq 0 -a $usesub -eq 0 ] && continue if [ $usesub -eq 1 ]; then subs= @@ -419,6 +449,7 @@ for f in "${f_info[@]}"; do split "$s" \ file idx codec start def force hearhelp lang title use setdef setforce [ $use -eq 1 ] || continue + [ $file -eq $i ] || continue subs=$subs,$idx newopt=( "${newopt[@]}" --default-track $idx:$setdef --forced-track $idx:$setforce )