Browser view only. Use the download link if you want to save the file.
Back to Toolbox#!/bin/bash
## SKIP_PEAK_ANALYSIS=0
source ./launch_random_terminal.sh
source ./load_video_files.sh
# Ensure SKIP_MP4KILLER has a default value (set in make_MKV.sh, make_MPG.sh, etc.)
if [[ -z "${SKIP_MP4KILLER+x}" ]]; then
SKIP_MP4KILLER=0
fi
# --- ensure audio normalized before any conversions ---
if [[ ! -f ".audio_check_done" ]]; then
echo "Running initial audio check + normalization..."
## separate box, more readable ./audio_check.sh -noterminal
./audio_check.sh
echo
else
echo "Audio already checked (flag found)."
fi
# Get the name of the script (without the path)
script_shortname=$(basename "$0" .sh)
## DEBUGGING ENABLE HERE ##
#debugfile="debug_${script_shortname}.txt"
debugfile="/dev/null"
# --- smart exit helpers ---
# True if script is sourced, not directly executed
is_sourced() { [[ "${BASH_SOURCE[0]}" != "$0" ]]; }
# True if running inside a mate-terminal or gnome-terminal session
from_terminal() {
# Detect typical terminal envs (TERM, parent process, or $profile)
[[ -n "$profile" ]] ||
[[ "$TERM" == "xterm"* || "$TERM" == "linux" ]] ||
ps -o comm= -p "$PPID" | grep -Eq 'mate-terminal|gnome-terminal|x-terminal-emulator'
}
safe_return() {
local code=${1:-0}
if is_sourced; then
return "$code" 2>/dev/null || exit "$code"
else
exit "$code"
fi
}
#### Hard quick overrides till picker can choose them ###
## Note, they MUST end in commas to make the list right, and yadif will end it if any are selected
#qparam="-q:v 0"
#qparam="-crf 18"
# === universal safe blind shot based on below + chatgpt suggestion ===
# === this worked on all 4 Tipton and Limon vids, noticed it could almost be run blindly on all jobs ===
#brightfix="curves=all='0/0 0.25/0.33 0.50/0.62 0.80/0.86 1/0.98',eq=brightness=0:contrast=1.06:saturation=1.20"
# === de-orange oversaturated witnesses + autobright attempt ===
# now with a bit of desat, hue push
#brightfix="curves=all='0/0 0.25/0.34 0.50/0.64 0.80/0.84 1/0.96',colortemperature=13000:pl=1,curves=r='0/0 0.5/0.42 1/0.88',eq=contrast=1.01:saturation=0.92:brightness=0.05,hue=h=345"
# STRONG
#brightfix="curves=all='0/0 0.25/0.34 0.50/0.64 0.80/0.84 1/0.96',colortemperature=13000:pl=1,curves=r='0/0 0.5/0.42 1/0.88',eq=contrast=1.03:saturation=1.12"
# MEDIUM
#brightfix="curves=all='0/0 0.25/0.34 0.50/0.63 0.80/0.85 1/0.97',colortemperature=12000:pl=1,curves=r='0/0 0.5/0.45 1/0.9',eq=contrast=1.04:saturation=1.10"
# === Earlier attempts that were onesy twosy
#brightfix="curves=all='0/0 0.25/0.33 0.50/0.62 0.80/0.86 1/0.98',eq=brightness=0.05:contrast=1.0:saturation=0.97" #Arango dim, white shirt
#brightfix="rotate=-2*PI/180,unsharp" #Insalaco pair for ALVS -- Rotate -2 degrees, sharpen
#brightfix="rotate=-2*PI/180,crop=in_w-80:in_h-80,unsharp=5:5:1.5:5:5:0.0" #Same rotate as above, but 40 pixels off each edge + extra sharpening
#brightfix="eq=contrast=0.95:saturation=0.90:brightness=0.05,hue=h=25" #Sgt Ruiz
#brightfix="curves='all=0/0 0.3/0.5 0.7/0.8 1/1',eq=brightness=0.015:contrast=1.1:saturation=1.2" #brighten due to white shirt
#brightfix="curves='all=0/0 0.3/0.5 0.7/0.8 1/1',eq=brightness=0.045:contrast=1.1:saturation=1.1" #chatGPT4 solution wow!
#brightfix="eq=contrast=1.1:saturation=0.65:brightness=0.05, hue=h=355" #chatGPT4 (Charmara Dailey)
#brightfix="eq=contrast=0.9:saturation=1.05:brightness=0.08" #(Glen Barnhill)
#brightfix="curves=all='0/0 0.15/0.30 0.40/0.65 0.70/0.88 0.92/0.97 1/1',eq=brightness=0.03:contrast=1.07:saturation=1.15" #Clifford Walker DC
#brightfix="curves=all='0/0 0.15/0.30 0.40/0.65 0.70/0.88 0.92/0.97 1/1',eq=brightness=0.20:contrast=1.07:saturation=1.15" #Jorge Perez
#brightext=.Brightfix
#scale="-s 1920x1080," #outside the -vf
#aspstring="-aspect 16:9" #outside the -vf
crop4x3param="crop=iw/4*3:ih," #inside the -vf
cropTBparam="crop=in_w:in_h*0.5773:0:in_h*0.2216,"
#### brightfix options for reference ####
# [C] brightfix=-vf "curves=preset=lighter, yadif"
# [U] brightfix=-vf "eq=brightness=0.0:contrast=1.0:saturation=1.0, unsharp"
# [2] brightfix=-vf "eq=brightness=0.18:contrast=1.20:saturation=1.2, unsharp"
# [3] brightfix=-vf "eq=brightness=0.22:contrast=1.25:saturation=1.2, unsharp"
# [desat] brightfix=-vf "eq=brightness=0.05:contrast=1.0:saturation=0.75"
# [depink] brightfix=-vf "hue=h=020:s=0.98"
# [kh1] brightfix=-vf "hue=h=015:s=0.98, eq=brightness=0.05:contrast=1.0:saturation=0.65"
# [chatgpt help] curves=all='0/0 0.3/0.5 0.7/0.8 1/1',eq=brightness=0.043:contrast=1.1:saturation=1.1
echo Make_MP4 started. Checking script name... > $debugfile
# Get the name of the script (without the path, but with the .sh)
script_name=$(basename "$0")
echo ... script is $script_name >>$debugfile
if [[ -v preset_mode ]]; then
echo "preset_mode is $preset_mode"
MODE=$preset_mode
targetEXT="${preset_mode,,}"
# Check if the script name matches make_MP4.sh or make_MKV.sh
elif [[ "$script_name" == "make_MP4.sh" ]]; then
echo "This is the MP4 conversion script."
MODE="MP4"
targetEXT="mp4"
elif [[ "$script_name" == "make_MKV.sh" ]]; then
echo "This is the MKV conversion script."
MODE="MKV"
targetEXT="mkv"
elif [[ "$script_name" == "make_MPEG1.sh" || "$script_name" == "make_Joseph.sh" || "$script_name" == "make_MPG.sh" ]]; then
echo "This is the MPEG1 conversion script."
MODE="MPG"
targetEXT="mpg"
elif [[ "$script_name" == "make_MP3.sh" ]]; then
echo "This is the MP3 conversion script."
MODE="MP3"
targetEXT="mp3"
else
echo ... unknown script, so starting clarification loop >> $debugfile
while true; do
clear
echo "This is an unknown script name $script_name. "
echo
echo "Which type of files would you like to make?"
echo
echo "Operate as MP4 maker or MKV maker?"
echo "[1] = MPEG-1 480p 1800k"
echo "[4] = MP4 (h264/AVC) "
echo "[K] = MKV (h265/HEVC)"
echo "[3] = MP3 audio 96k"
echo
echo ""
# Read a single character without echoing
read -rsn1 -p $'\e[1;97;44m Press 1, 4, K, 3 or ESC / Q to quit \e[0m' input
# Handle quitting options: q, Q, or ESC
if [[ $input == 'q' || $input == 'Q' || $input == $'\033' ]]; then
echo "Exiting the script."
kill -- "$PPID"
exit 0
elif [[ $input == '4' ]]; then
MODE="MP4"
targetEXT="mp4"
echo ... setting MP4 mode >> $debugfile
break # Exit the loop after valid input
elif [[ $input == 'k' || $input == 'K' ]]; then
MODE="MKV"
targetEXT="mkv"
echo ... setting MKV mode >> $debugfile
break # Exit the loop after valid input
elif [[ $input == '1' ]]; then
MODE="MPG"
targetEXT="mpg"
echo ... setting MPG mode >> $debugfile
break # Exit the loop after valid input
elif [[ $input == '3' ]]; then
MODE="MP3"
targetEXT="mp3"
echo ... setting MP3 mode >> $debugfile
break # Exit the loop after valid input
fi
done
fi
echo Out of the loop, continuing >> "$debugfile"
# no point in a welcome here, pick_conversions wipes it out and has its own
source ./pick_conversions.sh
## we have $filenames[] populated now, safe to count
total_jobs=${#filenames[@]}
job_num=1
#I'm not sure I'll need this, but this is the location - before picking
if [[ "$MODE" == "MPG" || "$MODE" == "MP3" ]]; then ENABLE_QUALITY=0; else ENABLE_QUALITY=1; fi
if [[ "$ENABLE_QUALITY" == "1" ]]; then
echo "DEBUG: Mode is $MODE, Enable Quality: $ENABLE_QUALITY" >> $debugfile
# pick_rez_quality should watch for preset_rez now
source ./pick_rez_quality.sh
#crop4x3param="crop=iw/4*3:ih," #inside the -vf
#cropTBparam="crop=in_w:in_h*0.5773:0:in_h*0.2216,"
if [[ "$aspect" == "4x3" ]]; then
echo Found aspect as 4x3 in $LINENO >> $debugfile
cropstring="$crop4x3param"
else
echo Aspect isn't 4x3, it's $aspect in $LINENO >> $debugfile
fi
if [[ "$aspect" == "TopBotCrop" ]]; then
cropstring="$cropTBparam"
echo Found aspect as TopBotCrop in $LINENO >> $debugfile
else
echo Aspect isn't TopBotCrop, it's $aspect in $LINENO >> $debugfile
fi
fi
speak() {
local message="$1"
if [[ $input == '1' ]]; then
return 0
fi
if [ -e "DND" ]; then
echo "DND is ON."
mpg123 -n 50 "$HOME/bellding.mp3"
return 0 # You can specify a status if needed
fi
#echo "(SayText \"$message\")" | festival --pipe
pico2wave -w=output.wav "$message"
aplay output.wav
rm output.wav
}
update_title() {
local message="$1"
echo -ne "\033]0;$message\007"
}
generate_progress_bar() {
local current=$1
local total=$2
local bar=""
# Define block characters
local filled_char="▆" # Full Block
local empty_char=" " # Light Shade was ░
# Calculate the number of filled and empty blocks
local filled_length=$(( current * 2 )) # 2 blocks per job
local total_length=$(( total * 2 )) # Total blocks
# Ensure filled_length does not exceed total_length
if [ "$filled_length" -gt "$total_length" ]; then
filled_length=$total_length
fi
# Generate filled blocks
for ((i=1; i<=filled_length; i++)); do
bar+="$filled_char"
done
# Generate empty blocks
for ((i=filled_length+1; i<=total_length; i++)); do
bar+="$empty_char"
done
echo "[$bar]"
}
my-notify () {
local message="$1"
USE_SYSTEM_NOTIFY="0" # Correct variable assignment
statusfile="$HOME/status.log" # Expand home directory properly
this_script=$(basename "$script_name" .sh)
# Generate the progress bar
local progress_bar=$(generate_progress_bar "$job_num" "$total_jobs")
if [ "$USE_SYSTEM_NOTIFY" == "1" ]; then
notify-send -t 3000 "$message"
else
## before counter added: echo -e "${color} $this_script: \033[0m $message" >> "$statusfile"
if [[ "$total_jobs" -gt 1 ]]; then
# Add "(job_num of total_jobs)" after the script name
echo -e "${color} $this_script : \033[0m $progress_bar $message" >> "$statusfile"
else
echo -e "${color} $this_script: \033[0m $message" >> "$statusfile"
fi
fi
}
## we're running, let's bring up ffdash if not already active
FFDASH_PATH="$HOME/bin/launch_ffdash.sh"
FFFLAG="$HOME/bin/ffdash_running.flag"
# --- Launch ffdash monitor if not active ---
if [[ -f "$FFFLAG" ]]; then
# Check if the PID inside flag (if stored) or any ffdash process is still alive
if pgrep -f "launch_ffdash.sh" >/dev/null 2>&1; then
echo "ffdash already running."
else
echo "ffdash flag found but process not active — relaunching."
rm -f "$FFFLAG"
fi
fi
if [[ ! -f "$FFFLAG" ]]; then
echo "Launching ffdash monitor..."
if command -v mate-terminal >/dev/null 2>&1; then
mate-terminal --title="FFdash" -- bash -c "'$FFDASH_PATH'"
elif command -v gnome-terminal >/dev/null 2>&1; then
gnome-terminal --title="FFdash" -- bash -c "'$FFDASH_PATH'" 2>/dev/null
else
xterm -T "FFdash" -e "$FFDASH_PATH"
fi
sleep 1
fi
# Function to ask user if they want to move source files to a folder
move_files() {
# Extract the extension from the first input file (original extension) and convert it to uppercase
ext=$(echo "${filenames[0]##*.}" | tr '[:lower:]' '[:upper:]')
folder_name="orig ${ext} files"
# Speak the question (if speak function is active)
speak "Move the source files to a new folder?"
# Prepare prompt appendage if folder already exists
if [ -d "$folder_name" ]; then
# count only regular files in the folder (not directories)
fcount=$(find "$folder_name" -maxdepth 1 -type f | wc -l)
appendage=" ($fcount files in \"$folder_name\" folder now)"
else
appendage=""
fi
while true; do
# Print the prompt on the same line and wait for user input
short_desc="(${ext} -> ${targetEXT})"
# Add colored appendage
if [ -n "$appendage" ]; then
appendage_colored=$'\e[47m\e[30m'"$appendage"$'\e[0m\e[1;97;44m'
else
appendage_colored=""
fi
read -rsn1 -p $'\e[1;97;44m '"${short_desc}"' - Move files into "'"${folder_name}"'" folder'"${appendage_colored}"'? (Y/N) : ' input
echo -e "\e[0m" # Ensures prompt is reset correctly
echo # Move to the next line after input is read
case "$input" in
[Yy])
echo "Moving files to $folder_name..."
mkdir -p "$folder_name" # Create the folder if it doesn't exist
for inputfile in "${filenames[@]}"; do
mv "$inputfile" "$folder_name/"
done
echo "Files moved to $folder_name."
break
;;
[Nn])
echo "Files will not be moved."
break
;;
*)
# Beep for invalid input
echo -ne '\007' # Terminal beep sound
echo "Invalid input. Please press Y or N."
;;
esac
done
}
# ========== PARAMS ========================
MPGbitrate="1800k"
##params1mpg="-f mpeg -r 29.97 -write_tmcd 0 -pix_fmt yuv420p"
##params3mpg="-b:v $MPGbitrate -maxrate $MPGbitrate -minrate $MPGbitrate -bufsize 32768k -sws_flags lanczos -s 720:480 -aspect 16:9 -g 15 -ar 48000 -ab 128k"
params1mpg="-f mpeg -r 29.97 -pix_fmt yuv420p"
params3mpg="-b:v 1800k -maxrate 1800k -minrate 1800k -bufsize 32768k -sws_flags lanczos+accurate_rnd+full_chroma_int -s 720x480 -aspect 16:9 -g 12 -bf 2 -qmin 2 -qmax 8 -ar 48000 -ac 2 -ab 160k"
##params1mp4="-vcodec libx264 -preset faster -pix_fmt yuv420p"
params1mp4="-fflags +genpts -avoid_negative_ts make_zero -ignore_editlist 1 -vcodec libx264 -preset faster -pix_fmt yuv420p"
##params3mp4="$scale $aspstring -max_muxing_queue_size 9999 -r 29.97 $qparam -tune film -ar 48000 -c:a aac -b:a 256k"
##params3mp4="$scale $aspstring -max_muxing_queue_size 9999 $qparam -tune film -c:a aac -b:a 256k -ar 48000 -ac 2"
params3mp4="$scale $aspstring -max_muxing_queue_size 9999 $qparam -tune film -c:a aac -b:a 256k -ar 48000 -ac 2 -af asetpts=PTS-STARTPTS,aresample=48000"
params1mkv="-c:v libx265 -preset superfast $qparam"
params3mkv="$scale $aspstring -max_muxing_queue_size 9999 -c:a aac -b:a 256k -ar 48000 -ac 2 -af asetpts=PTS-STARTPTS,aresample=48000"
MP3bitrate="96k"
params1mp3="-f mp3 -vn -ar 48000 -ac 2 -ab $MP3bitrate"
params3mp3="" # no more needed
## copy params over to the generic params1/3 variables based on the MODE we're in
if [ "$MODE" == "MP4" ]; then
params1="$params1mp4"
params3="$params3mp4"
elif [ "$MODE" == "MKV" ]; then
params1="$params1mkv"
params3="$params3mkv"
elif [ "$MODE" == "MPG" ]; then
params1="$params1mpg"
params3="$params3mpg"
elif [ "$MODE" == "MP3" ]; then
params1="$params1mp3"
params3="$params3mp3"
else
echo "Unknown mode: $MODE in $LINENO"
kill -- "$PPID"
exit 1
fi
#echo "params1=$params1" >> "$debugfile"
#echo "params3=$params3" >> "$debugfile"
# ==========================================
# Extract the filenames without extension
for i in "${!filenames[@]}"; do
inputfile="${filenames[$i]}"
basename=$(basename "$inputfile" | sed 's/\.[^.]*$//')
echo basename is $basename. >> $debugfile
# Conditional output filename based on the mode
if [[ "$MODE" == "MPG" || "$MODE" == "MP3" ]]; then
# For MPG, only include brightness adjustments or crops, no resolution or quality
outfile="${basename}${brightext}.${targetEXT}"
else
# For other modes (MP4, MKV), include resolution and quality
outfile="${basename}.${resolution}.${quality}.${targetEXT}"
fi
echo "Processing file: $inputfile" >> "$debugfile"
# Change the title of the terminal window
# Extract the duration of the input video using ffmpeg (trim the fractional second)
video_duration=$(ffmpeg -i "$inputfile" 2>&1 | grep "Duration" | cut -d ' ' -f 4 | sed 's/\.[0-9]*,//')
# Change the title of the terminal window to include the runtime
update_title "${basename} → $MODE (${video_duration}) $dBtitle"
# Run ffmpeg with the same parameters
echo Calling speak function.... >> $debugfile
if [ "$MODE" == "MPG" ]; then
speak "Mpeg one conversion started on $basename"
elif [[ $MODE == "MP3" ]]; then
speak "Emm pee three conversion started on $basename"
else
speak "$MODE conversion started on $basename"
fi
echo
my-notify "${inputfile##*/} -> $outfile ..."
echo Brightfix=$brightfix. and scale=$scale. and aspstring=$aspstring. >> "$debugfile"
# Concatenate the non-empty filter strings
filter_chain=""
## don't put scale in filterchain [[ -n "$scale" ]] && filter_chain+="$scale"
[[ -n "$cropstring" ]] && filter_chain+="$cropstring" ## crop goes in -vf
[[ -n "$brightfix" ]] && filter_chain+="$brightfix, yadif" ## last thing in -vf to close the comma list
##if crop or brightening is used, cram ext additions into $brightext
if [[ -n "$cropstring" ]]; then
brightext=".crop"
echo Cropstring populated, adding .crop to ext in $LINENO >> $debugfile
else
echo Cropstring NOT populated, no .crop added in $LINENO >> $debugfile
fi
if [[ -n "$brightfix" ]]; then
brightext+=".B1"
echo Brightfix populated, adding .B1 to ext in $LINENO >> $debugfile
else
echo Brightfix NOT populated, no .B1 added in $LINENO >> $debugfile
fi
# Debugging the filter_chain before adding yadif
echo "Filter chain: '$filter_chain'" >> "$debugfile"
# --- unified ffmpeg launcher with /tmp/ffdash params/progress tracking ---
# rename outfile name if filters are active
if [[ -n "$filter_chain" ]]; then
echo "#with vf"
if [ "$MODE" == "MPG" ]; then
outfile="${basename}$brightext.$targetEXT"
else
outfile="${basename}.${resolution}.${quality}$brightext.$targetEXT"
fi
echo "Filters enabled, changed name to $outfile" >> "$debugfile"
else
echo "#without vf"
echo "No filters enabled, name stays $outfile" >> "$debugfile"
fi
# --- setup progress + params in /tmp/ffdash ---
mkdir -p /tmp/ffdash
jobbase="${outfile##*/}"
paramfile="/tmp/ffdash/${jobbase}.params"
progressfile="/tmp/ffdash/${jobbase}.progress"
# get runtime string the same way as your titlebar
video_duration=$(ffmpeg -i "$inputfile" 2>&1 | grep "Duration" | cut -d ' ' -f 4 | sed 's/,//' | sed 's/\.[0-9]*$//')
if [[ -z "$video_duration" ]]; then
duration_seconds=0
else
IFS=: read -r h m s <<<"$video_duration"
duration_seconds=$(awk "BEGIN {print ($h * 3600) + ($m * 60) + $s}")
fi
# write params file
cat >"$paramfile" <<EOF
src=$inputfile
out=$outfile
duration=$duration_seconds
job=$job_num
total=$total_jobs
EOF
echo "Progress file: $progressfile" >> "$debugfile"
echo "Param file: $paramfile (duration=${duration_seconds}s)" >> "$debugfile"
# --- build ffmpeg command dynamically ---
ff_cmd=( ffmpeg -i "$inputfile" )
# add first param group
[[ -n "$params1" ]] && ff_cmd+=( $params1 )
# add filters if needed
# Always force CFR 29.97 in the video filtergraph (reliable)
if [[ -n "$filter_chain" ]]; then
ff_cmd+=( -vf "${filter_chain},fps=30000/1001" )
else
ff_cmd+=( -vf "fps=30000/1001" )
fi
# add audio / rest of params
[[ -n "$params3" ]] && ff_cmd+=( $params3 )
# add progress + output
ff_cmd+=( -progress "$progressfile" "$outfile" )
echo "EXECUTING : ${ff_cmd[*]}" >> "$debugfile"
# --- run ffmpeg ---
"${ff_cmd[@]}"
ffmpeg_status=$?
# --- cleanup ---
rm -f "$progressfile" "$paramfile"
# notify on result
if [ $ffmpeg_status -ne 0 ]; then
echo "FFmpeg failed with exit code $ffmpeg_status" >> "$debugfile"
my-notify " \033[41;97m FFmpeg FAILED!!! \033[0m exit code $ffmpeg_status for $outfile"
# Ask user if they want to continue or abort the batch
echo
echo -e "\e[1;41;97m ⚠️ FFmpeg error for $outfile (exit $ffmpeg_status) \e[0m"
echo "FFmpeg encountered an error."
echo
echo "Press C to continue with the next file, or Q to abort the batch."
echo
while true; do
read -rsn1 -p $'\e[1;97;44m [C]ontinue / [Q]uit \e[0m: ' choice
echo
case "$choice" in
[Cc])
echo "Continuing to next file..." >> "$debugfile"
break
;;
[Qq])
echo "User chose to abort batch after FFmpeg error." >> "$debugfile"
echo "Aborting batch..."
kill -- "$PPID"
exit "$ffmpeg_status"
;;
*)
echo "Invalid choice. Press C or Q."
;;
esac
done
fi
# --- only runs if FFmpeg succeeded ---
echo "FFmpeg succeeded" >> "$debugfile"
my-notify "$outfile \033[42;97m finished \033[0m"
# --- end unified ffmpeg launcher ---
# Increment the job counter
job_num=$((job_num + 1))
done
# --- Only reach here if all FFmpeg jobs succeeded ---
echo "All jobs finished successfully" >> "$debugfile"
my-notify "Batch complete"
if [ "$MODE" != "MP3" ]; then
update_title "*** DONE *** ${basename}"
# Avoid double ding with move_files prompt
if [[ ! -e "DND" ]]; then
speak "All jobs finished"
fi
move_files
else
# MP3 mode has no move_files; safe to ding once here
speak "All jobs finished"
fi
# If we launched from double-click, exit the terminal cleanly unless there's post minilauncher biz waiting (joseph cleanup)
if from_terminal; then
echo "All conversions completed. Closing window..."
sleep 1
if [[ "${SKIP_MP4KILLER:-0}" -eq 1 ]]; then
# Debug or chained mode — skip killing terminal
safe_return 0
else
# Normal mode — close the terminal completely
kill -- "$PPID"
exit 0
fi
else
safe_return 0
fi