Advertisement
MaxDjently

Ziggle Wump Media Compressor 0.2-beta.09.20.2024

Sep 19th, 2024
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 27.79 KB | Source Code | 0 0
  1. #!/data/data/com.termux/files/usr/bin/bash
  2.  
  3. # ATTENTION:
  4.  
  5. # Copying and pasting the script can introduce formatting that is improper to bash.  If the script doesn't run, you may need to use the dos2unix command to fix it.
  6.  
  7. # ex.  dos2unix ziggle_wump.sh
  8.  
  9. # --------------------
  10. #     Licence
  11. # --------------------
  12.  
  13. # This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
  14.  
  15. # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  16.  
  17. # You should have received a copy of the GNU General Public License along with  this program. If not, see <https://www.gnu.org/licenses/>.
  18.  
  19. # -----------------
  20. #     About
  21. # -----------------
  22.  
  23. # This script is for Termux on Android and is not associated with the apps it uses.
  24.  
  25. # Copyright Joshua Hansen and contributors: Microsoft Co-Pilot, OpenAI ChatGPT and Google Gemini.
  26.  
  27. # Shout out to Webernets https://youtu.be/0aeCfDKLfbs?si=58y58eWSiXurcHpj who gave me the inspiration for this, the one who did the heavy lifting figuring out the command line options. And this video helping me figure out how to do it in Handbrake, but I needed it all in a one-line command line option and this was it, I had to write it myself.
  28.  
  29. # -----------------------
  30. #   Instructions
  31. # -----------------------
  32.  
  33. instructions="#Ziggle Wump: The Simple FFmpeg Command Line Companion Script for Termux on Android
  34.  
  35. # Disclaimer
  36.  
  37. * This script does not allow conversion of encrypted files. Encrypted content typically indicates copyrighted material.
  38.  
  39. * The script itself does not violate copyright, but the user's actions might. The script cannot prevent users from circumventing copyright protection, nor is it the script's responsibility to do so. The onus lies with the copyright holder to encrypt their media.
  40.  
  41. * It is not recommended to use the script to violate copyright law. US copyright law allows for spaceshifting and fair use of copyrighted material.
  42.  
  43. # Note
  44.  
  45. * Some phones, like the Galaxy S24, have battery-saving features that can impact the encoding process and result in partially encoded files. Ensure Termux is in focus (full screen or split-screen) while encoding, and keep the screen on during the process.
  46.  
  47. * For more information and potential fixes for specific phones, visit https://dontkillmyapp.com/.
  48.  
  49. # Getting Started
  50.  
  51. * Download or Copy: Download or copy this script to a file and rename it if desired (e.g., ziggle_wump.sh). Placing it in your Movies folder makes it easily accessible on both Android and Termux, although you can also put it in /usr/bin to use it system-wide.
  52.  
  53. * Make it Executable: Run chmod +x ziggle_wump.sh to make the script executable.
  54. * Run the Script: Execute the script using bash ./ziggle_wump.sh [options].
  55.  
  56. # Options
  57.  
  58. * -r resolution: Sets a custom resolution height while preserving aspect ratio (e.g., bash ziggle_wump.sh -r 720).
  59. * -d: Checks and upgrades dependencies.
  60. * -y: Automatically confirms prompts.
  61. * -o output_fps: Sets a custom output FPS (e.g., bash ziggle_wump.sh -o 60).
  62. * -b max_video_bitrate: Sets a custom maximum video bitrate in kilobits per second (e.g., bash ziggle_wump.sh -b 2000).
  63. * -a avg_audio_bitrate: Sets a custom average audio bitrate in kilobits per second (e.g., bash ziggle_wump.sh -a 128).
  64. * -i: Installs the script to /data/data/com.termux/files/usr/bin/zwmc.
  65. * -u: Uninstalls the script from /data/data/com.termux/files/usr/bin/zwmc.
  66. * -m: Shows the menu for setting options.
  67. * -h, --help: Displays help message."
  68.  
  69. # -----------------------------
  70. #        Variables
  71. # -----------------------------
  72.  
  73. # Supported File Types
  74. video_ext="mp4 mkv avi mov flv wmv webm mts 3gp mpeg ogv rmvb m4v f4v vob ts m2ts asf swf m2v divx xvid mpg mpe m1v dvr-ms mxf gxf bink mng nsv"
  75. audio_ext="mp3 aac wav flac ogg m4a wma ac3 eac3 opus amr aiff alac caf dts mka mp2 ra tta voc"
  76.  
  77. # Maximum resolution by height in pixels maintaining aspect ratio. Either the user defined value or the input video original value will be selected
  78. resolution=600
  79. max_video_bitrate=688 # In Kilobits per second.
  80. # Set the audio bitrate for both videos and audio files.
  81. avg_audio_bitrate=96 # In Kilobits per second.
  82. output_fps=24 # Maximum output FPS. Original or user input, whichever is lower.
  83.  
  84. # Full Log file path
  85. log_file="$HOME/storage/shared/Movies/VideoDrop/convert.log"
  86.  
  87. # Shortened log file path for terminal output
  88. log_file_echo=$(echo "$log_file" | sed 's|/data/data/com.termux/files/home|.../home|g')
  89.  
  90. # Full directory paths
  91. video_drop_dir="$HOME/storage/shared/Movies/VideoDrop"
  92. video_processing_dir="$HOME/storage/shared/Movies/VideoProcessing"
  93. output_dir_base="$HOME/storage/shared/Movies/VideoConverted"
  94.  
  95. # Shortened directory paths for terminal output
  96. video_drop_dir_echo=$(echo "$video_drop_dir" | sed 's|/data/data/com.termux/files/home|.../home|g')
  97. video_processing_dir_echo=$(echo "$video_processing_dir" | sed 's|/data/data/com.termux/files/home|.../home|g')
  98. output_dir_base_echo=$(echo "$output_dir_base" | sed 's|/data/data/com.termux/files/home|.../home|g')
  99.  
  100. # combine audio and video extension variables to pass to find command.
  101. filetypes=""
  102. for ext in $audio_ext $video_ext; do
  103.     filetypes="$filetypes -iname '*.$ext' -o"
  104. done
  105. # Remove the trailing ' -o'
  106. filetypes="${filetypes% -o}"
  107.  
  108. # --------------------------------
  109. #     Command Flags
  110. # --------------------------------
  111.  
  112. show_menu() {
  113.     auto_yes=true  # Turn on auto_yes when the menu is shown
  114.     while true; do
  115.         deps_status="Off"
  116.         if [ "$check_deps" = true ]; then
  117.             deps_status="On"
  118.         fi
  119.  
  120.         menu_choice=$(dialog --menu "Choose an option:" 15 50 8 \
  121.             1 "Set Resolution Height ($resolution)" \
  122.             2 "Set Output FPS ($output_fps)" \
  123.             3 "Set Max Video Bitrate ($max_video_bitrate)" \
  124.             4 "Set Average Audio Bitrate ($avg_audio_bitrate)" \
  125.             5 "Check Dependencies ($deps_status)" \
  126.             6 "Install Script" \
  127.             7 "Uninstall Script" \
  128.             8 "Compress Files" 2>&1 >/dev/tty)
  129.  
  130.         case $menu_choice in
  131.             1)
  132.                 new_resolution=$(dialog --inputbox "Enter resolution height ($resolution):" 8 40 "$resolution" 2>&1 >/dev/tty)
  133.                 if [ $? -eq 0 ]; then
  134.                     resolution="$new_resolution"
  135.                 fi
  136.                 ;;
  137.             2)
  138.                 new_output_fps=$(dialog --inputbox "Enter output FPS ($output_fps):" 8 40 "$output_fps" 2>&1 >/dev/tty)
  139.                 if [ $? -eq 0 ]; then
  140.                     output_fps="$new_output_fps"
  141.                 fi
  142.                 ;;
  143.             3)
  144.                 new_max_video_bitrate=$(dialog --inputbox "Enter max video bitrate ($max_video_bitrate):" 8 40 "$max_video_bitrate" 2>&1 >/dev/tty)
  145.                 if [ $? -eq 0 ]; then
  146.                     max_video_bitrate="$new_max_video_bitrate"
  147.                 fi
  148.                 ;;
  149.             4)
  150.                 new_avg_audio_bitrate=$(dialog --inputbox "Enter average audio bitrate ($avg_audio_bitrate):" 8 40 "$avg_audio_bitrate" 2>&1 >/dev/tty)
  151.                 if [ $? -eq 0 ]; then
  152.                     avg_audio_bitrate="$new_avg_audio_bitrate"
  153.                 fi
  154.                 ;;
  155.             5)
  156.                 if [ "$check_deps" = true ]; then
  157.                     check_deps=false
  158.                 else
  159.                     check_deps=true
  160.                 fi
  161.                 ;;
  162.             6)
  163.                 clear
  164.                 install_script
  165.                 ;;
  166.             7)
  167.                 clear
  168.                 uninstall_script
  169.                 ;;
  170.             8)
  171.                 break
  172.                 ;;
  173.             *)
  174.                 tput sgr0  # Reset terminal colors
  175.                 clear
  176.                 exit 0
  177.                 ;;
  178.         esac
  179.     done
  180. }
  181.  
  182. # Function to display help message
  183. show_help() {
  184.     width=$(tput cols)
  185.     script_name=$(basename "$0")
  186.     echo "Place your media files in $video_drop_dir_echo." | tee -a "$log_file" | fmt -w $width
  187.     echo "Usage: bash $script_name [-r resolution] [-d] [-y] [-h|--help] [-o output_fps] [-b max_video_bitrate] [-a avg_audio_bitrate] [-i] [-u] [-m]" | fmt -w $width
  188.     echo "  -r resolution ex. bash $script_name -r 720               Custom resolution height preserving aspect ratio" | fmt -w $width
  189.     echo "  -d Check and upgrade dependencies." | fmt -w $width
  190.     echo "  -y Automatically say yes to prompts." | fmt -w $width
  191.     echo "  -o output_fps Set custom output FPS. ex. bash $script_name -o 60" | fmt -w $width
  192.     echo "  -b max_video_bitrate Set custom max video bitrate in kilobits per second. ex. bash $script_name -b 2000" | fmt -w $width
  193.     echo " -a avg_audio_bitrate Set custom average audio bitrate in kilobits per second. ex. bash $script_name -a 128" | fmt -w $width
  194.     echo "  -i Install the script to /data/data/com.termux/files/usr/bin/zwmc" | fmt -w $width
  195.     echo "  -u Uninstall the script from /data/data/com.termux/files/usr/bin/zwmc" | fmt -w $width
  196.     echo " -m: Shows the menu for setting options."
  197.     echo "  -h, --help Display this help message" | fmt -w $width
  198.     exit 0
  199. }
  200.  
  201. # Install Ziggle Wump to /usr/bin folder and widget script to ~/.shortcuts
  202. install_script() {
  203.     if [ -f /data/data/com.termux/files/usr/bin/zwmc ]; then
  204.         current_dir=$(dirname "$0")
  205.         if [ "$current_dir" != "/data/data/com.termux/files/usr/bin" ]; then
  206.             echo "Script is installed but not running from the bin directory. Reinstalling..."
  207.             cp "$0" /data/data/com.termux/files/usr/bin/zwmc
  208.             chmod +x /data/data/com.termux/files/usr/bin/zwmc
  209.             if whereis zwmc | grep -q "/data/data/com.termux/files/usr/bin/zwmc"; then
  210.                 echo "Script reinstalled to /data/data/com.termux/files/usr/bin/zwmc"
  211.             else
  212.                 echo "Reinstallation failed"
  213.             fi
  214.         else
  215.             echo "Script is already installed and running from the bin directory."
  216.         fi
  217.     else
  218.         cp "$0" /data/data/com.termux/files/usr/bin/zwmc
  219.         chmod +x /data/data/com.termux/files/usr/bin/zwmc
  220.         if whereis zwmc | grep -q "/data/data/com.termux/files/usr/bin/zwmc"; then
  221.             echo "Script installed to /data/data/com.termux/files/usr/bin/zwmc"
  222.         else
  223.             echo "Installation failed"
  224.         fi
  225.     fi
  226.  
  227.     # Create the shortcut script
  228.     mkdir -p $HOME/.shortcuts
  229.     echo '#!/data/data/com.termux/files/usr/bin/bash' > $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  230.     echo 'zwmc -m' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  231.     echo 'read -p "Press Enter to continue."' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  232.     echo 'case $return in' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  233.     echo '    * ) echo "Exiting script."' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  234.     echo 'esac' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  235.     chmod +x $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  236.  
  237.     exit 0
  238. }
  239.  
  240. uninstall_script() {
  241.     if [ ! -f /data/data/com.termux/files/usr/bin/zwmc ]; then
  242.         echo "Script is not installed."
  243.         exit 0
  244.     else
  245.         rm -f /data/data/com.termux/files/usr/bin/zwmc
  246.         if ! whereis zwmc | grep -q "/data/data/com.termux/files/usr/bin/zwmc"; then
  247.             echo "Script uninstalled from /data/data/com.termux/files/usr/bin/zwmc"
  248.         else
  249.             echo "Uninstallation failed"
  250.         fi
  251.     fi
  252.  
  253.     # Remove the shortcut script
  254.     rm -f $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  255.  
  256.     exit 0
  257. }
  258.  
  259. # Parse command-line options
  260. check_deps=false
  261. auto_yes=false  # Set auto_yes to false by default
  262. while getopts "r:dyho:b:a:ium" opt; do
  263.     case $opt in
  264.         r) resolution="$OPTARG" ;;  # Set custom resolution by height
  265.         d) check_deps=true ;;  # Check and upgrade dependencies
  266.         y) auto_yes=true ;;  # Automatically say yes to prompts
  267.         o) output_fps="$OPTARG" ;;  # Set custom output FPS
  268.         b) max_video_bitrate="$OPTARG" ;;  # Set custom max video bitrate
  269.         a) avg_audio_bitrate="$OPTARG" ;;  # Set custom average audio bitrate
  270.         i) install_script ;;  # Install the script
  271.         u) uninstall_script ;;  # Uninstall the script
  272.         m) auto_yes=true; show_menu ;;  # Show menu and turn on auto_yes
  273.         h) show_help ;;  # Display help
  274.         *) show_help ;;  # Display help for invalid options
  275.     esac
  276. done
  277.  
  278. shift $((OPTIND -1))
  279.  
  280. # ---------------------------------
  281. #       Dependencies
  282. # ---------------------------------
  283.  
  284. if $check_deps; then
  285.     echo "Checking and upgrading dependencies, please wait..."
  286.     pkg update && pkg upgrade -y
  287.     if ! command -v bc &> /dev/null; then
  288.         pkg install bc -y
  289.     fi
  290.     if ! command -v ffmpeg &> /dev/null; then
  291.         pkg install ffmpeg -y
  292.     fi
  293.     if ! command -v ncurses-utils &> /dev/null; then
  294.         pkg install ncurses-utils -y
  295.     fi
  296.     echo "Update complete."
  297. fi
  298.  
  299. # -----------------------------------------------------
  300. #     Create Directories and log file
  301. # -----------------------------------------------------
  302.  
  303. # Create necessary directories if they don't exist
  304. mkdir -p "$video_drop_dir" "$video_processing_dir" "$output_dir_base"
  305.  
  306. # Change to the VideoDrop directory
  307. cd "$video_drop_dir" || { echo "Directory change failed"; exit 1; }
  308.  
  309. # Check if log file exists
  310. if [ -f "$log_file" ]; then
  311.     echo "Old log file found.  Deleting..."
  312.     rm "$log_file"
  313.     echo "File deleted."
  314. else
  315.     echo "Log File does not exist.  The script will create $log_file_echo"
  316. fi
  317.  
  318. # ------------------------
  319. #     Start script
  320. # ------------------------
  321.  
  322. width=$(tput cols)
  323.  
  324. echo "Running... Press Ctrl+C to stop."
  325.  
  326. echo ""
  327. echo "$instructions" | tee -a "$log_file" | fmt -w $width
  328.  
  329. echo ""
  330. # Function to prompt the user to continue or quit
  331. prompt_continue_or_quit() {
  332.     while true; do
  333.         # Recursively find and process videos and audio
  334.         video_count=$(eval "find \"$video_drop_dir\" -type f \( $filetypes \) | wc -l")
  335.         echo "Detected $video_count compatible video and/or audio file(s) in:" | tee -a "$log_file" | fmt -w $width
  336.    
  337.         if [ "$video_count"  -eq 0 ]; then
  338.             echo "$video_drop_dir_echo folder created. Place your videos and audio here including files in folders using your favorite file manager for Android, and then run  the script again.  
  339.  
  340. Supported File Types:
  341.  
  342. Video: $video_ext
  343. Audio: $audio_ext" | tee -a "$log_file" | fmt -w $width
  344.             exit 0
  345.         fi
  346.  
  347.         # Prompt text
  348.         prompt_text="$video_drop_dir_echo. You can start encoding now. Do you wish to proceed? (Y/N): "
  349.  
  350.         # Format the prompt text
  351.         formatted_prompt=$(echo "$prompt_text" | fmt -w $width)
  352.  
  353.         if $auto_yes; then
  354.             echo "$formatted_prompt"
  355.             echo "Y"
  356.             return 0  # Automatically continue
  357.         else
  358.             # Read user input with formatted prompt
  359.             read -p "$formatted_prompt" yn
  360.             case $yn in
  361.                 [Yy]* ) return 0;;  # Continue
  362.                 [Nn]* ) echo "Exiting script."; exit 0;;  # Quit
  363.                 * ) echo "Please answer Y or N.";;
  364.             esac
  365.         fi
  366.     done
  367. }
  368.  
  369. # Function to clean up file names
  370. clean_file_names() {
  371.     eval "find \"$video_drop_dir\" -type f \( $filetypes \)" | while read -r file; do
  372.         dir=$(dirname "$file")
  373.         base=$(basename "$file")
  374.         new_base=$(echo "$base" | sed 's/[^a-zA-Z0-9 ._-]//g' | tr -s ' ')
  375.         new_file="$dir/$new_base"
  376.         if [ "$file" != "$new_file" ]; then
  377.             mv "$file" "$new_file"
  378.         fi
  379.     done
  380. }
  381.  
  382. # Clean up file names before processing
  383. clean_file_names
  384.  
  385. # Prompt the user to continue or quit?
  386. prompt_continue_or_quit
  387.  
  388. echo "Starting conversion process..." | tee -a "$log_file"
  389.  
  390. # Initialize ffmpeg_custom_options
  391. ffmpeg_custom_options="-c:v libx265 -x265-params \"deblock=-1:no-sao=1:keyint=250:aq-mode=3:psy-r=0.75:psy-rdoq=2.0:rd=4:rdoq-level=1:rect=0:strong-intra-smoothing=0\" -crf 32 -preset slow -c:a libopus -vbr on -ac 2 -af \"loudnorm=I=-23:LRA=7:TP=-2\""
  392.  
  393. # Add avg audio bitrate if avg_audio_bitrate is set
  394. if [ -n "$avg_audio_bitrate" ]; then
  395.     ffmpeg_custom_options="$ffmpeg_custom_options -b:a ${avg_audio_bitrate}k"
  396. fi
  397.  
  398. # Add maxrate and bufsize options if max_video_bitrate is set
  399. if [ -n "$max_video_bitrate" ]; then
  400.     ffmpeg_custom_options="$ffmpeg_custom_options -maxrate ${max_video_bitrate}k -bufsize 60M"
  401. fi
  402.  
  403.  
  404. # -----------------------------
  405. #    Audio Encoder
  406. # -----------------------------
  407.  
  408. width=$(tput cols)
  409.  
  410. # Function to check the log file for "Killed" message
  411. check_for_killed_message() {
  412.     if grep -q " Killed     " "$log_file"; then
  413.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error: Process was killed. Check log file for details." | tee -a "$log_file"
  414.         return 1
  415.     fi
  416.     return 0
  417. }
  418.  
  419. # Function to process audio files
  420. process_audio() {
  421.     local audio="$1"
  422.     local audio_echo=$(echo "$audio" | sed 's|/data/data/com.termux/files/home|/home|g')
  423.     local relative_path="${audio#$video_drop_dir/}"
  424.     local dir_path=$(dirname "$relative_path")
  425.     local base_name=$(basename "$relative_path" | tr -cd '[:alnum:]._ -')
  426.     local output_dir="$output_dir_base/$dir_path"
  427.     local output_file="$output_dir/${base_name}_converted.opus"
  428.     local temp_output_file="$output_file.tmp"
  429.  
  430.     mkdir -p "$output_dir"  # Create output directory
  431.  
  432.     echo "$(date '+%Y-%m-%d %H:%M:%S') Processing $audio to $output_file" | tee -a "$log_file"
  433.  
  434.     # Construct the FFmpeg command for audio processing
  435.     local ffmpeg_command="ffmpeg -nostdin -loglevel error -stats -stats_period 1 -y -i \"$audio\" -c:a libopus -b:a ${avg_audio_bitrate}k -vbr on -ac 2 -af \"loudnorm=I=-23:LRA=7:TP=-2\" -f opus \"$temp_output_file\""
  436.  
  437.     echo "$ffmpeg_command" | tee -a "$log_file"
  438.  
  439.     # Execute the FFmpeg command
  440.     eval $ffmpeg_command 2>&1 | tee -a "$log_file"
  441.  
  442.     # Check for "Killed" message in the log file
  443.     if ! check_for_killed_message; then
  444.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error: Process was killed. Exiting." | tee -a "$log_file"
  445.         exit 1
  446.     fi
  447.  
  448.     if [ $? -eq 0 ]; then
  449.         mv "$temp_output_file" "$output_file"  # Rename the temporary file to the final output file
  450.         mkdir -p "$video_processing_dir/$dir_path"  # Create processing directory
  451.         mv "$audio" "$video_processing_dir/$relative_path"
  452.         echo ""
  453.         echo "$(date '+%Y-%m-%d %H:%M:%S') Converted $audio_echo. Your original files will be in the $video_processing_dir_echo folder for comparison. Your new files are in $output_dir_base_echo" | tee -a "$log_file" | fmt -w $width
  454.     else
  455.         echo ""
  456.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error processing \"$audio\". Check log file for details." | tee -a "$log_file"
  457.         exit 1
  458.     fi
  459. }
  460.  
  461. # ------------------------------
  462. #      Video Encoder
  463. # ------------------------------
  464.  
  465. width=$(tput cols)
  466.  
  467. # Function to check the log file for "Killed" message
  468. check_for_killed_message() {
  469.     if grep -q " Killed     " "$log_file"; then
  470.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error: Process was killed. Check log file for details." | tee -a "$log_file" | fmt -w $width
  471.         exit 1
  472.     fi
  473. }
  474.  
  475. process_video() {
  476.     local video="$1"
  477.     local video_echo=$(echo "$video" | sed 's|/data/data/com.termux/files/home|.../home|g')
  478.     local relative_path="${video#$video_drop_dir/}"
  479.     local dir_path=$(dirname "$relative_path")
  480.     local base_name=$(basename "$relative_path" | tr -cd '[:alnum:]._ -')
  481.     local output_dir="$output_dir_base/$dir_path"
  482.     local output_file="$output_dir/${base_name}_converted.mkv"
  483.     local scale_filter=""
  484.  
  485.     # Get input video resolution
  486.     input_resolution=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "$video" | tr -d '[:space:],')
  487.  
  488.     if [ -n "$resolution" ]; then
  489.         if [ "$input_resolution" -gt "$resolution" ]; then
  490.             scale_filter="scale=-2:$resolution:flags=lanczos"
  491.             echo "Input resolution ($input_resolution) is higher than user-defined resolution ($resolution). Using user-defined resolution." | tee -a "$log_file"
  492.         else
  493.             echo "Input resolution ($input_resolution) is lower or equal to user-defined resolution ($resolution). Keeping original resolution." | tee -a "$log_file"
  494.         fi
  495.     else
  496.         echo "Resolution set to: original" | tee -a "$log_file"
  497.     fi
  498.  
  499.     mkdir -p "$output_dir"  # Create output directory
  500.  
  501.     echo "$(date '+%Y-%m-%d %H:%M:%S') Processing $video to $output_file" | tee -a "$log_file"
  502.  
  503.     # Get the frame rate of the input video
  504.     input_fps=$(ffprobe -v error -select_streams v:0 -show_entries stream=avg_frame_rate,r_frame_rate -of csv=p=0 "$video" | awk -F'/' '{if ($2) print $1/$2; else print $1}' | bc -l)
  505.  
  506.     # Define common frame rates
  507.     common_fps=(23.976 24 25 29.97 30 50 59.94 60)
  508.  
  509.     # Function to find the nearest valid frame rate
  510.     nearest_fps() {
  511.         local fps=$1
  512.         local nearest=${common_fps[0]}
  513.         local min_diff=$(echo "scale=5; $fps - ${common_fps[0]}" | bc | awk '{print ($1 >= 0) ? $1 : -$1}')
  514.         for rate in "${common_fps[@]}"; do
  515.             local diff=$(echo "scale=5; $fps - $rate" | bc | awk '{print ($1 >= 0) ? $1 : -$1}')
  516.             if (( $(echo "$diff < $min_diff" | bc -l) )); then
  517.                 min_diff=$diff
  518.                 nearest=$rate
  519.             fi
  520.         done
  521.         echo $nearest
  522.     }
  523.  
  524.     # Round the frame rate to the nearest valid frame rate
  525.     rounded_fps=$(nearest_fps $input_fps)
  526.  
  527.     # Determine the lower frame rate
  528.     if (( $(echo "$rounded_fps > $output_fps" | bc -l) )); then
  529.         final_fps=$output_fps
  530.     else
  531.         final_fps=$rounded_fps
  532.     fi
  533.  
  534. # Set the initial video filter option
  535. video_filter_option="yadif=2,fps=$final_fps,${scale_filter}"
  536.  
  537. # Detect the pixel format using ffprobe
  538. input_file="$video"
  539. pix_fmt=$(ffprobe -v repeat+level+error -select_streams v:0 -show_entries stream=pix_fmt -of default=noprint_wrappers=1:nokey=1 "$input_file" | awk 'NR==1{print $1}')
  540.  
  541. echo "Pixel format: $pix_fmt"
  542.  
  543. # Validate the pixel format
  544. valid_pix_fmt=$(ffmpeg -pix_fmts | grep -w "$pix_fmt")
  545.  
  546. # Check if 10-bit encoding is selected
  547. if [[ "$pix_fmt" == *"10le"* || "$pix_fmt" == *"10be"* ]]; then
  548.     video_filter_option="$video_filter_option,deband=1thr=0.01:2thr=0.01:3thr=0.01:4thr=0.01:range=4:direction=-3.14:blur=1:coupling=0"
  549. fi
  550.  
  551. # Define a variable for probe size and analyze duration
  552. probe_analyze_opts="-probesize 2147483647 -analyzeduration 2147483647"
  553.  
  554. # Detect subtitle codecs and convert unsupported ones
  555. subtitle_codecs=$(ffprobe -v error $probe_analyze_opts -select_streams s -show_entries stream=codec_name -of csv=p=0 "$input_file")
  556. subtitle_map=""
  557. if [ -n "$subtitle_codecs" ]; then
  558.     subtitle_map="-map 0:s"  # Include all subtitle streams
  559.     i=0
  560.     while read -r codec; do
  561.         case "$codec" in
  562.             # Text-based subtitles to be copied directly
  563.             srt)
  564.                 subtitle_map="$subtitle_map -c:s:$i copy"
  565.                 ;;
  566.             # Other text-based subtitles to be converted to SRT
  567.             ass|ssa|webvtt|eia_608|eia_708|scc|sami|ttml|smi|teletext|mov_text|microdvd|subviewer)
  568.                 subtitle_map="$subtitle_map -c:s:$i srt"
  569.                 ;;
  570.             # Bitmap-based subtitles to be converted to DVD subtitles
  571.             dvdsub|pgs|vobsub|hdmv_pgs_subtitle|dvd_subtitle)
  572.                 subtitle_map="$subtitle_map -c:s:$i dvdsub"
  573.                 ;;
  574.             # Copy other compatible subtitles
  575.             *)
  576.                 subtitle_map="$subtitle_map -c:s:$i copy"
  577.                 ;;
  578.         esac
  579.         i=$((i + 1))
  580.     done <<< "$subtitle_codecs"
  581. else
  582.     echo "No subtitle streams detected." | tee -a "$log_file"
  583. fi
  584.  
  585. # Combine common and custom FFmpeg options
  586. combined_ffmpeg_options="-nostdin -loglevel error -stats -stats_period 5 $probe_analyze_opts -y -fix_sub_duration -i \"$video\" -map 0:v:0 -map 0:a:? -map_chapters 0 $ffmpeg_custom_options"
  587.  
  588. # Construct the FFmpeg command
  589. local ffmpeg_command
  590. if [ -n "$valid_pix_fmt" ]; then
  591.     if [ -n "$subtitle_map" ]; then
  592.         ffmpeg_command="ffmpeg $combined_ffmpeg_options -vf \"$video_filter_option\" -pix_fmt $pix_fmt $subtitle_map"
  593.     else
  594.         ffmpeg_command="ffmpeg $combined_ffmpeg_options -vf \"$video_filter_option\" -pix_fmt $pix_fmt"
  595.     fi
  596. else
  597.     if [ -n "$subtitle_map" ]; then
  598.         ffmpeg_command="ffmpeg $combined_ffmpeg_options -vf \"$video_filter_option\" $subtitle_map"
  599.     else
  600.         ffmpeg_command="ffmpeg $combined_ffmpeg_options -vf \"$video_filter_option\""
  601.     fi
  602. fi
  603.  
  604. # Ensure all subtitle streams are marked as "default: off"
  605. subtitle_streams=$(ffprobe -v error $probe_analyze_opts -select_streams s -show_entries stream=index -of csv=p=0 "$input_file")
  606. if [ -n "$subtitle_streams" ]; then
  607.     for stream_index in $subtitle_streams; do
  608.         ffmpeg_command="$ffmpeg_command -disposition:s:$stream_index 0"
  609.     done
  610. fi
  611.  
  612. # Add the output file name at the end
  613. ffmpeg_command="$ffmpeg_command \"$output_file\""
  614.  
  615. echo "FFmpeg command: $ffmpeg_command" | tee -a "$log_file"
  616.  
  617. # Execute the FFmpeg command
  618. if ! eval $ffmpeg_command 2>&1 | tee -a "$log_file"; then
  619.     echo "FFmpeg command failed. Check the log for details." | tee -a "$log_file"
  620.     exit 1
  621. fi
  622.  
  623.     # Check for "Killed" message in the log file
  624.     check_for_killed_message
  625.  
  626.    if [ $? -eq 0 ]; then
  627.         mkdir -p "$video_processing_dir/$dir_path"  # Create processing directory
  628.         mv "$video" "$video_processing_dir/$relative_path"
  629.         echo ""
  630.         echo "$(date '+%Y-%m-%d %H:%M:%S') Complete.  Your original files are in $video_processing_dir_echo.  Your new files are in $output_dir_base_echo" | tee -a "$log_file" | fmt -w $width
  631.     else
  632.         echo ""
  633.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error processing \"$video\". Check log file for details." | tee -a "$log_file"
  634.         exit 1
  635.     fi
  636. }
  637.  
  638. # --------------------------------------
  639. #    Start Batch Encoding
  640. # --------------------------------------
  641.  
  642. width=$(tput cols)
  643.  
  644. stop_processing=false
  645.  
  646. # Function to handle SIGINT (Ctrl+C)
  647. handle_sigint() {
  648.     echo ""
  649.     echo "Caught SIGINT (Ctrl+C). Exiting immediately." | fmt -w $width
  650.     stop_processing=true
  651.     exit 1  # Exit immediately without performing cleanup
  652. }
  653.  
  654. # Trap SIGINT and call the handle_sigint function
  655. trap handle_sigint SIGINT
  656.  
  657. # Process each file found
  658. eval "find \"$video_drop_dir\" -type f \( $filetypes \)" | while read -r file; do
  659.     if [ "$stop_processing" = true ]; then
  660.         echo "Stopping processing due to SIGINT."
  661.         exit 1
  662.     fi
  663.  
  664.     case "$file" in
  665.         *.mp3|*.aac|*.wav|*.flac|*.ogg|*.m4a|*.wma|*.ac3|*.eac3|*.opus|*.amr|*.aiff|*.alac|*.caf|*.dts|*.mka|*.mp2|*.ra|*.tta|*.voc)
  666.             process_audio "$file" || exit 1
  667.             ;;
  668.  
  669.         *.mp4|*.mkv|*.avi|*.mov|*.flv|*.wmv|*.webm|*.mts|*.3gp|*.mpeg|*.ogv|*.rmvb|*.m4v|*.f4v|*.vob|*.ts|*.m2ts|*.asf|*.swf|*.m2v|*.divx|*.xvid|*.mpg|*.mpe|*.m1v|*.dvr-ms|*.mxf|*.gxf|*.bink|*.mng|*.nsv)
  670.             process_video "$file" || exit 1
  671.             ;;
  672.     esac
  673. done
  674.  
  675. # -----------------------------
  676. #     Finishing Up
  677. # -----------------------------
  678.  
  679. width=$(tput cols)
  680.  
  681. find "$PWD" -type d -empty -delete
  682. echo "removed empty folders in $video_drop_dir_echo directory"
  683.  
  684. # final output message
  685. echo ""
  686. echo "Log file is in $video_drop_dir_echo.  There may be unprocessed media files or other incompatible files.  Some files may need to be remuxed for compatibility." | tee -a "$log_file" | fmt -w $width
  687. exit 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement