#!/usr/bin/env bash # exit if a command fails set -o errexit # exit if required variables are not set set -o nounset # number of threads to use if [[ ${DEBUG} -eq 1 ]]; then parallel_thread_count=1 export oxipng_thread_count=1 else parallel_thread_count=4 export oxipng_thread_count=4 fi # make sure the correct number of arguments are passed; if not, output syntax and exit if [ "$#" -ne 1 ]; then echo -e "\nUsage: ${0} \n" exit 1 fi # set base directory (passed from invoking command) base_dir="${1}" # store current directory current_dir="$(pwd)" export current_dir if [[ ${DEBUG} -eq 1 ]]; then echo "DEBUG: Current directory is ${current_dir}" fi # create matching filesystem hierarchy in tempdir temp_dir="${base_dir}$(readlink -f .)" export temp_dir mkdir -p "${temp_dir}" if [[ ${DEBUG} -eq 1 ]]; then echo "DEBUG: Created temporary directory ${temp_dir} for ${current_dir}" fi # create temporary file to store pending commands commands="${temp_dir}/commands.txt" if [[ ${DEBUG} -eq 1 ]]; then echo "DEBUG: Storing commands for ${current_dir} in ${commands}" fi # attempt to optimize png, and embed checksum if successful function optimize_png() { local file_name file_name="${1}" local stored_checksum stored_checksum="$(exiftool -s3 -Comment "${file_name}")" local current_checksum current_checksum="$(identify -verbose "${file_name}" | grep signature | awk '{print $2}')" local new_file new_file="${temp_dir}/${file_name}" local old_file old_file="${current_dir}/${file_name}" if [ "${current_checksum}" == "${stored_checksum}" ]; then if [[ ${DEBUG} -eq 1 ]]; then echo "DEBUG: Stored and calculated checksums match for ${file_name}" fi return fi if oxipng --opt 2 --quiet --alpha --keep="tEXt,display" --zopfli --out "${new_file}" --threads "${oxipng_thread_count}" "${old_file}"; then # calculate size improvements old_size=$(du -b "${old_file}" | awk '{print $1}') old_size_human=$(du -h "${old_file}" | awk '{print $1}') new_size=$(du -b "${new_file}" | awk '{print $1}') new_size_human=$(du -h "${new_file}" | awk '{print $1}') new_percentage=$(echo "scale=2; ${new_size}/${old_size}*100" | bc) rounded_percentage=$(printf "%.0f" "${new_percentage}") # calculate new image data checksum new_checksum=$(identify -verbose "${new_file}" | grep signature | awk '{print $2}') # update metadata exiftool -Comment="${new_checksum}" "${new_file}" >/dev/null 2>&1 # output stats echo "Reduced file size of ${old_file} from ${old_size_human} to ${new_size_human} (${rounded_percentage}%)" # discard changes if in debug mode if [[ ${DEBUG} -eq 1 ]]; then echo "DEBUG: Not modifying ${file_name}." sleep 1 return fi # replace with updated file mv "${new_file}" "${old_file}" fi } export -f optimize_png # generate list of commands to run for input_file in *.png; do if [ -f "${input_file}" ]; then echo "optimize_png \"${input_file}\"" >> "${commands}" fi done # run commands in parallel if [ -s "${commands}" ]; then parallel --line-buffer --no-notice --jobs "${parallel_thread_count}" < "${commands}" fi