From 8ec5579545ee46c5847ada65d04676ef6350950f Mon Sep 17 00:00:00 2001 From: gacopl Date: Thu, 28 Dec 2023 01:48:32 +0100 Subject: [PATCH] add: add initial support for converting from mp4 input, fix: cleanup after failed conversion, improve lang filtering --- dvmkv2mp4 | 199 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 138 insertions(+), 61 deletions(-) diff --git a/dvmkv2mp4 b/dvmkv2mp4 index 7205607..0967fbc 100755 --- a/dvmkv2mp4 +++ b/dvmkv2mp4 @@ -12,6 +12,10 @@ DEBUG="no" ## Check OSTYPE and set ionice level to idle to not hammer disks during conversion ADDSUBS="no" ## Whether to add subs to mp4 file default no as some bad subs can break the encoding" +SRC_FORMAT="mkv" +## Default input source container used for conversion +DST_SUFFIX="DV-MP4" + if [[ $OSTYPE == 'darwin'* ]] then ionc="ionice --low" @@ -34,12 +38,14 @@ function print_help { echo "-r | --remove-source - remove source video after conversion" echo "-s | --add-subs - add srt subtitles to mp4 as subtitle tracks" echo "-d | --debug - keep intermediary conversion files" + echo "-m | --mp4-source - enable convert from MP4 source mode instead of MKV" + echo "-f | --override-type - override detected source type to one of dv5,dv7,dv8,hdr10plus echo "-v | --version - print version" echo "" echo "dvmkv2mp4 -l und,pol,eng -r -a # will process any DV/HDR10+ mkvs found in current dir and keep only Undefined, Polish and English tracks, will remove source file once done and will create audio-subs-meta file for future needs" } -TEMP=$(getopt -o acl:rsdvh --long asm,ext-compat,langs:,remove-source,add-subs,debug,version,help \ +TEMP=$(getopt -o acl:rsdmf:vh --long asm,ext-compat,langs:,remove-source,add-subs,debug,mp4-source,override-type:,version,help \ -n 'dvmkv2mp4' -- "$@") if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi @@ -58,6 +64,8 @@ while true; do -s | --add-subs ) ADDSUBS="yes"; shift ;; -c | --ext-compat ) EXT_COMPAT="yes"; shift ;; -h | --help ) print_help; shift; exit ;; + -m | --mp4-source ) SRC_FORMAT="mp4"; shift ;; + -f | --override-type ) SRC_TYPE="$2"; shift 2 ;; -l | --langs ) LANGS="$2"; shift 2 ;; -- ) shift; break ;; * ) break ;; @@ -65,29 +73,60 @@ while true; do done function cleanup { - while read i;do - rm "`echo "$i" | cut -f1 -d\|`" - done <<< "$(cat audio.exports)" - rm RPU.bin extra.json hdr10plus_metadata.json - rm audio.exports - rm sub.exports - rm tracks.list - rm chapters.list - rm BL*hevc - rm "${input}.dvconverting" - rm -rf ./tmp - if [ $REMOVESOURCE == "yes" ]; then - rm "${input}" - fi + if [ $DEBUG != "yes" ]; then + while read i;do + rm "`echo "$i" | cut -f1 -d\|`" + done <<< "$(cat audio.exports)" + + rm RPU.bin extra.json hdr10plus_metadata.json + rm audio.exports + rm sub.exports + rm tracks.list + rm chapters.list + rm BL*hevc + rm "${input}.dvconverting" + rm -rf ./tmp + + if [ $exit_code -eq 0 ]; then + if [ $REMOVESOURCE == "yes" ]; then + echo "" + echo "Removing source as requested" + rm "${input}" + if [[ "${input}" == *"${DST_SUFFIX}.mp4" ]]; then + # Move back files to original naming if source already had SUFFIX + echo "Renaming converted files to original source names" + mv "${output}.mp4" "${input}" + mv "${output}.nfo" "${inputbase}.nfo" + for filename in "${inputbase}".mp4.*.*; do + if [ -e "$filename" ]; then + new_filename=$(echo "$filename" | sed 's/\.mp4\.[^.]*\.[^.]*$//') + mv "$filename" "$new_filename" + fi + done + fi + fi + else + echo "Conversion FAILED keeping the source and SRT subtitles, deleting rest" + for i in "$output"*.{srt}; do + cp "$i" "${inputbase}$(echo "$i" | sed -n 's/.*\(\.[^.]*\.\(srt\|sup\)\)$/\1/p')" + echo "${inputbase}$(echo "$i" | sed -n 's/.*\(\.[^.]*\.\(srt\|sup\)\)$/\1/p')" + done + rm "${output}.nfo" "${output}.asm" "${output}.*.srt" "${output}.*.sup" "${input}.dvconverting" + fi + else + rm "${input}.dvconverting" + rm -rf ./tmp + fi } function processsubs { ### PROCESS SUBS - inputbase=${input%.mkv} - # COPY EXISTING SRT - for i in "$inputbase".*.srt; do - eval "cp \"$i\" \"`echo "$i" | $sed 's/\ DV\.\|\ HDR10+\.\|HDR\./\./g' | $sed 's/\.\(.*\)\.srt/\ DV-MP4\.\1\.srt/g'`\""; - done + # Only copy SRT if we are changing filename suffix + if [[ "$inputbase" != *"${DST_SUFFIX}.mp4" ]]; then + for i in "$inputbase"*.srt; do + cp "$i" "${output}$(echo "$i" | sed -n 's/.*\(\.[^.]*\.srt\)$/\1/p')" + done + fi # CONVERT PGS 2 SRT if [ -s "sub.exports" ] && [ "$(cat sub.exports | grep hdmv_pgs)" != "" ]; then while read i;do @@ -108,8 +147,30 @@ function processsubs { start=`date +%s` echo "$HEADER" echo "Starting Conversions `date`" -for f in *.mkv;do +echo "----------------------------" +for f in *.${SRC_FORMAT};do + echo "" + echo "-------------" + echo "Input file: $f" input="$f" + inputbase=${input%.${SRC_FORMAT}} + ffprobe_source_info=$(ffprobe "$input" 2>&1 | grep DOVI | grep -v comment | sed s/://g) + short_info=$(mediainfo "$input" |awk '/Text #1/{exit}1' | grep -i "^video\|^audio\|format\|commercial\|lang\|title" | cut -f2 -d":" | sed s/,//g | sed s/^Video/\ \|\ Video\ -\ /g | sed s/^Audio/\ \|\ Audio\ -\ /g) + dv=$(mediainfo "$input" | grep 'HDR format.*Dolby Vision') + hdr10plus=$(mediainfo "$input" | grep 'HDR format.*HDR10+') + output=`echo "$input" | $sed "s/\ DV.${SRC_FORMAT}\|\ HDR10+.${SRC_FORMAT}\|\ HDR.${SRC_FORMAT}/.${SRC_FORMAT}/g" | $sed s/\.${SRC_FORMAT}/\ ${DST_SUFFIX}\.${SRC_FORMAT}/g | $sed "s/\ ${DST_SUFFIX} ${DST_SUFFIX}\.${SRC_FORMAT}/\ ${DST_SUFFIX}\.${SRC_FORMAT}/g"` + output="${output%.${SRC_FORMAT}}" + if [[ "${SRC_FORMAT}" == "mp4" ]]; then + if [[ $input == *"${DST_SUFFIX}.mp4.mp4" ]]; then + echo "Input and output would be the same and input already .mp4.mp4 ABORTING!!!" + continue + elif [[ $input == *"${DST_SUFFIX}.mp4" ]]; then + output=${output}.mp4 + fi + fi + echo "Output file: $output.mp4" + echo "" + ffstart5_8="$ionc ffmpeg -i \"$input\" -y -loglevel error -stats -map 0:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc BL_RPU.hevc" ffstart7_1="$ionc ffmpeg -i \"$input\" -y -loglevel error -stats -map 0:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc -" ffstart7_2="$ionc ffmpeg -i \"$input\" -y -loglevel error -stats -map 0:v:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc BL.hevc -map 0:v:1 -c:v copy -vbsf hevc_mp4toannexb -f hevc -" @@ -117,12 +178,6 @@ for f in *.mkv;do ffend7_2="| $ionc dovi_tool -m 2 extract-rpu - -o RPU.bin" ffstarthdr10plus="$ionc ffmpeg -i \"$input\" -y -loglevel error -stats -map 0:v:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc BL.hevc -map 0:v:0 -c:v copy -vbsf hevc_mp4toannexb -f hevc -" ffendhdr10plus="| $ionc hdr10plus_tool extract -o hdr10plus_metadata.json -" - ffprobe_info=$(ffprobe "$input" 2>&1 | grep DOVI | sed s/://g) - short_info=$(mediainfo "$input" |awk '/Text #1/{exit}1' | grep -i "^video\|^audio\|format\|commercial\|lang\|title" | cut -f2 -d":" | sed s/,//g | sed s/^Video/\ \|\ Video\ -\ /g | sed s/^Audio/\ \|\ Audio\ -\ /g) - dv=$(mediainfo "$input" | grep 'HDR format.*Dolby Vision') - hdr10plus=$(mediainfo "$input" | grep 'HDR format.*HDR10+') - output=`echo "$input" | $sed 's/\ DV.mkv\|\ HDR10+.mkv\|\ HDR.mkv/.mkv/g' | $sed s/\.mkv/\ DV-MP4\.mkv/g` - output="${output%.mkv}" ## AUTODETECT SOURCE TYPE AND CONSTRUCT FFMPEG if [ -z "$hdr10plus" ] && [ -z "$dv" ] || [ -f "${input}.dvconverting" ]; then @@ -131,11 +186,22 @@ for f in *.mkv;do else touch "${input}.dvconverting" if [ ! -z "$dv" ]; then - dv_profile=$(mediainfo "$input" | grep "HDR format.*dvhe\." | $sed 's/.*dvhe\.0\(.\).*/\1/') - if { [ "$dv_profile" -ne 4 ] && [ "$dv_profile" -ne 5 ] && [ "$dv_profile" -ne 7 ] && [ "$dv_profile" -ne 8 ]; } ; then - info "Unsupported Dolby Vision profile '$dv_profile'; doing nothing" - rm *.dvconverting - continue + if [[ "$SRC_TYPE" != "" ]]; then + source_type_msg="Source Type overriden to $SRC_TYPE !!!" + echo "$source_type_msg" + created_tag+=" | $source_type_msg" + dv_profile=$SRC_TYPE + else + dv_profile=$(mediainfo "$input" | grep "HDR format.*dvhe\." | $sed 's/.*dvhe\.0\(.\).*/\1/') + if ! [[ $dv_profile =~ ^[0-9]+$ ]]; then + echo "Could not recognize Dolby Vision Profile in source - ABORTING" + echo "Please use -f switch to set profile manually (dv5,dv7,dv8.1,dv8.2,dv8.4)" + fi + if { [ "$dv_profile" -ne 4 ] && [ "$dv_profile" -ne 5 ] && [ "$dv_profile" -ne 7 ] && [ "$dv_profile" -ne 8 ]; } ; then + info "Unsupported Dolby Vision profile '$dv_profile'; doing nothing" + rm *.dvconverting + continue + fi fi vscount=`ffprobe -loglevel error -select_streams v -show_entries stream=type:stream=codec_name:stream=index:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep hevc | wc -l` if [ "$dv_profile" -eq 4 ] || [ "$dv_profile" -eq 5 ] || [ "$dv_profile" -eq 8 ];then @@ -147,21 +213,19 @@ for f in *.mkv;do fi if [ "$dv_profile" -eq 5 ]; then dv_target=5 - elif [ "$dv_profile" -eq 8 ]; then - dv_target=8 elif [ "$dv_profile" -eq 4 ]; then dv_target=4 else - dv_target=8 + dv_target=8.1 fi - echo "Converting DV$dv_profile to DV$dv_target: \"$input\"" + echo "Converting DV$dv_profile $SRC_FORMAT to DV$dv_target mp4" echo - created_tag+=" | Source - DV$dv_profile $ffprobe_info | $short_info" + created_tag+=" | Source - DV$dv_profile $ffprobe_source_info | $short_info" elif [ ! -z "$hdr10plus" ]; then - echo "Converting HDR10+ to DV8: \"$input\"" + echo "Converting HDR10+ $SRC_FORMAT to DV8" echo - created_tag+=" | Source - HDR10+ $ffprobe_info | $short_info" - dv_target=8 + created_tag+=" | Source - HDR10+ $ffprobe_source_info | $short_info" + dv_target=8.1 ffstring=("$ffstarthdr10plus") MaxDML=`mediainfo "$input" | grep 'Mastering display luminance' | cut -f 4 -d:` MaxDML=${MaxDML% cd*} @@ -206,13 +270,13 @@ EOF fi fi # SELECT LANG TRACKS - as=$(ffprobe -loglevel error -select_streams a -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep $(echo $LANGS | $sed 's/,/,\\|,/g') | $sed 's/,/\|/g') + as=$(ffprobe -loglevel error -select_streams a -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep $(echo $LANGS | $sed 's/,/\\|,.*,.*,/g' | $sed 's/^/,.*,.*,/') | $sed 's/,/\|/g' ) if [ "$as" == "" ]; then ffprobe -loglevel error -select_streams a -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | $sed 's/,/\|/g' > tracks.list else echo "$as" > tracks.list fi - ts=$(ffprobe -loglevel error -select_streams s -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep $(echo $LANGS | $sed 's/,/,\\|,/g') | $sed 's/,/\|/g') + ts=$(ffprobe -loglevel error -select_streams s -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | grep $(echo $LANGS | $sed 's/,/\\|,.*,.*,/g' | $sed 's/^/,.*,.*,/') | $sed 's/,/\|/g' ) if [ "$ts" == "" ]; then ffprobe -loglevel error -select_streams s -show_entries stream=type:stream=codec_name:stream=index:stream=start_pts:stream_tags=language:stream_tags=title -of csv=p=0 "$input" | $sed 's/,/\|/g' >> tracks.list else @@ -264,6 +328,10 @@ EOF echo "$output.${lang}${id}.ass|$id|ass|$orig_codec|$delay|$lang|$title" >> sub.exports echo "$output.${lang}${id}.srt|$id|srt|$orig_codec|$delay|$lang|$title" >> sub.exports fi + if [ "$orig_codec" == "mov_text" ]; then + ffopts="-map 0:$id -c:s:0 srt \"$output.${lang}${id}.srt\"" + echo "$output.${lang}${id}.srt|$id|srt|$orig_codec|$delay|$lang|$title" >> sub.exports + fi if [ "$orig_codec" == "srt" ] || [ "$orig_codec" == "subrip" ]; then ffopts="-map 0:$id -c:s:0 copy \"$output.${lang}${id}.srt\"" echo "$output.${lang}${id}.srt|$id|srt|$orig_codec|$delay|$lang|$title" >> sub.exports @@ -325,24 +393,24 @@ EOF else echo "Invalid HDR10+ Metadata ABORTING" echo "HDR10+ Metadata Frames: $MetadataCount VideoFrames: $FrameCount $MetadataPercent%" - echo "Preserving extracted subs" echo - inputbase=${input%.mkv} - dvmp4sub=`echo ${input%.mkv} | $sed 's/\ DV\|\ HDR10+//g'` - for i in "$dvmp4sub"\ DV-MP4.*.{srt,sup}; do - eval "cp \"$i\" \"`echo "$i" | $sed 's/\ DV-MP4//g' | $sed \"s/.*\.\(.*\)\.\(s..\)/\$inputbase\.\1\.\2/g\"`\""; - done - rm BL.hevc *DV-MP4.asm *DV-MP4.*.srt *DV-MP4.sup ${input}.dvconverting + exit_code=-1 + cleanup continue fi fi ### GRAB CHAPTERS - mkvextract chapters -s "${input}" > chapters.list + if [[ "$SRC_FORMAT" == "mp4" ]]; then + MP4Box -dump-chap-ogg "${input}" && mv "$inputbase".txt chapters.list + else + mkvextract chapters -s "${input}" > chapters.list + fi + - ### MUX MP4 - if [ $EXT_COMPAT == "yes" ]; then + ### FINAL COMPOSITION + if [[ "$EXT_COMPAT" == "yes" && $dv_target -ne 5 ]]; then echo echo "Faking DV$dv_target to DV5 for extended device compatiblity" echo @@ -363,7 +431,9 @@ EOF mp4string+=($mp4opts) tcount=$((tcount+1)) done <<< "$(cat audio.exports)" + if [ $ADDSUBS == "yes" ]; then + echo "Below subtitles will be added to output file" while read i;do stream=`echo "$i" | cut -f1 -d\|` id=`echo "$i" | cut -f2 -d\|` @@ -373,7 +443,6 @@ EOF lang=`echo "$i" | cut -f6 -d\|` title=`echo "$i" | cut -f7 -d\|` echo $stream - echo $codec if [ "$codec" == "srt" ] && [ -s "$stream" ]; then mp4opts="-add \"$stream\":lang=$lang:name=\"$title\" -delay $tcount=$delay" mp4string+=($mp4opts) @@ -381,30 +450,38 @@ EOF fi done <<< "$(cat sub.exports)" fi + if [ -s "chapters.list" ]; then mp4string+=("-chap chapters.list") fi + + echo "" + echo "Muxing to output file" + echo "" mp4string+=("-tmp ./tmp -brand mp42isom -ab dby1 -tags comment=\"${created_tag}\" -new \"$output.mp4\"") echo ${mp4string[*]} eval ${mp4string[*]} + exit_code=$? + + # Preparing NFO ffprobe_output_info=$(ffprobe "${output}.mp4" 2>&1 | grep DOVI | grep -v comment | sed s/://g) echo "Created with dvmkv2mp4 $@ ($VERSION)" > "${output}.nfo" + echo "$source_type_msg" >> "${output}.nfo" echo "--------- SOURCE ------------" >> "${output}.nfo" - echo $ffprobe_info >> "${output}.nfo" + echo $ffprobe_source_info >> "${output}.nfo" mediainfo "${input}" >> "${output}.nfo" echo "--------- TARGET ------------" >> "${output}.nfo" echo $ffprobe_output_info >> "${output}.nfo" mediainfo "${output}.mp4" >> "${output}.nfo" - if [ $DEBUG != "yes" ]; then - cleanup - else - rm "${input}.dvconverting" - rm -rf ./tmp - fi + + cleanup + coment_tag="" + echo "-------------" done end=`date +%s` runtime=$((end-start)) hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 )); -echo +echo "" +echo "-------------------------------" echo "Conversions runtime: $hours:$minutes:$seconds (hh:mm:ss)" echo