#!/bin/bash input="$1" output="$2" target_size_raw="$3" if [[ -z "$input" || -z "$output" || -z "$target_size_raw" ]]; then echo "Usage: $0 input output.jpg " exit 1 fi parse_size() { local size="$1" size=$(echo "$size" | tr '[:lower:]' '[:upper:]' | tr -d ' ') if [[ "$size" =~ ^([0-9]+(\.[0-9]+)?)(B|KB|MB)?$ ]]; then value="${BASH_REMATCH[1]}" unit="${BASH_REMATCH[3]}" case "$unit" in ""|"B") multiplier=1 ;; "KB") multiplier=1024 ;; "MB") multiplier=$((1024 * 1024)) ;; *) echo "Invalid size unit: $unit"; exit 1 ;; esac awk "BEGIN {printf \"%d\", $value * $multiplier}" else echo "Invalid size format: $size" exit 1 fi } target_size=$(parse_size "$target_size_raw") compress_and_get_size() { local q=$1 magick "$work" -strip -interlace Plane -quality "$q" "$output" stat -c%s "$output" } work=$(mktemp --suffix=.jpg) cp "$input" "$work" magick "$input" \ -auto-orient \ -colorspace sRGB \ -strip \ -background white -alpha remove -alpha off \ "$work" echo "Trying to fit below $target_size bytes" while true; do # Binary search low=1 high=95 best_q=1 while (( low <= high )); do mid=$(((low + high) / 2)) size=$(compress_and_get_size "$mid") echo " Quality $mid -> $size bytes" best_size=$size if (( size <= target_size )); then best_q=$mid low=$((mid + 1)) else high=$((mid - 1)) fi done echo "Best quality this round: $best_q ($best_size bytes)" if (( best_size <= target_size )); then echo "DONE: $output is $best_size bytes" break fi echo " Reducing resolution" tmp=$(mktemp --suffix=.jpg) magick "$work" -resize 75% -quality 85 "$tmp" mv "$tmp" "$work" done rm -f "$work"