Skip to content

Instantly share code, notes, and snippets.

@sqbs
Last active May 1, 2020 02:29
Show Gist options
  • Save sqbs/1a4986452387dbbd697f7cc045c5cfef to your computer and use it in GitHub Desktop.
Save sqbs/1a4986452387dbbd697f7cc045c5cfef to your computer and use it in GitHub Desktop.
burnsub.sh v1.2.1 bugfix
#!/bin/sh
#======================================================================
# burnsub.sh by sqbs
#======================================================================
#
# Special thanks to Bill, lts
#
#======================================================================
# WHAT IS IT?
#======================================================================
#
# A POSIX script for easy subtitle burning onto MKVs
#
# Requires:
#
# - handbrake-cli
# - ffmpeg
# - mkvtoolnix-cli
#
#======================================================================
# WHAT DOES IT DO?
#======================================================================
#
# Outputs an MKV with video + subs merged and a single audio track
#
#======================================================================
# HOW TO USE / EXAMPLES
#======================================================================
#
# When prompted to select audio/sub tracks select the number
# next to mkvextract: (track ID for mkvmerge & mkvextract: 1)
# (track ID for mkvmerge & mkvextract: 2)
# (track ID for mkvmerge & mkvextract: 3)
#
# burnsub.sh file1.mkv file*.mkv file_S*E*.mkv
#
# Ask for audio and sub tracks before each file
#
# burnsub.sh -f file1.mkv file*.mkv file_S*E*.mkv
#
# Same audio and sub tracks selected for all
#
# burnsub.sh -F file1.mkv file*.mkv file_S*E*.mkv
#
# Ignore corrupt/error checks and proceed with the batch
#
# burnsub.sh -fF file1.mkv file*.mkv file_S*E*.mkv
#
# Same audio + sub tracks and ignore errors. Problem files
# are labelled with "_ERR" in the completed filename
#
# burnsub.sh -fS file1.mkv file*.mkv file_S*E*.mkv
#
# Same audio + sub tracks but skip files with errors
#
#======================================================================
# FUNCTIONS
#======================================================================
version="1.2.1"
info() {
echo ""
printf "burnsub.sh v%s " "$version"
printf "\n\n%s\n\n" "https://gist.github.com/1a4986452387dbbd697f7cc045c5cfef"
}
help() {
info
echo "A POSIX script for easy subtitle burning onto MKVs"
echo ""
echo "Syntax: burnsub.sh [-f|F|h|t|T|S|v]"
echo "Options:"
echo ""
echo " f Same audio + subtitle selection for all files"
echo " F Continue on error"
echo " h Display this screen"
echo " t Keep all temp files"
echo " T Keep all temp files only on error"
echo " S Skip on error"
echo " v Display version number"
echo ""
}
get_user_input() {
file=$*
case "${file_ext}" in
"mkv")
printf "\nFile: \"%s\"\n\n" "$file"
;;
*)
printf "\nSource file is not a .mkv\n"
exit 1
;;
esac
mkvinfo "$file" | sed -nr '/[\w\W]*(Track number|Track type|Language|Display|Name)[\w\W]*/p'
printf "\nSelect audio track: "
read -r audio_track
case $audio_track in
*[!0-9]*)
printf "\n\"%s\" is not a valid audio track\n" "$audio_track"
exit 1
;;
*) ;;
esac
printf "Select subtitle track: "
read -r subtitle_track
case $subtitle_track in
*[!0-9]*)
printf "\n\"%s\" is not a valid subtitle track\n" "$subtitle_track"
exit 1
;;
*) ;;
esac
}
print_execution_time() {
time_end=$(date +%s)
runtime=$((time_end - time_start))
min=0
hour=0
day=0
if [ $runtime -gt 59 ]; then
sec=$((runtime % 60))
runtime=$((runtime / 60))
if [ $runtime -gt 59 ]; then
min=$((runtime % 60))
runtime=$((runtime / 60))
if [ $runtime -gt 23 ]; then
hour=$((runtime % 24))
day=$((runtime / 24))
else
hour=$runtime
fi
else
min=$runtime
fi
else
sec=$runtime
fi
[ $day -gt 0 ] && output="$output ${day}d"
[ $hour -gt 0 ] && output="$output ${hour}h"
[ $min -gt 0 ] && output="$output ${min}m"
[ $sec -gt 0 ] && output="$output ${sec}s"
printf "\nExecution time: %s\n\n" "$output"
}
del_temp_files() {
if [ -d "${folder_path}/hc_temp/" ]; then
case "$1" in
"-"*["t"]*) ;;
"-"*["T"]*)
if [ ! "$error_subs" ] && [ ! "$error_video" ] && [ ! "$error_audio" ]; then
find "${folder_path}/hc_temp/" -name "*_*.tmp.*" -type f -delete
fi
;;
*) find "${folder_path}/hc_temp/" -name "*_*.tmp.*" -type f -delete;;
esac
fi
}
end() {
del_temp_files "$1"
print_execution_time
exit 0
}
end_error() {
if [ "$batch" -eq 1 ]; then
case "$1" in
"-"*["F""S"]*) ;;
*)
printf "\n\nUse argument -F to ignore errors and finish batch"
printf "\nUse argument -S to skip on errors and finish batch\n"
;;
esac
fi
case "$1" in
"-"*["T"]*) ;;
*) del_temp_files "$1";;
esac
print_execution_time
exit 1
}
#===========================================================
# INIT
#===========================================================
case "$1" in
"-"*["h"]*) help; exit 0;;
"-"*["v"]*) info; exit 0;;
*);;
esac
time_start=$(date +%s)
[ -x "$(command -v mkvmerge)" ] || pass=false
[ -x "$(command -v ffmpeg)" ] || pass=false
[ -x "$(command -v HandBrakeCLI)" ] || pass=false
[ "$pass" != false ] || { printf "\nPlease install mkvtoolnix-cli + ffmpeg + handbrake-cli and run me again\n\n"; exit 1; }
#===========================================================
# MAIN
#===========================================================
case "$1" in
"-"*)
if [ -z "$2" ]; then
file=0
else
file=1
fi
;;
*)
if [ -z "$1" ]; then
file=0
else
file=1
fi
;;
esac
if [ $file -eq 0 ]; then
printf "\nNo .mkv file specified\n\n"
exit 1
fi
case "$1" in
"-"*)
if [ "$#" -gt 2 ]; then
batch=1
else
batch=0
fi
;;
*)
if [ "$#" -gt 1 ]; then
batch=1
else
batch=0
fi
;;
esac
printf "
/ / /
(___ ___ ___ ___ (___ ___ (___
| )| )| )| )|___ | )| ) |___ | )
|__/ |__/ | | / __/ |__/ |__/ __/ | / v%s
-
" "$version"
for var in "$@"
do
if case "$var" in "-"*["f""F""t""T""S"]*) true;; *) false;; esac; then
continue
fi
filename_ext=$(basename -- "$var")
file_ext="${filename_ext##*.}"
filename=${filename_ext%".$file_ext"}
folder_path=$(realpath "$var")
folder_path=${folder_path%"/$filename_ext"}
case "$1" in
"-"*"f"*)
if [ ! "$received_user_input" ]; then
get_user_input "$var"
received_user_input=true;
fi
;;
*) get_user_input "$var" ;;
esac
printf "\nPreparing \"%s\"...\n" "$filename_ext"
error_video=""
error_audio=""
error_subs=""
del_temp_files ""
mkdir -p "${folder_path}/hc_temp"
printf "\nChecking subtitle format...\n"
check_sub_format=$(mkvmerge -i "$var" | sed -nr "/Track ID $subtitle_track:[\w\W]*/p")
sub_format=""
if echo "$check_sub_format" | grep 'SRT'; then
sub_format="srt"
printf "\n.SRT (Text)\n"
elif echo "$check_sub_format" | grep 'PGS'; then
sub_format="sup"
printf "\n.SUP (Bluray/PGS)\n"
elif echo "$check_sub_format" | grep 'VobSub'; then
sub_format="sub"
printf "\n.IDX/.SUB (VobSub)\n"
fi
if [ ! "$sub_format" ]; then
printf "\nCouldn't detect a supported subtitle format (.srt/.sup/.idx/.sub)\n"
error_subs="_ERR_SUB"
case "$1" in
"-"*"f"*)
if [ $batch -eq 1 ]; then
printf "\nRemove -f argument to select a subtitle track for this file if available\n"
fi
end_error "$1"
;;
"-"*"F"*)
printf "\nContinuing...\n"
;;
"-"*"S"*)
printf "\nSkipping...\n"
continue
;;
*)
end_error "$1"
;;
esac
fi
printf "\nExtracting video...\n"
# if ffmpeg -y -v error -i "$var" -c copy -map 0:v:0 "${folder_path}/hc_temp/${filename}_video_no_sound.tmp.mp4" 2>&1 | grep "Read error"; then
burn_ext="mkv"
if mkvmerge -o "${folder_path}/hc_temp/${filename}_video_no_sound.tmp.mkv" --no-audio --no-subtitles "${folder_path}/${filename_ext}" | grep "error."; then
printf "\nVideo track is corrupt.\n"
error_video="_ERR_VIDEO"
case "$1" in
"-"*"F"*)
printf "\nContinuing...\n"
;;
"-"*"S"*)
printf "\nSkipping...\n"
del_temp_files "$1"
continue
;;
*)
printf "\nCan't extract video from .mkv container\n"
end_error "$1"
;;
esac
fi
printf "\nExtracting audio and subtitles...\n"
if mkvextract tracks "$var" "${audio_track}:${folder_path}/hc_temp/${filename}_audio.tmp.ac3" "${subtitle_track}:${folder_path}/hc_temp/${filename}_subtitles.tmp.${sub_format}" | tail -1 | grep -v "100%"; then
printf "\nAudio/subtitles track is corrupt.\n"
error_audio="_ERR_AUDIO_SUB"
case "$1" in
"-"*"F"*)
printf "\nContinuing...\n"
;;
"-"*"S"*)
printf "\nSkipping...\n"
del_temp_files "$1"
continue
;;
*)
printf "\nCan't extract audio/subs from .mkv container\n"
end_error "$1"
;;
esac
fi
case "$sub_format" in
"srt")
printf "\nBurning subtitles to video...\n"
ffmpeg -y -i "${folder_path}/hc_temp/${filename}_video_no_sound.tmp.${burn_ext}" -vf "subtitles='${folder_path}/hc_temp/${filename}_subtitles.tmp.${sub_format}'" -c:v libx265 -crf 20 -preset ultrafast "${folder_path}/hc_temp/${filename}_video_hardsubs_no_sound.tmp.${burn_ext}"
;;
"sup" | "sub")
printf "\nMuxing video with subtitles...\n"
if echo "$sub_format" | grep "sup"; then
mkvmerge -o "${folder_path}/hc_temp/${filename}_video_subs_no_sound.tmp.mkv" "${folder_path}/hc_temp/${filename}_video_no_sound.tmp.${burn_ext}" "${folder_path}/hc_temp/${filename}_subtitles.tmp.${sub_format}"
else
mkvmerge -o "${folder_path}/hc_temp/${filename}_video_subs_no_sound.tmp.mkv" "${folder_path}/hc_temp/${filename}_video_no_sound.tmp.${burn_ext}" "${folder_path}/hc_temp/${filename}_subtitles.tmp.idx" "${folder_path}/hc_temp/${filename}_subtitles.tmp.sub"
fi
printf "\nBurning subtitles to video...\n"
HandBrakeCLI -i "${folder_path}/hc_temp/${filename}_video_subs_no_sound.tmp.mkv" -o "${folder_path}/hc_temp/${filename}_video_hardsubs_no_sound.tmp.mkv" --subtitle 1 --subtitle-burned
;;
esac
printf "\nMuxing video with audio...\n"
mkvmerge -o "${folder_path}/hc_temp/${filename}_HC${error_video}${error_audio}${error_subs}.tmp.mkv" "${folder_path}/hc_temp/${filename}_video_hardsubs_no_sound.tmp.${burn_ext}" "${folder_path}/hc_temp/${filename}_audio.tmp.ac3"
mkdir -p "${folder_path}/hc_done"
mv "${folder_path}/hc_temp/${filename}_HC${error_video}${error_audio}${error_subs}.tmp.mkv" "${folder_path}/hc_done/${filename}_HC${error_video}${error_audio}${error_subs}.mkv"
printf "\nCleaning up...\n"
del_temp_files "$1"
done
end "$1"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment