SHARE
TWEET

HandBrake HEVC encode script

a guest Nov 19th, 2019 111 in 7 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/bin/bash
  2. # This script will:
  3. # * Parse the input filename, get movie info from IMDb, incl. name
  4. #   and year.
  5. # * Extract core DTS track from DTS-HD MA track (with ffmpeg).
  6. # * Remux a Blu-Ray remux (in an M2TS or MKV), without all its
  7. #   audio tracks, with the extracted DTS track (with mkvmerge).
  8. # * Encode the remuxed MKV to HEVC in HandBrake.
  9. # * Create info txt files for input, output, and remux output.
  10.  
  11. # The input file should be a Blu-Ray remux, that has identical bitrate
  12. # to the original Blu-Ray disc, for the video and audio tracks.
  13. # As an example, this is the video bitrate of
  14. # the "The Imaginarium of Doctor Parnassus (2009)" Blu-Ray:
  15. # 29495915 b/s = 29495.915 kb/s = 29.495915 mb/s
  16.  
  17. # Output bitrate:
  18. # HEVC video track: 5000 kb/s
  19. # DTS audio track: 1536 kb/s
  20. # video track + audio track = 6536 kilobits / s
  21. # kilobits to kilobytes: 817 kilobytes / s
  22.  
  23. # So, you need to know the length of the input video in order to
  24. # figure out how large the output file will be:
  25. # seconds * bitrate = output size
  26.  
  27. # The subtitles will add a bit of extra size.
  28.  
  29. # First argument to this script should be a file, for example an
  30. # M2TS or MKV.
  31. # Second argument is an optional switch (-grain), which tells the script
  32. # whether or not to use the '--encoder-tune grain' HandBrake argument.
  33. # This is only needed if the input video has a lot of film grain.
  34.  
  35. # Attention:
  36.  
  37. # It's recommended to run this script in a background TTY.
  38. # For example: TTY4 (Ctrl + Alt + F4)
  39.  
  40. # Because then HandBrake can be remotely paused from the desktop:
  41.  
  42. # kill -s 20 PID
  43.  
  44. # And then resumed:
  45.  
  46. # kill -s 18 PID
  47.  
  48. # This script finishes as it's supposed to.
  49.  
  50. # The HandBrake PID can be found by running:
  51. # ps -C HandBrakeCLI -o pid,args
  52.  
  53. # One more thing...:
  54.  
  55. # This script assumes that you're not a n00b, but a hacker.
  56.  
  57. # Signs of being a hacker:
  58. # * uses Linux
  59. # * uses an SSD for the root partition
  60. # * uses a larger magnetic hard drive for the /home partition
  61.  
  62. # Hence, getting speed, as well as storage. Always keeping
  63. # the root (/) partition and the /home partition separate. In case of
  64. # wanting to format and reinstall Linux, or try a different distro.
  65. # The hacker gets to keep his / her data, because the hacker is smart.
  66.  
  67. # TL;DR The script assumes that you have enough free space in $HOME.
  68. # The output, and remux output, files are quite large.
  69.  
  70. # 2019-10-04
  71. # Changed the 'is_handbrake' function and moved some code to it.
  72. # Now the script waits with the encoding until all other
  73. # HandBrake processes have exited.
  74. # The code, that checks if commands are available for the system, now
  75. # looks cleaner.
  76. # Moved running any command, saving the exit status, and quitting if
  77. # the exit status is false, to its own function called 'run_or_quit'.
  78. # Changed the 'imdb' function to use curl, instead of lynx.
  79. # 2019-10-06
  80. # Changed break_name function, so that it now outputs the unchanged
  81. # input filename if it can't be parsed.
  82. # Added a part to the if statement in the imdb function (in 'get_imdb'),
  83. # so it still works even if $id is empty.
  84. # Changed the regex:es in 'parse_imdb', to take into account that some
  85. # titles on IMDb say "Movie (TV Movie 1999)", and the like.
  86. # Added a part that searches IMDb even if the year is unknown.
  87. # The year in the output filenames will be set to '0000' if it wasn't
  88. # found on IMDb.
  89. # Added a part to the command in the 'remux_mkv' function, that sets
  90. # the language of the DTS track to be the same as in $lang. Otherwise,
  91. # the language code of the audio track will be undefined in the
  92. # finished file.
  93. # 2019-10-07
  94. # Added new function called 'uriencode', which will encode special
  95. # characters in any string to their URL counterparts. It will be used
  96. # with the 'imdb' function.
  97. # Changed the mkvmerge command in the 'remux_mkv' function, to set an
  98. # empty title, in case the source file has a title set. We don't want
  99. # to copy that over to the finished rip.
  100. # 2019-10-13
  101. # Added a new function called 'fsencode', which will delete special
  102. # characters from the output filename, that aren't allowed on certain
  103. # filesystems.
  104. # Fixed the 'is_handbrake' function, so that it always parses the
  105. # PID and command arguments correctly.
  106. # 2019-10-27
  107. # Added a function called 'is_torrent', which recognizes if input file
  108. # is an unfinished download, and waits for the file to fully download
  109. # before processing it.
  110. # 2019-11-15
  111. # Shortened the 'info_txt' function by a couple of lines.
  112. # 2019-11-17
  113. # Replaced the 'uriencode' function with a curl command, since URL
  114. # translation can be done directly in curl.
  115. # Replaced redundant 'echo' commands with the <<< here string.
  116.  
  117. # Creates a variable that will work as a switch. If this variable
  118. # is set to '1', it will skip running the 'dts_extract' and 'remux_mkv'
  119. # functions. This is handy if that file has already been created in
  120. # a previous session of this script.
  121. existing=0
  122.  
  123. # Gets full path of input file.
  124. if=$(readlink -f "$1")
  125. bname=$(basename "$if")
  126.  
  127. # Creates a function called 'usage', which echoes the syntax,
  128. # some basic info, and quits.
  129. usage () {
  130.     bname=$(basename "$0")
  131.     echo -e "Usage: ${bname} [M2TS|MKV] [-grain]\n"
  132.     echo -e "This script encodes an input M2TS or MKV Blu-Ray remux to HEVC.\n"
  133.     echo "The input file should be a Blu-Ray remux, that has identical bitrate"
  134.     echo "to the original Blu-Ray disc, for the video and audio tracks."
  135.     echo "As an example, this is the video bitrate of"
  136.     echo "the \"The Imaginarium of Doctor Parnassus (2009)\" Blu-Ray:"
  137.     echo -e "29495915 b/s = 29495.915 kb/s = 29.495915 mb/s\n"
  138.     echo -e "The output filenames will be in the home directory of user: ${USER}\n"
  139.     echo -e "\tOptions:\n"
  140.     echo '-grain'
  141.     echo -e "\tOnly needed if the input file has film grain.\n"
  142.     echo -e "\tAttention:\n"
  143.     echo "It's recommended to run this script in a background TTY."
  144.     echo -e "For example: TTY4 (Ctrl + Alt + F4)\n"
  145.     echo -e "Because then HandBrake can be remotely paused from the desktop:\n"
  146.     echo "kill -s 20 PID"
  147.     echo -e "\nAnd then resumed:\n"
  148.     echo "kill -s 18 PID"
  149.     echo -e "\nThis script finishes as it's supposed to.\n"
  150.     echo "The HandBrake PID can be found by running:"
  151.     echo -e "ps -C HandBrakeCLI -o pid,args\n"
  152.     exit
  153. }
  154.  
  155. # Creates a function called 'is_torrent', which checks if the filename
  156. # ends with '.part', or if there's a filename in the same directory that
  157. # ends with '.part'. If there is, wait until the filename changes, and
  158. # '.part' is removed from the filename. This function recognizes if
  159. # input file is an unfinished download, and waits for the file to fully
  160. # download before processing it.
  161. is_torrent () {
  162.  
  163.     if [[ $if =~ .part$ ]]; then
  164.         if_tmp="$if"
  165.     else
  166.         if_tmp="${if}.part"
  167.     fi
  168.  
  169.     if [[ -f $if_tmp ]]; then
  170.  
  171.         echo -e "\nWaiting for this download to finish:"
  172.         echo -e "${if_tmp}\n"
  173.  
  174.         while [[ -f $if_tmp ]]; do
  175.  
  176.             sleep 5
  177.  
  178.         done
  179.  
  180.         if="${if%.part}"
  181.  
  182.         md5=$(md5sum -b "$if")
  183.         md5_f="${HOME}/${bname}_MD5-${RANDOM}.txt"
  184.  
  185.         echo "$md5" | tee "$md5_f"
  186.  
  187.     fi
  188. }
  189.  
  190. is_torrent
  191.  
  192. # If first argument is empty, or is not a real file, then print
  193. # syntax and quit. If second argument exists, but isn't '-grain', then
  194. # print syntax and quit.
  195. if [[ -z $1 || ! -f $if ]]; then
  196.     usage
  197. elif [[ $2 && $2 != '-grain' ]]; then
  198.     usage
  199. fi
  200.  
  201. # Checks if the second argument to the script is '-grain'.
  202. # If so, set $grain to 1, otherwise 0.
  203. if [[ $2 == '-grain' ]]; then
  204.     grain=1
  205. else
  206.     grain=0
  207. fi
  208.  
  209. # Generates a random number, which can be used for these filenames:
  210. # dts track, output, output remux, input info txt, output info txt,
  211. # output remux info txt.
  212. session="$RANDOM"
  213.  
  214. # Creates a variable that will contain the exit status of all
  215. # commands run in the script.
  216. exit_status=0
  217.  
  218. # Creates a backup of the $IFS variable.
  219. IFS_bak="$IFS"
  220.  
  221. # Creates an array of the list of commands needed by this script.
  222. cmd=(HandBrakeCLI ffmpeg mkvmerge curl)
  223.  
  224. # Declares an associative array (hash), which contains the package names
  225. # of the commands that are needed by the script.
  226. declare -A pkg
  227. pkg[${cmd[0]}]='HandBrake'
  228. pkg[${cmd[1]}]='ffmpeg'
  229. pkg[${cmd[2]}]='mkvtoolnix'
  230. pkg[${cmd[3]}]='curl'
  231.  
  232. # Checks if HandBrake, ffmpeg, mkvtoolnix and curl are available on
  233. # this system. If not, display a message, and quit.
  234. for cmd_tmp in ${cmd[@]}; do
  235.  
  236.     check=$(basename $(command -v ${cmd_tmp}) 2>&-)
  237.  
  238.     if [[ -z $check ]]; then
  239.         echo -e "\nYou need ${pkg[${cmd_tmp}]} installed on your system."
  240.         echo -e "Install it through your package manager.\n"
  241.         exit
  242.     fi
  243. done
  244.  
  245. # Declares the $imdb associative array (hash), which will store all the
  246. # IMDb id:s that have been processed, to prevent the same id from being
  247. # processed twice.
  248. declare -A imdb
  249.  
  250. # Sets the default language to English. This language code is what
  251. # the script till look for when extracting the core DTS track.
  252. lang='eng'
  253.  
  254. # Setting some variables that will be used to create a full HandBrake
  255. # command, with args.
  256. format='av_mkv'
  257. v_encoder='x265_10bit'
  258. preset='slow'
  259. v_bitrate=5000
  260. a_encoder='copy:dts'
  261.  
  262. # Creates a variable which contains the last part of the output
  263. # filename.
  264. rls_type='1080p.BluRay.HEVC.DTS'
  265.  
  266. # This creates a function called 'fsencode', which will delete special
  267. # characters that are not allowed in filenames on certain filesystems.
  268. # The characters in the regex are allowed. All others are deleted.
  269. # Based on the "POSIX fully portable filenames" entry:
  270. # https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations
  271. fsencode () {
  272.  
  273.     sed 's/[^ A-Za-z0-9._-]//g' <<<"$1"
  274.  
  275. }
  276.  
  277. # This creates a function called 'uriencode', which will translate
  278. # the special characters in any string to be URL friendly. This will be
  279. # used in the 'imdb' function.
  280. uriencode () {
  281.  
  282.     curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" <<<"${@}" | sed -E 's/..(.*).../\1/'
  283.  
  284. }
  285.  
  286. # This creates a function called 'break_name', which will break up
  287. # the input filename, and parse it, to extract the movie name, and year.
  288. break_name () {
  289.  
  290.     # Sets $bname to the first argument passed to this function.
  291.     bname="$1"
  292.  
  293.     declare -a name
  294.  
  295.     temp=$(grep -Eo "^.*[0-9]{4}([[:punct:]]|[[:space:]]){1}" <<<"$bname" | sed 's/^[[:space:]]*\[.*\][[:space:]]*//')
  296.  
  297.     # If $temp can't be parsed, set it to the input filename instead,
  298.     # although limit the string by 64 characters, and remove possible
  299.     # trailing whitespace from the string.
  300.     if [[ -z $temp ]]; then
  301.         temp=$(sed 's/ *$//' <<<"${bname:0:64}")
  302.     fi
  303.  
  304.     # Break $bname up in a list of words, and store those
  305.     # words in arrays, depending on whether $bname
  306.     # is separated by dots, hyphens, underscores or spaces.
  307.     bname_dots=( $(cut -d'.' --output-delimiter=' ' --fields=1- <<<"$temp") )
  308.     bname_hyphens=( $(cut -d'-' --output-delimiter=' ' --fields=1- <<<"$temp") )
  309.     bname_underscores=( $(cut -d'_' --output-delimiter=' ' --fields=1- <<<"$temp") )
  310.     bname_spaces=( $(cut -d' ' --output-delimiter=' ' --fields=1- <<<"$temp") )
  311.  
  312.     # Declares an associative array (hash), that stores the element
  313.     # numbers for each kind of word separator: dots, hyphens,
  314.     # underscores, spaces.
  315.     declare -A bname_elements
  316.     bname_elements[dots]=${#bname_dots[@]}
  317.     bname_elements[hyphens]=${#bname_hyphens[@]}
  318.     bname_elements[underscores]=${#bname_underscores[@]}
  319.     bname_elements[spaces]=${#bname_spaces[@]}
  320.  
  321.     # If there are more dots in $bname than hyphens, underscores
  322.     # or spaces, that means $bname is separated by dots. Otherwise,
  323.     # it's separated by hyphens, underscores or spaces. In either case,
  324.     # loop through the word list in either array, and break the name up
  325.     # in separate words. The last element is the year, so do a regex
  326.     # on that to filter out other characters besides four digits.
  327.  
  328.     elements=0
  329.  
  330.     # This for loop is to figure out if $bname is separated by
  331.     # dots, hyphens, underscores or spaces.
  332.     for type in dots hyphens underscores spaces; do
  333.         temp_number="bname_elements[${type}]"
  334.  
  335.         if [[ ${!temp_number} -gt $elements ]]; then
  336.             elements="${!temp_number}"
  337.             temp_type="$type"
  338.         fi
  339.     done
  340.  
  341.     # This for loop is to go through the word list. The last element is
  342.     # the year, so do a regex on that to filter out other characters
  343.     # besides four digits.
  344.     for (( i = 0; i < $elements; i++ )); do
  345.  
  346.         # Creates a reference, pointing to the $i element of the
  347.         # 'bname_$temp_type' array.
  348.         array_ref="bname_${temp_type}[${i}]"
  349.  
  350.         name[${i}]=$(tr -d '[:space:]' <<<"${!array_ref}")
  351.  
  352.         if [[ $i -eq $(( elements - 1 )) ]]; then
  353.             year=$(grep -Eo "([[:punct:]]|[[:space:]])*[0-9]{4}([[:punct:]]|[[:space:]])*$" <<<"${!array_ref}" | tr -d '[:punct:]')
  354.  
  355.             # If the $year variable is set, use it, otherwise use
  356.             # '0000' as the year, adding it as the last element to
  357.             # the $name array.
  358.             if [[ $year ]]; then
  359.                 name[${i}]="(${year})"
  360.             else
  361.                 name+=('(0000)')
  362.             fi
  363.         fi
  364.     done
  365.  
  366.     # Echoes the complete parsed name.
  367.     echo "${name[@]}"
  368.  
  369. }
  370.  
  371. imdb () {
  372.  
  373.     shopt -s expand_aliases
  374.     alias curl='curl --silent --connect-timeout 10'
  375.  
  376.     #agent='Lynx/2.8.9rel.1 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/1.1.1d'
  377.     agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
  378.  
  379.     # https://www.krazyworks.com/imdb-lookup-script/
  380.     #IMDB is one of the most complete movie and television databases on the Internet. It offers an advanced Web search UI, but Unix CLI support has been discontinued years ago and the old scripts no longer work. There are a few nice third-party scripts out there for searching IMDB. Unfortunately, most of them are not maintained and won’t work right without some tweaking.
  381.  
  382.     #Here’s my simplistic version that should be a breeze to maintain and it will provide you with the basics: movie description and rating. You need to feed it the movie title and release year like so: “The Godfather (1972)”.
  383.  
  384.     #You can also leave the year out and just enter “The Godfather”. This will use Google search instead for the imdb.com domain. Depending on the uniqueness of the title, the accuracy may drop. Additionally, running too many Google queries in a short period of time may cause Google to temporarily bad your IP or start prompting for captcha.
  385.  
  386.     #Originally I wrote this to go through a list of movie titles and help me figure what’s worth watching. No special dependencies apart from lynx, but that’s easy enough to install. Save the following as /usr/bin/imdb and run like so:
  387.  
  388.     # 2019-09-21: Modified to run inside a function, and output only basic information.
  389.     # 2019-09-21: Changed the $tmpfile variable, to save the temp file in /dev/shm, instead of /tmp.
  390.     # This is faster, because /dev/shm is a RAM disk. It also keeps the root drive from being written to unnecessarily.
  391.     # It's common for SSDs to be used as a root drive, and they wear out easily if constantly written to.
  392.     # 2019-10-04: Modified to use curl instead of lynx.
  393.     # 2019-10-06: Added a part to the if statement in 'get_imdb', so it still works even if $id is empty.
  394.     # 2019-10-06: Added an if statement which checks if the year is '0000', which means that the year is undefined, and then it searches for all movies with that title, no matter the year the movie was made.
  395.     # 2019-10-06: Changed the regex:es in 'parse_imdb', to take into account that some titles on IMDb say "Movie (TV Movie 1999)", and the like.
  396.     # 2019-10-06: Added categories to include in the IMDb search results. Now it includes: Feature film, TV movie, TV special, Documentary, Video.
  397.     # 2019-10-07: Changed the $y and $t variables. Now the $t variable relies on a new function called 'uriencode', which will encode special characters to their URL counterparts.
  398.  
  399.     if [ $# -eq 0 ]
  400.     then
  401.         echo 'Usage: imdb "Movie Title (Year)"'
  402.         exit 1
  403.     else
  404.         y=$(grep -Eo "\([0-9]{4}\)$" <<<"${@}" | tr -d '[:punct:]')
  405.         t=$(uriencode "$(sed 's/ ([0-9]\{4\})$//' <<<"${@}")")
  406.     fi
  407.  
  408.     configure() {
  409.         tmpfile="/dev/shm/imdb-mf_${RANDOM}.tmp"
  410.     }
  411.  
  412.     cleanup() {
  413.         if [ -f "${tmpfile}" ]
  414.         then
  415.             /bin/rm -f "${tmpfile}"
  416.         fi
  417.     }
  418.  
  419.     get_imdb() {
  420.  
  421.         # Sets the type of IMDb search results to include.
  422.         type='feature,tv_movie,tv_special,documentary,video'
  423.  
  424.         # If the year is set to '0000', that means it's unknown,
  425.         # hence we will need to use slightly different URLs, when
  426.         # searching for the movie.
  427.  
  428.         if [[ $y == '0000' ]]; then
  429.             url_tmp="https://www.imdb.com/search/title/?title=${t}&title_type=${type}&view=simple"
  430.             url_tmp2="https://www.google.com/search?hl=en&as_q=${t}&as_sitesearch=imdb.com"
  431.         else
  432.             url_tmp="https://www.imdb.com/search/title/?title=${t}&title_type=${type}&release_date=${y},${y}&view=simple"
  433.             url_tmp2="https://www.google.com/search?hl=en&as_q=${t}+${y}&as_sitesearch=imdb.com"
  434.         fi
  435.  
  436.         id=$(curl "${url_tmp}" | grep -Eo "<a href=\"/title/tt[0-9]{4,}/\"" | grep -Eo "tt[0-9]{4,}" | head -n 1)
  437.         id2=$(curl --user-agent "${agent}" "${url_tmp2}" | grep -Eo "https://www.imdb.com/title/tt[0-9]{4,}" | grep -Eo "tt[0-9]{4,}" | head -n 1)
  438.  
  439.  
  440.         # In case IMDb and Google give different IMDb IDs, use the one
  441.         # from the Google search results. If $id is empty, but $id2
  442.         # isn't, use $id2. Else, use $id.
  443.  
  444.         if [[ $id && $id2 && $id != $id2 ]]; then
  445.             url="https://www.imdb.com/title/${id2}/"
  446.         elif [[ -z $id && $id2 ]]; then
  447.             url="https://www.imdb.com/title/${id2}/"
  448.         else
  449.             url="https://www.imdb.com/title/${id}/"
  450.         fi
  451.  
  452.         # Exports the content of $url to $tmpfile.
  453.         curl -o "${tmpfile}" "$url" 2>&-
  454.  
  455.     }
  456.  
  457.     parse_imdb() {
  458.  
  459.         if [[ ! -f $tmpfile ]]; then
  460.             return
  461.         fi
  462.  
  463.         full=$(grep -Eo "<meta property='og:title' content=\".* \(.*[0-9]{4}\) - IMDb\"" "${tmpfile}" | cut -d'"' -f2 | sed 's/ - IMDb$//')
  464.  
  465.         title=$(sed 's/ (.*[0-9]\{4\})$//' <<<"$full")
  466.         year=$(grep -Eo "[0-9]{4}\)$" <<<"$full" | tr -d '[:punct:]')
  467.  
  468. # Saving these because the regex:es might be useful in the future.
  469. #       temp=$(grep "og:description" "${tmpfile}" | sed -e 's/content="/@/g' -e 's/" \/>/@/g' -e 's/\&quot;/\"/g' | awk -F'@' '{print $(NF-1)}')
  470. #       director=$(echo "${temp}" | cut -d'.' -f1 | sed 's/^Directed by //')
  471. #       cast=$(echo "${temp}" | cut -d'.' -f2 | sed 's/^ *//')
  472. #       plot=$(echo "${temp}" | cut -d'.' -f3 | sed 's/^ *//')
  473. #       rating=$(grep -i "ratingValue" "${tmpfile}" | head -n 1 | cut -d'"' -f4)
  474.     }
  475.  
  476.     print_imdb() {
  477.  
  478.         if [[ $id && ${imdb[${id}]} -ne 1 ]]; then
  479.             imdb[${id}]=1
  480.             echo "$title"
  481.             echo "$year"
  482.         fi
  483.  
  484.     }
  485.  
  486.     # RUNTIME
  487.  
  488.     configure
  489.     cleanup
  490.     get_imdb
  491.     parse_imdb
  492.     print_imdb
  493.     cleanup
  494. }
  495.  
  496. # Creates a function called 'dts_extract', which will find a DTS-HD MA
  497. # track (if it exists), with the same language code as in $lang.
  498. # If this track is found, extract the core DTS track from it, and save
  499. # it as $dts_tmp.
  500. dts_extract () {
  501.  
  502.     for (( i = 0; i < ${#if_info[@]}; i++ )); do
  503.  
  504.         # See if the current line is a DTS-HD MA track, and the same
  505.         # language as $lang.
  506.         dts_track=$(grep 'dts (DTS-HD MA)' <<<"${if_info[${i}]}" | grep "(${lang})")
  507.  
  508.         if [[ $dts_track ]]; then
  509.  
  510.             # Gets the ffmpeg map code of DTS-HD MA track.
  511.             map=$(grep -Eo "Stream #0:[0-9]{1,2}" <<<"$dts_track" | sed 's/Stream #//')
  512.  
  513.             # Creates ffmpeg command.
  514.             # Runs ffmpeg, and extracts the core DTS track.
  515.             args=(${cmd[1]} -i \"${if}\" -map ${map} -bsf:a dca_core -c:a copy \"${dts_tmp}\")
  516.  
  517.             echo -e "\nCommand used to extract core DTS track:" | tee --append "$command_f"
  518.             echo ${args[@]} | tee --append "$command_f"
  519.  
  520.             if [[ $existing -ne 1 ]]; then
  521.  
  522.                 # Runs ffmpeg. If the command wasn't successful, quit.
  523.                 run_or_quit
  524.  
  525.                 # If the ffmpeg command was successful, return from this
  526.                 # function, otherwise quit.
  527.                 if [[ $exit_status -eq 0 ]]; then
  528.                     return
  529.                 fi
  530.             else
  531.  
  532.                 return
  533.             fi
  534.         fi
  535.     done
  536. }
  537.  
  538. # Creates a function called 'remux_mkv', which will remux the input
  539. # file, without all its audio tracks, with the core DTS track.
  540. remux_mkv () {
  541.  
  542.     # Creates mkvmerge command.
  543.     args=(${cmd[2]} --title \"\" -o \"${of_remux}\" --no-audio \"${if}\" --language "0:${lang}" \"${dts_tmp}\")
  544.  
  545.     # Echoes the full mkvmerge command, and executes it.
  546.     echo -e "\nCommand used to remux:" | tee --append "$command_f"
  547.     echo ${args[@]} | tee --append "$command_f"
  548.  
  549.     if [[ $existing -ne 1 ]]; then
  550.  
  551.         # Runs mkvmerge. If the command wasn't successful, quit.
  552.         run_or_quit
  553.  
  554.         # If mkvmerge command was successful, remove the extracted
  555.         # DTS track.
  556.         # If the command failed, quit.
  557.         if [[ $exit_status -eq 0 ]]; then
  558.             rm "$dts_tmp"
  559.         fi
  560.     fi
  561. }
  562.  
  563. # Creates a function called 'hb_encode', which will generate a full
  564. # HandBrake command (with args), and then execute it.
  565. hb_encode () {
  566.  
  567.     # Creates the HandBrake command, which will be used to encode the
  568.     # input file to a HEVC output file.
  569.     # Depending on whether $grain is set to 1 or 0, we get a different
  570.     # HandBrake command.
  571.     if [[ $grain -eq 1 ]]; then
  572.         args=(${cmd[0]} --format $format --markers --encoder $v_encoder --encoder-preset $preset --encoder-tune grain --vb $v_bitrate --two-pass --vfr --aencoder $a_encoder --all-subtitles -i \"${of_remux}\" -o \"${of}\")
  573.     elif [[ $grain -eq 0 ]]; then
  574.         args=(${cmd[0]} --format $format --markers --encoder $v_encoder --encoder-preset $preset --vb $v_bitrate --two-pass --vfr --aencoder $a_encoder --all-subtitles -i \"${of_remux}\" -o \"${of}\")
  575.     fi
  576.  
  577.     # Echoes the full HandBrake command, and executes it.
  578.     echo -e "\nCommand used to encode:" | tee --append "$command_f"
  579.     echo ${args[@]} | tee --append "$command_f"
  580.  
  581.     # Runs HandBrake. If the command wasn't successful, quit.
  582.     run_or_quit
  583. }
  584.  
  585. # Creates a function called 'info_txt', which creates info txt files
  586. # containing information generated by ffmpeg. It creates a separate
  587. # txt file for input file, output file and remux output.
  588. info_txt () {
  589.  
  590.     # Creates the basename of $of and $of_remux.
  591.     of_bname=$(basename "$of")
  592.     of_remux_bname=$(basename "$of_remux")
  593.  
  594.     # Creates filenames for the info txt files, which contain the
  595.     # information generated by 'ffmpeg'.
  596.     # Also creates filenames for HandBrake version and options.
  597.     if_info_f="${info_dir}/${bname}_info.txt"
  598.     of_info_f="${info_dir}/${of_bname}_info.txt"
  599.     of_remux_info_f="${info_dir}/${of_remux_bname}_info.txt"
  600.     hb_version_info_f="${info_dir}/${cmd[0]}_version.txt"
  601.     hb_opts_info_f="${info_dir}/${cmd[0]}_options.txt"
  602.  
  603.     # If the info txt filenames already exist, add a random number to the
  604.     # end of the filename. Also, create empty files with those names.
  605.     if [[ -f $if_info_f ]]; then
  606.         if_info_f="${info_dir}/${bname}_info-${session}.txt"
  607.     fi
  608.  
  609.     if [[ -f $of_info_f ]]; then
  610.         of_info_f="${info_dir}/${of_bname}_info-${session}.txt"
  611.     fi
  612.  
  613.     if [[ -f $of_remux_info_f ]]; then
  614.         of_remux_info_f="${info_dir}/${of_remux_bname}_info-${session}.txt"
  615.     fi
  616.  
  617.     touch "$if_info_f" "$of_info_f" "$of_remux_info_f"
  618.  
  619.     # If '$hb_version_info_f' or '$hb_opts_info_f' exist as files,
  620.     # remove them.
  621.     if [[ -f $hb_version_info_f ]]; then
  622.  
  623.         rm "$hb_version_info_f"
  624.     elif [[ -f $hb_opts_info_f ]]; then
  625.  
  626.         rm "$hb_opts_info_f"
  627.     fi
  628.  
  629.     # Create new empty files, called '$hb_version_info_f' and
  630.     # '$hb_opts_info_f'.
  631.     touch "$hb_version_info_f" "$hb_opts_info_f"
  632.  
  633.     # Sets IFS to newline.
  634.     IFS=$'\n'
  635.  
  636.     # Gets information about output file.
  637.     of_info=( $(eval ${cmd[1]} -hide_banner -i \"${of}\" 2>&1) )
  638.  
  639.     # Gets information about output file.
  640.     of_remux_info=( $(eval ${cmd[1]} -hide_banner -i \"${of_remux}\" 2>&1) )
  641.  
  642.     # Gets HandBrake version.
  643.     hb_version=( $(eval ${cmd[0]} --version 2>&- | grep -E "^HandBrake [[:digit:]]") )
  644.  
  645.     # Gets the list of HandBrake options.
  646.     hb_help=( $(eval ${cmd[0]} --help 2>&-) )
  647.  
  648.     IFS="$IFS_bak"
  649.  
  650.     # Echoes the information gathered from the input file, by ffmpeg.
  651.     for (( i = 0; i < ${#if_info[@]}; i++ )); do
  652.         echo "${if_info[${i}]}" >> "$if_info_f"
  653.     done
  654.  
  655.     # Echoes the information gathered from the output file, by ffmpeg.
  656.     for (( i = 0; i < ${#of_info[@]}; i++ )); do
  657.         echo "${of_info[${i}]}" >> "$of_info_f"
  658.     done
  659.  
  660.     # Echoes the information gathered from the remux output file,
  661.     # by ffmpeg.
  662.     for (( i = 0; i < ${#of_remux_info[@]}; i++ )); do
  663.         echo "${of_remux_info[${i}]}" >> "$of_remux_info_f"
  664.     done
  665.  
  666.     # Echoes the version and options of HandBrake.
  667.     echo "${hb_version[0]}" > "$hb_version_info_f"
  668.     for (( i = 0; i < ${#hb_help[@]}; i++ )); do
  669.         echo "${hb_help[${i}]}" >> "$hb_opts_info_f"
  670.     done
  671. }
  672.  
  673. # Creates a function called 'run_or_quit', which will run any command
  674. # stored in the $args array, and quit if the command returns a false
  675. # exit status.
  676. run_or_quit () {
  677.     eval ${args[@]}
  678.  
  679.     exit_status="$?"
  680.  
  681.     if [[ $exit_status -ne 0 ]]; then
  682.         exit
  683.     fi
  684. }
  685.  
  686. # Creates a function called 'is_handbrake', which will check if there
  687. # are any running HandBrake processes, and if so, wait.
  688. is_handbrake () {
  689.  
  690.     args=(ps -C ${cmd[0]} -o pid,args \| tail -n +2)
  691.  
  692.     # Sets IFS to newline.
  693.     IFS=$'\n'
  694.  
  695.     # Checks if HandBrake is running.
  696.     hb_pids=( $(eval ${args[@]}) )
  697.  
  698.     IFS="$IFS_bak"
  699.  
  700.     # Prints the PID and arguments of the HandBrake commands that
  701.     # are running, if any.
  702.     if [[ ${hb_pids[0]} ]]; then
  703.         echo -e "\nWaiting for this to finish:\n"
  704.         for (( i = 0; i < ${#hb_pids[@]}; i++ )); do
  705.  
  706.             ps_tmp=( $(sed 's/ \+/ /' <<<"${hb_pids[${i}]}" | cut -d' ' -f1-) )
  707.  
  708.             pid="${ps_tmp[0]}"
  709.             unset ps_tmp[0]
  710.             comm="${ps_tmp[@]}"
  711.  
  712.             echo "PID: ${pid}"
  713.             echo -e "COMMAND: ${comm}\n"
  714.         done
  715.     fi
  716.  
  717.     # Starts the loop that will wait for HandBrake to finish.
  718.     while [[ ${hb_pids[0]} ]]; do
  719.  
  720.         # Sleeps for 5 seconds.
  721.         sleep 5
  722.  
  723.         # Unsets the $hb_pids array.
  724.         unset -v hb_pids
  725.  
  726.         # Sets IFS to newline.
  727.         IFS=$'\n'
  728.  
  729.         hb_pids=( $(eval ${args[@]}) )
  730.  
  731.         IFS="$IFS_bak"
  732.     done
  733. }
  734.  
  735. # Creates a function called 'if_m2ts', which will be called if
  736. # input file is an M2TS, in the directory structure '/BDMV/STREAM/'.
  737. # The function outputs a name, which can be used with the 'break_name'
  738. # function, to get the movie information from IMDb. If the input
  739. # filename doesn't match regex '/BDMV/STREAM/[[:digit:]]{5}.m2ts$',
  740. # return from this function, hence leaving the $if_m2ts variable empty.
  741. if_m2ts () {
  742.  
  743.     if [[ ! $if =~ /BDMV/STREAM/[[:digit:]]{5}.m2ts$ ]]; then
  744.         return
  745.     fi
  746.  
  747.     count=$(grep -o '/' <<<"$if" | grep -c .)
  748.     bd_title_field=$(( count - 2 ))
  749.     bd_title=$(cut -d'/' -f${bd_title_field}- <<<"$if" | cut -d'/' -f1)
  750.  
  751.     echo "$bd_title"
  752. }
  753.  
  754. # If the input filename is an M2TS, get the movie title and year from
  755. # the surrounding directory structure.
  756. if_m2ts=$(if_m2ts)
  757.  
  758. if [[ $if_m2ts ]]; then
  759.     bname="$if_m2ts"
  760. fi
  761.  
  762. # Breaks up the input filename, and gets its IMDb name.
  763. bname_tmp=$(break_name "$bname")
  764.  
  765. # Sets IFS to newline.
  766. IFS=$'\n'
  767.  
  768. # Gets information about input file.
  769. if_info=( $(eval ${cmd[1]} -hide_banner -i \"${if}\" 2>&1) )
  770.  
  771. # Gets information from IMDb, and removes special characters.
  772. imdb_tmp=( $(fsencode "$(imdb "$bname_tmp")") )
  773. #fsencode "$(imdb "$bname_tmp")"
  774.  
  775. IFS="$IFS_bak"
  776.  
  777. # * If IMDb lookup succeeded, use that information.
  778. # * If not, use the information in $bname_tmp instead, but delete
  779. # special characters.
  780. if [[ $imdb_tmp ]]; then
  781.     title=$(echo "${imdb_tmp[0]}" | tr ' ' '.')
  782.     year="${imdb_tmp[1]}"
  783. else
  784.     bname_tmp_fs=$(fsencode "$bname_tmp")
  785.     title=$(sed 's/ [0-9]\{4\}$//' <<<"$bname_tmp_fs" | tr ' ' '.')
  786.     year=$(grep -Eo "[0-9]{4}$" <<<"$bname_tmp_fs" | tr -d '[:punct:]')
  787. fi
  788.  
  789. # Creates a directory structure in the current user's home directory:
  790. # "${title}.${year}.${rls_type}/Info"
  791. of_bname="${title}.${year}.${rls_type}"
  792. of_dir="${HOME}/${of_bname}"
  793. info_dir="${of_dir}/Info"
  794. mkdir -p "$info_dir"
  795.  
  796. # If we failed to create the directory, quit.
  797. if [[ $? -ne 0 ]]; then
  798.     exit
  799. fi
  800.  
  801. # Creates the output filename, as well as the remux output filename.
  802. of="${of_dir}/${of_bname}.mkv"
  803. of_remux="${of_dir}/${title}.${year}.REMUX.mkv"
  804.  
  805. # Creates a filename which will contain the commands run by this script.
  806. # If the filename already exists, delete that file, and then create
  807. # a new one.
  808. command_f="${of_dir}/Info/${title}.${year}_commands.txt"
  809. if [[ -f $command_f ]]; then
  810.     rm "$command_f"
  811. fi
  812. touch "$command_f"
  813.  
  814. if [[ $existing -ne 1 ]]; then
  815.  
  816.     # If output filename already exists, add a random number to the end of
  817.     # the filename.
  818.     if [[ -f $of ]]; then
  819.         of="${of_dir}/${title}.${year}.${rls_type}-${session}.mkv"
  820.     elif [[ -f $of_remux ]]; then
  821.         of_remux="${of_dir}/${title}.${year}.REMUX-${session}.mkv"
  822.     fi
  823. fi
  824.  
  825. # Creates the temp filename for the core DTS track.
  826. dts_tmp="${of_dir}/TS-Core-${session}.dts"
  827.  
  828. # * Checks if HandBrake is already running, and if so, wait.
  829. # * Extracts the core DTS track.
  830. # * Remuxes input file, without all its audio tracks, with the core
  831. #   DTS track.
  832. # * Encodes the remux with HandBrake.
  833. # * Creates info txt files for input file, output file and remux
  834. #   output file.
  835. is_handbrake
  836. dts_extract
  837. remux_mkv
  838. hb_encode
  839. info_txt
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top