Skip to content

Instantly share code, notes, and snippets.

@erikvullings
Last active July 16, 2025 17:17
Show Gist options
  • Save erikvullings/20a6427adfc42df4660ae810236f185e to your computer and use it in GitHub Desktop.
Save erikvullings/20a6427adfc42df4660ae810236f185e to your computer and use it in GitHub Desktop.
Compressing comic books (CBR, CBZ files) via the command line (on macos)

Comic Book Compression Script

A Bash script that compresses CBR and CBZ comic book files by converting images to WebP format while maintaining optimal quality and proper aspect ratios for two-page spreads.

Features

  • Supports both CBR, CBZ and PDF formats - Extracts and repacks comic books
  • WebP conversion - Reduces file size by 80-90% while maintaining good quality
  • Smart two-page spread detection - Maintains proper height for wide images
  • Configurable quality and size - Adjust compression settings as needed
  • Multiple conversion methods - Uses cwebp when available, falls back to sips
  • Cross-platform - Works on macOS with Homebrew packages

Requirements

Required Tools

  • 7-Zip - For extracting CBZ files and creating new archives

    brew install sevenzip
  • unrar - For extracting CBR files

    brew install carlocab/personal/unrar
  • pdftoppm - For extracting PDF files

Optional Tools (Recommended)

  • WebP - For better WebP conversion quality

    brew install webp
  • bc - For precise aspect ratio calculations

    brew install bc
  • ImageMagick - For better image dimension detection

    brew install imagemagick

Installation

  1. Download the script:

    curl -O https://gist.githubusercontent.com/your-username/your-gist-id/raw/compress_comics.sh
  2. Make it executable:

    chmod +x compress_comics.sh
  3. Install required dependencies:

    brew install sevenzip carlocab/personal/unrar webp bc imagemagick

Usage

./compress_comics.sh <comic_file.cbr|.cbz> [quality]

Parameters

  • comic_file: CBR or CBZ file to compress (required)
  • quality: WebP quality from 1-100 (optional, default: 75)
    • Higher values = better quality, larger files
    • Recommended: 85-95 for high quality, 65-80 for balanced, 40-60 for small files

Examples

# Compress with default quality (75)
./compress_comics.sh "My Comic Book.cbr"

# Compress with high quality (90)
./compress_comics.sh "My Comic Book.cbr" 90

# Compress with smaller file size (60)
./compress_comics.sh "Another Comic.cbz" 60

The script will create a new file with _optimized_webp_q{quality}.cbz suffix in the same directory as the original file.

Configuration

You can modify these variables at the top of the script:

TARGET_HEIGHT=1800  # All images scaled to this height
WEBP_CONVERSION_METHOD="cwebp"  # "cwebp" or "sips"

Note: WebP quality is now controlled via the command line parameter rather than editing the script.

How It Works

  1. Extraction - Uses unrar for CBR files, 7zz for CBZ files
  2. Image Analysis - Detects image dimensions and aspect ratios
  3. Consistent Height Scaling - All images (covers, regular pages, spreads) are scaled to the same height for uniform appearance
  4. WebP Conversion - Converts all images to WebP format with specified quality
  5. Progress Tracking - Shows a real-time progress bar during processing
  6. Repacking - Creates a new CBZ file with optimized images
  7. Cleanup - Removes temporary files

Image Processing

The script processes all images consistently:

  • Height Uniformity: All images are scaled to the same height (TARGET_HEIGHT=1800) for consistent appearance
  • Aspect Ratio Preservation: Images maintain their original proportions during scaling
  • Two-Page Spread Detection: Wide images (aspect ratio > 1.3) are detected for logging purposes but processed the same way
  • Progress Display: Shows a visual progress bar with percentage completion

Output

  • Original CBR/CBZ: Kept unchanged
  • Optimized CBZ: Created with _optimized_webp_q{quality}.cbz suffix
  • Typical compression: 80-95% file size reduction depending on quality setting
  • Quality: Adjustable from 1-100 via command line parameter

Example Results

Original:  My Comic Book.cbr (800 MB)
Quality 75: My Comic Book_optimized_webp_q75.cbz (50 MB) - 94% reduction
Quality 90: My Comic Book_optimized_webp_q90.cbz (85 MB) - 89% reduction

Troubleshooting

Common Issues

  1. "unrar not found"

    brew install carlocab/personal/unrar
  2. "7zz not found"

    brew install sevenzip
  3. Poor WebP quality

    • Use higher quality parameter (e.g., ./compress_comics.sh file.cbr 90)
    • Install webp for better conversion: brew install webp
  4. Two-page spreads look wrong

    • Install bc for better calculations: brew install bc
    • Install imagemagick for better dimension detection: brew install imagemagick

Performance Tips

  • Process smaller comics first to test quality settings
  • Use SSD storage for faster temporary file operations
  • Start with quality 75, then adjust up (90+) or down (60-) based on results
  • Higher quality values significantly increase processing time and file size

License

This script is provided as-is for personal use. Feel free to modify and distribute.

Contributing

Improvements and bug fixes are welcome! Please test thoroughly before submitting changes.

#!/bin/bash
# Function to display usage information
usage() {
echo "Usage: $0 <comic_file.cbr|.cbz|.pdf> [quality]"
echo " comic_file: CBR, CBZ, or PDF file to compress"
echo " quality: WebP quality (1-100, default: 75)"
echo " Higher values = better quality, larger files"
echo " Recommended: 85-95 for high quality, 65-80 for balanced, 40-60 for small files"
echo ""
echo "This script extracts CBR files using 'unrar', CBZ files using '7zz', and PDF files using 'pdftoppm'."
echo "It then re-encodes images to WebP and repacks them into a new CBZ file using '7zz'."
echo "Requires 'unrar' (brew install carlocab/personal/unrar if mainline fails), 'sevenzip' (brew install sevenzip),"
echo "and 'poppler' (brew install poppler) for PDF support to be installed."
echo "For WebP conversion: 'webp' (cwebp) (brew install webp) is recommended, but sips is a fallback."
exit 1
}
# Check if a file path is provided
if [ -z "$1" ]; then
usage
fi
COMIC_FILE="$1"
WEBP_QUALITY="${2:-85}" # Use second argument or default to 85
# Validate quality parameter
if ! [[ "$WEBP_QUALITY" =~ ^[0-9]+$ ]] || [ "$WEBP_QUALITY" -lt 1 ] || [ "$WEBP_QUALITY" -gt 100 ]; then
echo "Error: Quality must be a number between 1 and 100"
usage
fi
FILENAME=$(basename "$COMIC_FILE")
DIRNAME=$(dirname "$COMIC_FILE")
BASENAME="${FILENAME%.*}" # File name without extension
EXTENSION="${FILENAME##*.}" # Extension (cbr or cbz)
TEMP_DIR="${DIRNAME}/${BASENAME}_temp_webp_comic_processing"
OUTPUT_FILE="${DIRNAME}/${BASENAME}_optimized_webp_q${WEBP_QUALITY}.cbz"
# --- Configuration for Image Optimization ---
MAX_DIMENSION=1200 # Set the maximum dimension (width or height) for the images
TARGET_HEIGHT=1800 # All images will be scaled to this height
# --- Choose WebP Conversion Method ---
# Set to 'cwebp' to use cwebp (recommended, requires installation)
# Set to 'sips' to use sips (fallback, built-in)
WEBP_CONVERSION_METHOD="cwebp"
echo "πŸš€ Starting comic book optimization for: ${FILENAME}"
echo "-----------------------------------------------------"
# 1. Check for necessary tools
echo "πŸ” Checking for required tools..."
if ! command -v 7zz &> /dev/null; then
echo "Error: '7zz' (from sevenzip package) is not installed. Please install it using 'brew install sevenzip'."
exit 1
fi
echo "βœ… '7zz' found."
if [ "$EXTENSION" == "cbr" ]; then
if ! command -v unrar &> /dev/null; then
echo "Error: 'unrar' is not installed. Please install it using 'brew install carlocab/personal/unrar'."
exit 1
fi
echo "βœ… 'unrar' found."
fi
if [ "$EXTENSION" == "pdf" ]; then
if ! command -v pdftoppm &> /dev/null; then
echo "Error: 'pdftoppm' is not installed. Please install it using 'brew install poppler'."
exit 1
fi
echo "βœ… 'pdftoppm' found."
fi
if [ "$WEBP_CONVERSION_METHOD" == "cwebp" ]; then
if ! command -v cwebp &> /dev/null; then
echo "Warning: 'cwebp' not found. Falling back to 'sips' for WebP conversion."
WEBP_CONVERSION_METHOD="sips"
else
echo "βœ… 'cwebp' found."
fi
fi
# Check for bc calculator (for aspect ratio calculations)
if ! command -v bc &> /dev/null; then
echo "Warning: 'bc' calculator not found. Two-page spread detection may be less accurate."
echo "Install with: brew install bc"
else
echo "βœ… 'bc' found."
fi
# 2. Create a temporary directory
echo "πŸ“‚ Creating temporary directory: ${TEMP_DIR}"
mkdir -p "$TEMP_DIR" || { echo "Error: Could not create temporary directory. Exiting."; exit 1; }
# 3. Extract the comic book based on extension
echo "πŸ“¦ Extracting comic book..."
case "$EXTENSION" in
cbz)
# Use 7zz for CBZ (ZIP)
echo " Using 7zz to extract CBZ file."
7zz x "$COMIC_FILE" -o"${TEMP_DIR}/" -y &> /dev/null || { echo "Error: Failed to extract CBZ file with 7zz. Exiting."; rm -rf "$TEMP_DIR"; exit 1; }
echo "βœ… CBZ extracted by 7zz."
;;
cbr)
# Use unrar for CBR (RAR), fallback to 7zz if it's actually a ZIP file
echo " Using unrar to extract CBR file."
if unrar x -inul "$COMIC_FILE" "$TEMP_DIR" 2>/dev/null; then
echo "βœ… CBR extracted by unrar."
else
echo " unrar failed, trying 7zz (CBR might be a ZIP file)..."
7zz x "$COMIC_FILE" -o"${TEMP_DIR}/" -y &> /dev/null || { echo "Error: Failed to extract CBR file with both unrar and 7zz. Exiting."; rm -rf "$TEMP_DIR"; exit 1; }
echo "βœ… CBR extracted by 7zz (was actually a ZIP file)."
fi
;;
pdf)
# Use pdftoppm for PDF with high DPI for better quality
echo " Using pdftoppm to extract PDF file at 300 DPI."
pdftoppm -png -r 300 "$COMIC_FILE" "${TEMP_DIR}/page" || { echo "Error: Failed to extract PDF file with pdftoppm. Exiting."; rm -rf "$TEMP_DIR"; exit 1; }
echo "βœ… PDF extracted by pdftoppm at 300 DPI."
;;
*)
echo "Error: Unsupported file type. Only .cbr, .cbz, and .pdf are supported."
rm -rf "$TEMP_DIR"
exit 1
;;
esac
# 4. Skip the target height calculation - we use a fixed height for all images
echo "πŸ“ Using fixed target height: $TARGET_HEIGHT"
# 5. Process and re-encode images to WebP
echo "πŸ–ΌοΈ Re-encoding images to WebP (target height ${TARGET_HEIGHT}, quality ${WEBP_QUALITY})..."
# Count total images first
TOTAL_IMAGES=0
for IMG in "$TEMP_DIR"/*.jpg "$TEMP_DIR"/*.jpeg "$TEMP_DIR"/*.png; do
[ -f "$IMG" ] && TOTAL_IMAGES=$((TOTAL_IMAGES + 1))
done
# Process images one by one with progress indicator
CURRENT_IMAGE=0
for IMG in "$TEMP_DIR"/*.jpg "$TEMP_DIR"/*.jpeg "$TEMP_DIR"/*.png; do
# Skip if no files match the pattern
[ ! -f "$IMG" ] && continue
CURRENT_IMAGE=$((CURRENT_IMAGE + 1))
ORIGINAL_IMG="$IMG"
BASENAME_IMG="${ORIGINAL_IMG%.*}"
NEW_WEBP_IMG="${BASENAME_IMG}.webp"
# Calculate and display progress
PROGRESS=$((CURRENT_IMAGE * 100 / TOTAL_IMAGES))
printf "\r Progress: [%-50s] %d%% (%d/%d)" "$(printf '%*s' $((PROGRESS / 2)) '' | tr ' ' '=')" "$PROGRESS" "$CURRENT_IMAGE" "$TOTAL_IMAGES"
# Get image dimensions to detect two-page spreads
if command -v identify &> /dev/null; then
# Use ImageMagick if available
DIMENSIONS=$(identify -format "%w %h" "$ORIGINAL_IMG" 2>/dev/null)
WIDTH=$(echo $DIMENSIONS | cut -d' ' -f1)
HEIGHT=$(echo $DIMENSIONS | cut -d' ' -f2)
else
# Fallback to sips
DIMENSIONS=$(sips -g pixelWidth -g pixelHeight "$ORIGINAL_IMG" 2>/dev/null | tail -2 | awk '{print $2}')
WIDTH=$(echo "$DIMENSIONS" | head -1)
HEIGHT=$(echo "$DIMENSIONS" | tail -1)
fi
# Check if this is a two-page spread (width > height * 1.3)
if [ -n "$WIDTH" ] && [ -n "$HEIGHT" ] && [ "$WIDTH" -gt 0 ] && [ "$HEIGHT" -gt 0 ]; then
ASPECT_RATIO=$(echo "scale=2; $WIDTH / $HEIGHT" | bc -l 2>/dev/null || echo "1.0")
IS_SPREAD=$(echo "$ASPECT_RATIO > 1.3" | bc -l 2>/dev/null || echo "0")
RESIZE_PARAM="0 $TARGET_HEIGHT" # All images resized to same height
else
RESIZE_PARAM="$MAX_DIMENSION 0" # Default fallback
fi
if [ "$WEBP_CONVERSION_METHOD" == "cwebp" ]; then
cwebp -q $WEBP_QUALITY -resize $RESIZE_PARAM "$ORIGINAL_IMG" -o "$NEW_WEBP_IMG" &> /dev/null || {
echo " Warning: Failed to convert $(basename "$ORIGINAL_IMG") to WebP with cwebp. Keeping original format."
sips -Z $MAX_DIMENSION "$ORIGINAL_IMG" &> /dev/null
continue
}
else # WEBP_CONVERSION_METHOD == "sips"
# For all images with sips, resize to target height
# sips doesn't support resize by height only, so we calculate width from aspect ratio
TARGET_WIDTH=$(echo "scale=0; $WIDTH * $TARGET_HEIGHT / $HEIGHT" | bc 2>/dev/null || echo "$MAX_DIMENSION")
sips -Z $TARGET_WIDTH "$ORIGINAL_IMG" --setProperty format webp "$NEW_WEBP_IMG" &> /dev/null || {
echo " Warning: Failed to convert $(basename "$ORIGINAL_IMG") to WebP with sips. Keeping original format."
sips -Z $MAX_DIMENSION "$ORIGINAL_IMG" &> /dev/null
continue
}
fi
# Remove the original image after successful conversion
rm "$ORIGINAL_IMG"
done
echo "" # New line after progress bar
echo "βœ… Images re-encoded to WebP."
# 5. Repack into a new CBZ file using 7zz (cannot use unrar for compression)
echo "πŸ“¦ Compressing the re-encoded WebP images into a new CBZ file with 7zz..."
7zz a -tzip -mx=9 "${OUTPUT_FILE}" "${TEMP_DIR}"/* &> /dev/null || { echo "Error: Failed to repack CBZ file with 7zz. Exiting."; rm -rf "$TEMP_DIR"; exit 1; }
echo "βœ… New CBZ file created: ${OUTPUT_FILE}"
# 6. Clean up temporary directory
echo "🧹 Cleaning up temporary directory: ${TEMP_DIR}"
rm -rf "$TEMP_DIR"
echo "βœ… Cleanup complete."
echo "πŸŽ‰ Optimization finished! Enjoy your smaller, WebP comic book."
echo "-----------------------------------------------------"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment