Files
Bash-Utility-Scripts/image-tools/optimize-jpg.sh
2025-09-27 18:39:33 +02:00

297 lines
10 KiB
Bash

#!/bin/bash
# Script to compress JPG images with progress bar
# Usage: ./minimize-jpg.sh [quality] file1.jpg file2.jpg ...
# Usage: ./minimize-jpg.sh [quality] *.jpg
# Works as Nemo script with selected files
# Default quality: 90
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to get quality from user via dialog (for Nemo)
get_quality_dialog() {
local quality=90
# Try zenity
if command -v zenity &> /dev/null; then
quality=$(zenity --entry \
--title="JPG Compression Quality" \
--text="Enter compression quality (1-100):" \
--entry-text="90" \
2>/dev/null)
# Check if user cancelled or entered invalid value
if [ $? -ne 0 ] || [ -z "$quality" ]; then
echo "90"
return
fi
else
# No dialog available, use default
echo "90"
return
fi
# Validate quality
if [[ "$quality" =~ ^[0-9]+$ ]] && [ "$quality" -ge 1 ] && [ "$quality" -le 100 ]; then
echo "$quality"
else
echo "90"
fi
}
check_imagemagick() {
if ! command -v mogrify &> /dev/null; then
echo -e "${RED}Error: ImageMagick is not installed.${NC}"
echo -e "${YELLOW}Install ImageMagick with:${NC}"
echo " Ubuntu/Debian: sudo apt-get install imagemagick"
echo " CentOS/RHEL: sudo yum install ImageMagick"
echo " Arch: sudo pacman -S imagemagick"
exit 1
fi
}
# Main function
main() {
local quality=90
local file_args=()
local quality_was_explicit=false
# Parse arguments
for arg in "$@"; do
if [[ "$arg" =~ ^[0-9]+$ ]] && [ "$arg" -ge 1 ] && [ "$arg" -le 100 ]; then
quality="$arg"
quality_was_explicit=true
else
# Add all non-numeric arguments as potential file paths
file_args+=("$arg")
fi
done
# Check if ImageMagick is installed
check_imagemagick
# Create output directory
local output_dir="min"
if [ ! -d "$output_dir" ]; then
mkdir -p "$output_dir"
echo -e "${GREEN}Created output directory: $output_dir${NC}"
fi
# Get files to process
local jpg_files=()
# If we have file arguments, process them
if [ ${#file_args[@]} -gt 0 ]; then
# Process file arguments (expand patterns and check files)
for pattern in "${file_args[@]}"; do
# Use shell expansion for patterns like *.jpg
for file in $pattern; do
if [[ -f "$file" ]] && [[ "$file" =~ \.(jpg|jpeg|JPG|JPEG)$ ]]; then
jpg_files+=("$file")
fi
done
done
else
echo -e "${RED}Error: No files specified.${NC}"
echo -e "${YELLOW}Usage from terminal: $0 [quality] file1.jpg file2.jpg ...${NC}"
echo -e "${YELLOW}Usage from terminal: $0 [quality] *.jpg${NC}"
echo -e "${YELLOW}Or select files in Nemo and run as script${NC}"
exit 1
fi
if [ ${#jpg_files[@]} -eq 0 ]; then
echo -e "${YELLOW}No JPG files found to process.${NC}"
exit 0
fi
local total_files=${#jpg_files[@]}
local total_size_before=0
local total_size_after=0
# Detect if running from Nemo (file manager) or terminal
local use_gui=false
local quality_from_dialog=false
local original_quality=$quality
# Only use GUI if:
# 1. Quality wasn't explicitly set
# 2. We're likely running from Nemo (NEMO_SCRIPT_SELECTED_FILE_PATHS is set)
# 3. zenity is available
if [ "$quality_was_explicit" = false ] && [ -n "$NEMO_SCRIPT_SELECTED_FILE_PATHS" ] && command -v zenity &> /dev/null; then
local dialog_quality
dialog_quality=$(get_quality_dialog)
if [ -n "$dialog_quality" ]; then
quality="$dialog_quality"
quality_from_dialog=true
use_gui=true
fi
fi
# Show initial info only for terminal
if [ "$use_gui" = false ]; then
echo -e "${GREEN}JPG compression started${NC}"
echo -e "${BLUE}Quality set to: ${quality}%${NC}"
echo -e "${BLUE}Files to process: ${total_files}${NC}"
echo -e "${BLUE}Output directory: ${output_dir}${NC}"
echo ""
fi
# Calculate total size before compression
for file in "${jpg_files[@]}"; do
if [ -f "$file" ]; then
size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
total_size_before=$((total_size_before + size))
fi
done
# Process each file with appropriate progress display
local count=0
if [ "$use_gui" = true ]; then
# Use zenity progress bar for GUI
(
for file in "${jpg_files[@]}"; do
if [ -f "$file" ]; then
count=$((count + 1))
filename=$(basename "$file")
output_file="${output_dir}/${filename}"
echo "# Compressing $filename... ($count/$total_files)"
# Copy and compress the file
cp "$file" "$output_file"
mogrify -quality "$quality" "$output_file"
echo "$((count * 100 / total_files))"
fi
done
) | zenity --progress --title="JPG Compression" --text="Initializing..." --percentage=0 --auto-close
# Check if user cancelled
if [ $? -ne 0 ]; then
zenity --error --text="Compression was cancelled."
exit 1
fi
else
# Use terminal progress bar
for file in "${jpg_files[@]}"; do
if [ -f "$file" ]; then
count=$((count + 1))
filename=$(basename "$file")
output_file="${output_dir}/${filename}"
# Show terminal progress
local percentage=$((count * 100 / total_files))
printf "\r${BLUE}[%3d%%] Processing $filename ($count/$total_files)${NC}" "$percentage"
# Copy and compress the file
cp "$file" "$output_file"
mogrify -quality "$quality" "$output_file"
fi
done
echo "" # New line after progress
fi
# Calculate total size after compression
for file in "${jpg_files[@]}"; do
filename=$(basename "$file")
output_file="${output_dir}/${filename}"
if [ -f "$output_file" ]; then
size=$(stat -f%z "$output_file" 2>/dev/null || stat -c%s "$output_file" 2>/dev/null)
total_size_after=$((total_size_after + size))
fi
done
# Calculate statistics
local saved_bytes=$((total_size_before - total_size_after))
local saved_percentage=0
if [ "$total_size_before" -gt 0 ]; then
saved_percentage=$((saved_bytes * 100 / total_size_before))
fi
# Convert bytes to readable format with automatic KB/MB selection
local size_before_display
local size_after_display
local saved_display
local unit_before="MB"
local unit_after="MB"
local unit_saved="MB"
# Check if sizes are less than 1MB to display in KB
if [ "$total_size_before" -lt 1048576 ]; then
size_before_display=$((total_size_before / 1024))
unit_before="KB"
else
size_before_display=$((total_size_before / 1024 / 1024))
fi
if [ "$total_size_after" -lt 1048576 ]; then
size_after_display=$((total_size_after / 1024))
unit_after="KB"
else
size_after_display=$((total_size_after / 1024 / 1024))
fi
if [ "$saved_bytes" -lt 1048576 ]; then
saved_display=$((saved_bytes / 1024))
unit_saved="KB"
else
saved_display=$((saved_bytes / 1024 / 1024))
fi
# Show final report
if [ "$use_gui" = true ]; then
# Show report with zenity
local report_message="✓ Compression completed!\n\n"
report_message+="Files processed: ${total_files}\n"
report_message+="Original size: ${size_before_display} ${unit_before}\n"
report_message+="Compressed size: ${size_after_display} ${unit_after}\n"
report_message+="Space saved: ${saved_display} ${unit_saved} (${saved_percentage}%)\n"
report_message+="Quality used: ${quality}%\n"
report_message+="Output directory: ${output_dir}/"
zenity --info --title="JPG Compression Complete" --text="$report_message"
else
# Show report in terminal
echo ""
echo -e "${GREEN}✓ Compression completed!${NC}"
echo ""
echo -e "${BLUE}Statistics:${NC}"
echo -e " Files processed: ${total_files}"
echo -e " Original size: ${size_before_display} ${unit_before}"
echo -e " Compressed size: ${size_after_display} ${unit_after}"
echo -e " Space saved: ${saved_display} ${unit_saved} (${saved_percentage}%)"
echo -e " Quality used: ${quality}%"
echo -e " Output directory: ${output_dir}/"
fi
}
# Show help if requested
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
echo -e "${GREEN}Script to compress JPG images${NC}"
echo ""
echo -e "${YELLOW}Usage:${NC}"
echo " $0 [quality] file1.jpg file2.jpg ..."
echo " $0 [quality] *.jpg"
echo " Or select files in Nemo file manager and run as script"
echo ""
echo -e "${YELLOW}Parameters:${NC}"
echo " quality Compression quality (1-100, default: 90)"
echo " files JPG/JPEG files to compress"
echo ""
echo -e "${YELLOW}Examples:${NC}"
echo " $0 photo1.jpg photo2.jpg # Compress specific files with quality 90"
echo " $0 85 *.jpg # Compress all JPG files with quality 85"
echo " $0 75 vacation/*.jpeg # Compress JPEG files in vacation folder"
echo ""
echo -e "${BLUE}Note: Compressed images will be saved in the 'min' subdirectory${NC}"
echo -e "${BLUE} When used as Nemo script, a dialog will ask for quality${NC}"
echo -e "${BLUE} Requires zenity for GUI dialog${NC}"
exit 0
fi
# Execute main
main "$@"