Advertisement
MaxDjently

Ziggle Wump Media Compressor Testing 0.2-beta.09.24.2024

Sep 23rd, 2024 (edited)
70
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 31.94 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. * Install Termux and Termux:Widget: Grab Termux from F-Droid if you haven't already. Termux:Widget too (optional).
  54.  
  55. * Run termux-setup-storage to obtain file permissions to the shared folders.
  56.  
  57. * Obtain storage access: Run termux-setup-storage and allow access.
  58.  
  59. * Make it Executable: Run chmod +x ziggle_wump.sh to make the script executable.
  60.  
  61. * Run the Script: Execute the script using bash ./ziggle_wump.sh [options]. (FIRST TIME RUN: bash ./ziggle_wump.sh -d to install dependencies.)
  62.  
  63. * Optional:  Install Termux:Widget and give it permissions.  Place widget on Home Screen.  Run bash ziggle_wump.sh -i to install script to .../usr/bin and it creates widget in $HOME/.shortcuts.  Reload Widget and you will see 'Ziggle Wump Media Compressor.sh'
  64.  
  65. # Options
  66.  
  67. * -r resolution: Sets a custom resolution height while preserving aspect ratio (e.g., bash ziggle_wump.sh -r 720).
  68. * -d: Checks and upgrades dependencies.
  69. * -y: Automatically confirms prompts.
  70. * -o output_fps: Sets a custom output FPS (e.g., bash ziggle_wump.sh -o 60).
  71. * -b max_video_bitrate: Sets a custom maximum video bitrate in kilobits per second (e.g., bash ziggle_wump.sh -b 2000).
  72. * -a avg_audio_bitrate: Sets a custom average audio bitrate in kilobits per second (e.g., bash ziggle_wump.sh -a 128).
  73. * -p preset: Sets encoding speed preset for x265 (0=slowest, 8=fastest) (e.g., bash ziggle_wump.sh -p 4).
  74. * -i: Installs the script to /data/data/com.termux/files/usr/bin/zwmc.
  75. * -u: Uninstalls the script from /data/data/com.termux/files/usr/bin/zwmc.
  76. * -m: Shows the menu for setting options.
  77. * -h, --help: Displays help message."
  78.  
  79. # -----------------------------
  80. #        Variables
  81. # -----------------------------
  82.  
  83. # Supported File Types
  84. 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"
  85. audio_ext="mp3 aac wav flac ogg m4a wma ac3 eac3 opus amr aiff alac caf dts mka mp2 ra tta voc"
  86.  
  87. # Maximum resolution by height in pixels maintaining aspect ratio. Either the user defined value or the input video original value will be selected
  88. resolution=600
  89. max_video_bitrate=688 # In Kilobits per second.
  90. # Set the audio bitrate for both videos and audio files.
  91. avg_audio_bitrate=96 # In Kilobits per second.
  92. output_fps=24 # Maximum output FPS. Original or user input, whichever is lower.
  93. crf=28
  94. # Define an array of presets from slowest to fastest
  95. presets=("veryslow" "slower" "slow" "medium" "fast" "faster" "veryfast" "superfast" "ultrafast")
  96. preset=slow # Default Speed Preset
  97. # Full Log file path
  98. log_file="$HOME/storage/shared/Movies/VideoDrop/convert.log"
  99.  
  100. # Shortened log file path for terminal output
  101. log_file_echo=$(echo "$log_file" | sed 's|/data/data/com.termux/files/home|.../home|g')
  102.  
  103. # Full directory paths
  104. video_drop_dir="$HOME/storage/shared/Movies/VideoDrop"
  105. video_processing_dir="$HOME/storage/shared/Movies/VideoProcessing"
  106. output_dir_base="$HOME/storage/shared/Movies/VideoConverted"
  107.  
  108. # Shortened directory paths for terminal output
  109. video_drop_dir_echo=$(echo "$video_drop_dir" | sed 's|/data/data/com.termux/files/home|.../home|g')
  110. video_processing_dir_echo=$(echo "$video_processing_dir" | sed 's|/data/data/com.termux/files/home|.../home|g')
  111. output_dir_base_echo=$(echo "$output_dir_base" | sed 's|/data/data/com.termux/files/home|.../home|g')
  112.  
  113. # combine audio and video extension variables to pass to find command.
  114. filetypes=""
  115. for ext in $audio_ext $video_ext; do
  116.     filetypes="$filetypes -iname '*.$ext' -o"
  117. done
  118. # Remove the trailing ' -o'
  119. filetypes="${filetypes% -o}"
  120.  
  121. # Define a lock file
  122. LOCKFILE="/data/data/com.termux/files/usr/var/lock/zwmc.lock"
  123.  
  124. # Cleanup function to remove the lock file
  125. cleanup() {
  126.     rm -f $LOCKFILE
  127. }
  128.  
  129. # Set the trap to call cleanup on script exit
  130. trap cleanup EXIT
  131.  
  132. # Try to acquire the lock
  133. exec 200>$LOCKFILE
  134. flock -n 200 || { echo "Script is already running"; exit 1; }
  135.  
  136. # The lock file will be removed when the script exits, thanks to the trap
  137.  
  138. # --------------------------------
  139. #     Command Flags
  140. # --------------------------------
  141.  
  142. show_menu() {
  143.     auto_yes=true  # Turn on auto_yes when the menu is shown
  144.     while true; do
  145.         deps_status="Off"
  146.         if [ "$check_deps" = true ]; then
  147.             deps_status="On"
  148.         fi
  149.  
  150.         menu_choice=$(dialog --menu "Ziggle Wump Media Compressor Chooser" 16 50 10 \
  151.             1 "Set Max Resolution ($resolution)" \
  152.             2 "Set Max Output FPS ($output_fps)" \
  153.             3 "Set Max Video Bitrate ($max_video_bitrate)" \
  154.             4 "Set Average Audio Bitrate ($avg_audio_bitrate)" \
  155.             5 "Set Constant Rate Factor ($crf)" \
  156.             6 "Set Encoding Speed ($preset)" \
  157.             7 "Check Dependencies ($deps_status)" \
  158.             8 "Install Script" \
  159.             9 "Uninstall Script" \
  160.             10 "Compress Files in $(basename $video_drop_dir)" 2>&1 >/dev/tty)
  161.  
  162.         case $menu_choice in
  163.             1)
  164.                 new_resolution=$(dialog --inputbox "Enter resolution maximum height in pixels ($resolution):" 8 40 "$resolution" 2>&1 >/dev/tty)
  165.                 if [ $? -eq 0 ]; then
  166.                     resolution="$new_resolution"
  167.                 fi
  168.                 ;;
  169.             2)
  170.                 new_output_fps=$(dialog --inputbox "Enter maximum output FPS ($output_fps):" 8 40 "$output_fps" 2>&1 >/dev/tty)
  171.                 if [ $? -eq 0 ]; then
  172.                     output_fps="$new_output_fps"
  173.                 fi
  174.                 ;;
  175.             3)
  176.                 new_max_video_bitrate=$(dialog --inputbox "Enter maximum video bitrate ($max_video_bitrate):" 8 40 "$max_video_bitrate" 2>&1 >/dev/tty)
  177.                 if [ $? -eq 0 ]; then
  178.                     max_video_bitrate="$new_max_video_bitrate"
  179.                 fi
  180.                 ;;
  181.             4)
  182.                 new_avg_audio_bitrate=$(dialog --inputbox "Enter average audio bitrate ($avg_audio_bitrate):" 8 40 "$avg_audio_bitrate" 2>&1 >/dev/tty)
  183.                 if [ $? -eq 0 ]; then
  184.                     avg_audio_bitrate="$new_avg_audio_bitrate"
  185.                 fi
  186.                 ;;
  187.             5)
  188.                 dialog --msgbox "CRF (Constant Rate Factor) is a quality setting for video encoding. It ranges from 0 (lossless) to 51 (worst quality). Lower values mean better quality and larger files, while higher values mean lower quality and smaller files." 10 50
  189.                 new_crf=$(dialog --inputbox "Enter CRF value (0-51) ($crf):" 8 40 "$crf" 2>&1 >/dev/tty)
  190.                 if [ $? -eq 0 ]; then
  191.                     echo "User entered CRF: $new_crf" >&2  # Debug message
  192.                     if [ "$new_crf" -ge 0 ] && [ "$new_crf" -le 51 ]; then
  193.                         crf="$new_crf"
  194.                         echo "CRF set to: $crf" >&2  # Debug message
  195.                     else
  196.                         dialog --msgbox "Invalid CRF value. Please enter a value between 0 and 51." 6 40
  197.                         echo "Invalid CRF value entered: $new_crf" >&2  # Debug message
  198.                     fi
  199.                 fi
  200.                 ;;
  201.             6)
  202.                 dialog --msgbox "Choose an x265 speed preset. The preset determines the encoding speed and compression efficiency. Choose a preset that best fits your  needs. Slower presets will provide better compression and quality but take longer to encode." 10 50
  203.  
  204.                 # Your original preset selection dialog
  205.                 new_preset=$(dialog --menu "Choose  x265 Speed Preset" 15 50 9 \
  206.                     ultrafast "8" \
  207.                     superfast "7" \
  208.                     veryfast "6" \
  209.                     faster "5" \
  210.                     fast "4" \
  211.                     medium "3" \
  212.                     slow "2" \
  213.                     slower "1" \
  214.                     veryslow "0" 2>&1 >/dev/tty)
  215.  
  216.                 # Check if a new preset was selected
  217.                 if [ $? -eq 0 ]; then
  218.                     preset=$new_preset
  219.                 fi
  220.                 ;;
  221.             7)
  222.                 while true; do
  223.                     if [ "$check_deps" = true ]; then
  224.                         check_deps=false
  225.                     else
  226.                         check_deps=true
  227.                     fi
  228.                     deps_status="Off"
  229.                     if [ "$check_deps" = true ]; then
  230.                         deps_status="On"
  231.                     fi
  232.                     dialog --msgbox "Dependencies are now $deps_status" 6 40
  233.                     break
  234.                 done
  235.                 ;;
  236.             8)
  237.                 clear
  238.                 install_script
  239.                 ;;
  240.             9)
  241.                 clear
  242.                 uninstall_script
  243.                 ;;
  244.             10)
  245.                 break
  246.                 ;;
  247.             *)
  248.                 tput sgr0  # Reset terminal colors
  249.                 clear
  250.                 exit 0
  251.                 ;;
  252.         esac
  253.     done
  254. }
  255.  
  256. # Function to display help message
  257. show_help() {
  258.     width=$(tput cols)
  259.     script_name=$(basename "$0")
  260.     echo "Place your media files in $video_drop_dir_echo." | tee -a "$log_file" | fmt -w $width
  261.     echo "Usage: bash $script_name [-r resolution] [-d] [-y] [-o output_fps] [-b max_video_bitrate] [-a avg_audio_bitrate] [-p preset] [-i] [-u] [-m] [-h|--help]" | fmt -w $width
  262.     echo "  -r resolution ex. bash $script_name -r 720 Custom resolution height preserving aspect ratio" | fmt -w $width
  263.     echo "  -d Check and upgrade dependencies." | fmt -w $width
  264.     echo "  -y Automatically say yes to prompts." | fmt -w $width
  265.     echo "  -o output_fps Set custom output FPS. ex. bash $script_name -o 60" | fmt -w $width
  266.     echo "  -b max_video_bitrate Set custom max video bitrate in kilobits per second. ex. bash $script_name -b 2000" | fmt -w $width
  267.     echo "  -a avg_audio_bitrate Set custom average audio bitrate in kilobits per second. ex. bash $script_name -a 128" | fmt -w $width
  268.     echo "  -p preset Set encoding speed preset for x265 (0=slowest, 8=fastest). ex. bash $script_name -p 4" | fmt -w $width
  269.     echo "  -i Install the script to /data/data/com.termux/files/usr/bin/zwmc" | fmt -w $width
  270.     echo "  -u Uninstall the script from /data/data/com.termux/files/usr/bin/zwmc" | fmt -w $width
  271.     echo "  -m Shows the menu for setting options." | fmt -w $width
  272.     echo "  -h, --help Display this help message" | fmt -w $width
  273.     exit 0
  274. }
  275.  
  276. # Install Ziggle Wump to /usr/bin folder and widget script to ~/.shortcuts
  277. install_script() {
  278.     if [ -f /data/data/com.termux/files/usr/bin/zwmc ]; then
  279.         current_dir=$(dirname "$0")
  280.         if [ "$current_dir" != "/data/data/com.termux/files/usr/bin" ]; then
  281.             echo "Script is installed but not running from the bin directory. Reinstalling..."
  282.             cp "$0" /data/data/com.termux/files/usr/bin/zwmc
  283.             chmod +x /data/data/com.termux/files/usr/bin/zwmc
  284.             if whereis zwmc | grep -q "/data/data/com.termux/files/usr/bin/zwmc"; then
  285.                 echo "Script reinstalled to /data/data/com.termux/files/usr/bin/zwmc"
  286.             else
  287.                 echo "Reinstallation failed"
  288.             fi
  289.         else
  290.             echo "Script is already installed and running from the bin directory."
  291.         fi
  292.     else
  293.         cp "$0" /data/data/com.termux/files/usr/bin/zwmc
  294.         chmod +x /data/data/com.termux/files/usr/bin/zwmc
  295.         if whereis zwmc | grep -q "/data/data/com.termux/files/usr/bin/zwmc"; then
  296.             echo "Script installed to /data/data/com.termux/files/usr/bin/zwmc"
  297.         else
  298.             echo "Installation failed"
  299.         fi
  300.     fi
  301.  
  302.     # Create the shortcut script
  303.     mkdir -p $HOME/.shortcuts
  304.     echo '#!/data/data/com.termux/files/usr/bin/bash' > $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  305.     echo 'zwmc -m' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  306.     echo 'read -p "Press Enter to continue.  Bye!"' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  307.     echo 'case $return in' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  308.     echo '    * ) echo "Exiting script."' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  309.     echo 'esac' >> $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  310.     chmod +x $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  311.  
  312.     exit 0
  313. }
  314.  
  315. uninstall_script() {
  316.     if [ ! -f /data/data/com.termux/files/usr/bin/zwmc ]; then
  317.         echo "Script is not installed."
  318.         exit 0
  319.     else
  320.         rm -f /data/data/com.termux/files/usr/bin/zwmc
  321.         if ! whereis zwmc | grep -q "/data/data/com.termux/files/usr/bin/zwmc"; then
  322.             echo "Script uninstalled from /data/data/com.termux/files/usr/bin/zwmc"
  323.         else
  324.             echo "Uninstallation failed"
  325.         fi
  326.     fi
  327.  
  328.     # Remove the shortcut script
  329.     rm -f $HOME/.shortcuts/Ziggle\ Wump\ Media\ Compressor.sh
  330.  
  331.     exit 0
  332. }
  333.  
  334. # Parse command-line options
  335. check_deps=false
  336. auto_yes=false  # Set auto_yes to false by default
  337. while getopts "r:dyho:b:a:c:iump:" opt; do
  338.     case $opt in
  339.         r) resolution="$OPTARG" ;;  # Set custom resolution by height
  340.         d) check_deps=true ;;  # Check and upgrade dependencies
  341.         y) auto_yes=true ;;  # Automatically say yes to prompts
  342.         o) output_fps="$OPTARG" ;;  # Set custom output FPS
  343.         b) max_video_bitrate="$OPTARG" ;;  # Set custom max video bitrate
  344.         a) avg_audio_bitrate="$OPTARG" ;;  # Set custom average audio bitrate
  345.         c) crf="$OPTARG" ;;  # Set custom CRF value
  346.         i) install_script ;;  # Install the script
  347.         u) uninstall_script ;;  # Uninstall the script
  348.         m) auto_yes=true; show_menu ;;  # Show menu and turn on auto_yes
  349.         p) preset="${presets[$OPTARG]}" ;;  # Set preset based on number 0-8
  350.         h) show_help ;;  # Display help
  351.         *) show_help ;;  # Display help for invalid options
  352.     esac
  353. done
  354.  
  355. shift $((OPTIND -1))
  356.  
  357. # ---------------------------------
  358. #       Dependencies
  359. # ---------------------------------
  360.  
  361. if $check_deps; then
  362.     echo "Checking and upgrading dependencies, please wait..."
  363.     pkg update && pkg upgrade -y
  364.     if ! command -v bc &> /dev/null; then
  365.         pkg install bc -y
  366.     fi
  367.     if ! command -v ffmpeg &> /dev/null; then
  368.         pkg install ffmpeg -y
  369.     fi
  370.     if ! command -v ncurses-utils &> /dev/null; then
  371.         pkg install ncurses-utils -y
  372.     fi
  373.     echo "Update complete."
  374. fi
  375.  
  376. # -----------------------------------------------------
  377. #     Create Directories and log file
  378. # -----------------------------------------------------
  379.  
  380. # Create necessary directories if they don't exist
  381. mkdir -p "$video_drop_dir" "$video_processing_dir" "$output_dir_base"
  382.  
  383. # Change to the VideoDrop directory
  384. cd "$video_drop_dir" || { echo "Directory change failed"; exit 1; }
  385.  
  386. # Check if log file exists
  387. if [ -f "$log_file" ]; then
  388.     echo "Old log file found.  Deleting..."
  389.     rm "$log_file"
  390.     echo "File deleted."
  391. else
  392.     echo "Log File does not exist.  The script will create $log_file_echo"
  393. fi
  394.  
  395. # ------------------------
  396. #     Start script
  397. # ------------------------
  398.  
  399. width=$(tput cols)
  400.  
  401. echo "Running... Press Ctrl+C to stop."
  402.  
  403. echo ""
  404. echo "$instructions" | tee -a "$log_file" | fmt -w $width
  405.  
  406. echo ""
  407. # Function to prompt the user to continue or quit
  408. prompt_continue_or_quit() {
  409.     while true; do
  410.         # Recursively find and process videos and audio
  411.         video_count=$(eval "find \"$video_drop_dir\" -type f \( $filetypes \) | wc -l")
  412.         echo "Detected $video_count compatible video and/or audio file(s) in:" | tee -a "$log_file" | fmt -w $width
  413.    
  414.         if [ "$video_count"  -eq 0 ]; then
  415.             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.  
  416.  
  417. Supported File Types:
  418.  
  419. Video: $video_ext
  420. Audio: $audio_ext" | tee -a "$log_file" | fmt -w $width
  421.             exit 0
  422.         fi
  423.  
  424.         # Prompt text
  425.         prompt_text="$video_drop_dir_echo. You can start encoding now. Do you wish to proceed? (Y/N): "
  426.  
  427.         # Format the prompt text
  428.         formatted_prompt=$(echo "$prompt_text" | fmt -w $width)
  429.  
  430.         if $auto_yes; then
  431.             echo "$formatted_prompt"
  432.             echo "Y"
  433.             return 0  # Automatically continue
  434.         else
  435.             # Read user input with formatted prompt
  436.             read -p "$formatted_prompt" yn
  437.             case $yn in
  438.                 [Yy]* ) return 0;;  # Continue
  439.                 [Nn]* ) echo "Exiting script."; exit 0;;  # Quit
  440.                 * ) echo "Please answer Y or N.";;
  441.             esac
  442.         fi
  443.     done
  444. }
  445.  
  446. # Function to clean up file names
  447. clean_file_names() {
  448.     eval "find \"$video_drop_dir\" -type f \( $filetypes \)" | while read -r file; do
  449.         dir=$(dirname "$file")
  450.         base=$(basename "$file")
  451.         new_base=$(echo "$base" | sed 's/[^a-zA-Z0-9 ._-]//g' | tr -s ' ')
  452.         new_file="$dir/$new_base"
  453.         if [ "$file" != "$new_file" ]; then
  454.             mv "$file" "$new_file"
  455.         fi
  456.     done
  457. }
  458.  
  459. # Clean up file names before processing
  460. clean_file_names
  461.  
  462. # Prompt the user to continue or quit?
  463. prompt_continue_or_quit
  464.  
  465. echo "Starting conversion process..." | tee -a "$log_file"
  466.  
  467. # Initialize ffmpeg_custom_options
  468. 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 $crf -preset $preset -c:a libopus -vbr on -ac 2 -af \"loudnorm=I=-23:LRA=7:TP=-2\""
  469.  
  470. # Add avg audio bitrate if avg_audio_bitrate is set
  471. if [ -n "$avg_audio_bitrate" ]; then
  472.     ffmpeg_custom_options="$ffmpeg_custom_options -b:a ${avg_audio_bitrate}k"
  473. fi
  474.  
  475. # Add maxrate and bufsize options if max_video_bitrate is set
  476. if [ -n "$max_video_bitrate" ]; then
  477.     ffmpeg_custom_options="$ffmpeg_custom_options -maxrate ${max_video_bitrate}k -bufsize 60M"
  478. fi
  479.  
  480.  
  481. # -----------------------------
  482. #    Audio Encoder
  483. # -----------------------------
  484.  
  485. width=$(tput cols)
  486.  
  487. # Function to check the log file for "Killed" message
  488. check_for_killed_message() {
  489.     if grep -q " Killed     " "$log_file"; then
  490.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error: Process was killed. Check log file for details." | tee -a "$log_file"
  491.         return 1
  492.     fi
  493.     return 0
  494. }
  495.  
  496. # Function to process audio files
  497. process_audio() {
  498.     local audio="$1"
  499.     local audio_echo=$(echo "$audio" | sed 's|/data/data/com.termux/files/home|/home|g')
  500.     local relative_path="${audio#$video_drop_dir/}"
  501.     local dir_path=$(dirname "$relative_path")
  502.     local base_name=$(basename "$relative_path" | tr -cd '[:alnum:]._ -')
  503.     local output_dir="$output_dir_base/$dir_path"
  504.     local output_file="$output_dir/${base_name}_converted.opus"
  505.     local temp_output_file="$output_file.tmp"
  506.  
  507.     mkdir -p "$output_dir"  # Create output directory
  508.  
  509.     echo "$(date '+%Y-%m-%d %H:%M:%S') Processing $audio to $output_file" | tee -a "$log_file"
  510.  
  511.     # Construct the FFmpeg command for audio processing
  512.     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\""
  513.  
  514.     echo "$ffmpeg_command" | tee -a "$log_file"
  515.  
  516.     # Execute the FFmpeg command
  517.     eval $ffmpeg_command 2>&1 | tee -a "$log_file"
  518.  
  519.     # Check for "Killed" message in the log file
  520.     if ! check_for_killed_message; then
  521.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error: Process was killed. Exiting." | tee -a "$log_file"
  522.         exit 1
  523.     fi
  524.  
  525.     if [ $? -eq 0 ]; then
  526.         mv "$temp_output_file" "$output_file"  # Rename the temporary file to the final output file
  527.         mkdir -p "$video_processing_dir/$dir_path"  # Create processing directory
  528.         mv "$audio" "$video_processing_dir/$relative_path"
  529.         echo ""
  530.         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
  531.     else
  532.         echo ""
  533.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error processing \"$audio\". Check log file for details." | tee -a "$log_file"
  534.         exit 1
  535.     fi
  536. }
  537.  
  538. # ------------------------------
  539. #      Video Encoder
  540. # ------------------------------
  541.  
  542. width=$(tput cols)
  543.  
  544. # Function to check the log file for "Killed" message
  545. check_for_killed_message() {
  546.     if grep -q " Killed     " "$log_file"; then
  547.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error: Process was killed. Check log file for details." | tee -a "$log_file" | fmt -w $width
  548.         exit 1
  549.     fi
  550. }
  551.  
  552. process_video() {
  553.     local video="$1"
  554.     local video_echo=$(echo "$video" | sed 's|/data/data/com.termux/files/home|.../home|g')
  555.     local relative_path="${video#$video_drop_dir/}"
  556.     local dir_path=$(dirname "$relative_path")
  557.     local base_name=$(basename "$relative_path" | tr -cd '[:alnum:]._ -')
  558.     local output_dir="$output_dir_base/$dir_path"
  559.     local output_file="$output_dir/${base_name}_converted.mkv"
  560.     local scale_filter=""
  561.  
  562.     # Get input video resolution
  563.     input_resolution=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "$video" | tr -d '[:space:],')
  564.  
  565.     if [ -n "$resolution" ]; then
  566.         if [ "$input_resolution" -gt "$resolution" ]; then
  567.             scale_filter="scale=-2:$resolution:flags=lanczos"
  568.             echo "Input resolution ($input_resolution) is higher than user-defined resolution ($resolution). Using user-defined resolution." | tee -a "$log_file"
  569.         else
  570.             echo "Input resolution ($input_resolution) is lower or equal to user-defined resolution ($resolution). Keeping original resolution." | tee -a "$log_file"
  571.         fi
  572.     else
  573.         echo "Resolution set to: original" | tee -a "$log_file"
  574.     fi
  575.  
  576.     mkdir -p "$output_dir"  # Create output directory
  577.  
  578.     echo "$(date '+%Y-%m-%d %H:%M:%S') Processing $video to $output_file" | tee -a "$log_file"
  579.  
  580.     # Get the frame rate of the input video
  581.     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)
  582.  
  583.     # Define common frame rates
  584.     common_fps=(23.976 24 25 29.97 30 50 59.94 60)
  585.  
  586.     # Function to find the nearest valid frame rate
  587.     nearest_fps() {
  588.         local fps=$1
  589.         local nearest=${common_fps[0]}
  590.         local min_diff=$(echo "scale=5; $fps - ${common_fps[0]}" | bc | awk '{print ($1 >= 0) ? $1 : -$1}')
  591.         for rate in "${common_fps[@]}"; do
  592.             local diff=$(echo "scale=5; $fps - $rate" | bc | awk '{print ($1 >= 0) ? $1 : -$1}')
  593.             if (( $(echo "$diff < $min_diff" | bc -l) )); then
  594.                 min_diff=$diff
  595.                 nearest=$rate
  596.             fi
  597.         done
  598.         echo $nearest
  599.     }
  600.  
  601.     # Round the frame rate to the nearest valid frame rate
  602.     rounded_fps=$(nearest_fps $input_fps)
  603.  
  604.     # Determine the lower frame rate
  605.     if (( $(echo "$rounded_fps > $output_fps" | bc -l) )); then
  606.         final_fps=$output_fps
  607.     else
  608.         final_fps=$rounded_fps
  609.     fi
  610.  
  611. # Set the initial video filter option
  612. video_filter_option="yadif=2,fps=$final_fps"
  613.  
  614. # Check if scale_filter is not empty
  615. if [ -n "$scale_filter" ]; then
  616.     video_filter_option="${video_filter_option},${scale_filter}"
  617. fi
  618.  
  619. echo "Video filter option: $video_filter_option" | tee -a "$log_file"
  620.  
  621. # Detect the pixel format using ffprobe
  622. input_file="$video"
  623. 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}')
  624.  
  625. echo "Pixel format: $pix_fmt"
  626.  
  627. # Validate the pixel format
  628. valid_pix_fmt=$(ffmpeg -pix_fmts | grep -w "$pix_fmt")
  629.  
  630. # Check if 10-bit encoding is selected
  631. if [[ "$pix_fmt" == *"10le"* || "$pix_fmt" == *"10be"* ]]; then
  632.     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"
  633. fi
  634.  
  635. # Define a variable for probe size and analyze duration
  636. probe_analyze_opts="-probesize 2147483647 -analyzeduration 2147483647"
  637.  
  638. # Detect subtitle codecs and convert unsupported ones
  639. subtitle_codecs=$(ffprobe -v error $probe_analyze_opts -select_streams s -show_entries stream=codec_name -of csv=p=0 "$input_file")
  640. subtitle_map=""
  641. if [ -n "$subtitle_codecs" ]; then
  642.     subtitle_map="-map 0:s"  # Include all subtitle streams
  643.     i=0
  644.     while read -r codec; do
  645.         case "$codec" in
  646.             # Text-based subtitles to be copied directly
  647.             srt)
  648.                 subtitle_map="$subtitle_map -c:s:$i copy"
  649.                 ;;
  650.             # Other text-based subtitles to be converted to SRT
  651.             ass|ssa|webvtt|eia_608|eia_708|scc|sami|ttml|smi|teletext|mov_text|microdvd|subviewer)
  652.                 subtitle_map="$subtitle_map -c:s:$i srt"
  653.                 ;;
  654.             # Bitmap-based subtitles to be converted to DVD subtitles
  655.             dvdsub|pgs|vobsub|hdmv_pgs_subtitle|dvd_subtitle)
  656.                 subtitle_map="$subtitle_map -c:s:$i dvdsub"
  657.                 ;;
  658.             # Copy other compatible subtitles
  659.             *)
  660.                 subtitle_map="$subtitle_map -c:s:$i copy"
  661.                 ;;
  662.         esac
  663.         i=$((i + 1))
  664.     done <<< "$subtitle_codecs"
  665. else
  666.     echo "No subtitle streams detected." | tee -a "$log_file"
  667. fi
  668.  
  669. # Combine common and custom FFmpeg options
  670. 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"
  671.  
  672. # Construct the FFmpeg command
  673. local ffmpeg_command
  674. if [ -n "$valid_pix_fmt" ]; then
  675.     if [ -n "$subtitle_map" ]; then
  676.         ffmpeg_command="ffmpeg $combined_ffmpeg_options -vf \"$video_filter_option\" -pix_fmt $pix_fmt $subtitle_map"
  677.     else
  678.         ffmpeg_command="ffmpeg $combined_ffmpeg_options -vf \"$video_filter_option\" -pix_fmt $pix_fmt"
  679.     fi
  680. else
  681.     if [ -n "$subtitle_map" ]; then
  682.         ffmpeg_command="ffmpeg $combined_ffmpeg_options -vf \"$video_filter_option\" $subtitle_map"
  683.     else
  684.         ffmpeg_command="ffmpeg $combined_ffmpeg_options -vf \"$video_filter_option\""
  685.     fi
  686. fi
  687.  
  688. # Ensure all subtitle streams are marked as "default: off"
  689. subtitle_streams=$(ffprobe -v error $probe_analyze_opts -select_streams s -show_entries stream=index -of csv=p=0 "$input_file")
  690. if [ -n "$subtitle_streams" ]; then
  691.     for stream_index in $subtitle_streams; do
  692.         ffmpeg_command="$ffmpeg_command -disposition:s:$stream_index 0"
  693.     done
  694. fi
  695.  
  696. # Add the output file name at the end
  697. ffmpeg_command="$ffmpeg_command \"$output_file\""
  698.  
  699. echo "FFmpeg command: $ffmpeg_command" | tee -a "$log_file"
  700.  
  701. # Execute the FFmpeg command
  702. if ! eval $ffmpeg_command 2>&1 | tee -a "$log_file"; then
  703.     echo "FFmpeg command failed. Check the log for details." | tee -a "$log_file"
  704.     exit 1
  705. fi
  706.  
  707.     # Check for "Killed" message in the log file
  708.     check_for_killed_message
  709.  
  710.    if [ $? -eq 0 ]; then
  711.         mkdir -p "$video_processing_dir/$dir_path"  # Create processing directory
  712.         mv "$video" "$video_processing_dir/$relative_path"
  713.         echo ""
  714.         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
  715.     else
  716.         echo ""
  717.         echo "$(date '+%Y-%m-%d %H:%M:%S') Error processing \"$video\". Check log file for details." | tee -a "$log_file"
  718.         exit 1
  719.     fi
  720. }
  721.  
  722. # --------------------------------------
  723. #    Start Batch Encoding
  724. # --------------------------------------
  725.  
  726. width=$(tput cols)
  727.  
  728. stop_processing=false
  729.  
  730. # Function to handle SIGINT (Ctrl+C)
  731. handle_sigint() {
  732.     echo ""
  733.     echo "Caught SIGINT (Ctrl+C). Exiting immediately." | fmt -w $width
  734.     stop_processing=true
  735.     exit 1  # Exit immediately without performing cleanup
  736. }
  737.  
  738. # Trap SIGINT and call the handle_sigint function
  739. trap handle_sigint SIGINT
  740.  
  741. # Process each file found
  742. eval "find \"$video_drop_dir\" -type f \( $filetypes \)" | while read -r file; do
  743.     if [ "$stop_processing" = true ]; then
  744.         echo "Stopping processing due to SIGINT."
  745.         exit 1
  746.     fi
  747.  
  748.     case "$file" in
  749.         *.mp3|*.aac|*.wav|*.flac|*.ogg|*.m4a|*.wma|*.ac3|*.eac3|*.opus|*.amr|*.aiff|*.alac|*.caf|*.dts|*.mka|*.mp2|*.ra|*.tta|*.voc)
  750.             process_audio "$file" || exit 1
  751.             ;;
  752.  
  753.         *.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)
  754.             process_video "$file" || exit 1
  755.             ;;
  756.     esac
  757. done
  758.  
  759. # -----------------------------
  760. #     Finishing Up
  761. # -----------------------------
  762.  
  763. width=$(tput cols)
  764.  
  765. find "$PWD" -type d -empty -delete
  766. echo "removed empty folders in $video_drop_dir_echo directory"
  767.  
  768. # final output message
  769. echo ""
  770. 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
  771. exit 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement