Skip to content

Instantly share code, notes, and snippets.

@javabean
Last active July 27, 2024 08:39
Show Gist options
  • Save javabean/9ef827ea82268c1a9868ec617de3a1d8 to your computer and use it in GitHub Desktop.
Save javabean/9ef827ea82268c1a9868ec617de3a1d8 to your computer and use it in GitHub Desktop.
#!/bin/sh
set -eu
# requires jpeg-recompress <https://github.com/danielgtaylor/jpeg-archive>
# same as jpeg-archive, but:
# * without 3rd-party dependencies
# * can re-compress a single file as well as a directory
# * replaces original with optimized version (keeping file timestamps)
# * no support for RAW images
# Copyright 2015-2024 Cédrik LIME
help()
{
echo 'JPEG-Archive-Replace - Compress an replace a single JPEG image, or a directory of JPEG images'
echo "Usage: $(basename $0) [file|dir] [options]"
echo 'Warning: [file|dir] *must* be first argument; expect the unexpected if you dare try to put options first!'
echo ''
echo 'Possible compression options:'
echo "$(jpeg-recompress --help | tail -n+6)"
}
# number of parallel re-compression processes to use
PARALLELISM=1
get_ncpu()
{
OS_NAME="$(uname)"
# Use hyperthreading?
HT="${1:-}"
CORES=""
if [ -z "$HT" ]; then
HT="no"
fi
if [ "$HT" = "yes" ]; then
if [ "$OS_NAME" = "Linux" ]; then
CORES=$(grep -c '^processor' /proc/cpuinfo)
elif [ "$OS_NAME" = "Darwin" ]; then
CORES=$(sysctl -n hw.logicalcpu)
elif [ "$OS_NAME" = "FreeBSD" ]; then
CORES=$(sysctl -n hw.ncpu)
elif [ "$OS_NAME" = "SunOS" ]; then
CORES=$(psrinfo | grep -c 'on-line')
elif [ -e /proc/cpuinfo ]; then
CORES=$(grep -c '^processor' /proc/cpuinfo)
fi
elif [ "$HT" = "no" ]; then
if [ "$OS_NAME" = "Linux" ]; then
if grep -q 'physical id' /proc/cpuinfo; then
PHYSICAL=$(grep 'physical id' /proc/cpuinfo | sort -u | wc -l)
# $PHYSICAL physical CPUs
if grep -q 'core id' /proc/cpuinfo; then
# Computing the total number of physical core(s):
# physical processor(s) * physical core(s) per CPU
# (assuming all installed CPUs have the same number of cores)
CORES="$(expr $PHYSICAL '*' $(grep 'core id' /proc/cpuinfo | sort -u | wc -l))"
# $CORES physical cores
else
# Single core processor(s)
CORES=$PHYSICAL
fi
else
# No 'physical id' section found in /proc/cpuinfo, typical for older or "exotic" (e.g. ARM) cpus
CORES=$(grep -c '^processor' /proc/cpuinfo)
fi
elif [ "$OS_NAME" = "Darwin" ]; then
CORES=$(sysctl -n hw.physicalcpu)
elif [ "$OS_NAME" = "FreeBSD" ]; then
CORES=$(sysctl -n hw.ncpu)
elif [ "$OS_NAME" = "SunOS" ]; then
CORES=$(psrinfo | grep -c 'on-line')
elif [ -e /proc/cpuinfo ]; then
CORES=$(grep 'cpu cores' /proc/cpuinfo | cut -d ":" -f 2 | uniq | sed -e 's/\ //g')
fi
fi
if [ -z "$CORES" -o "$CORES" -eq "0" ]; then
# fallback
CORES=1
fi
#echo $CORES
PARALLELISM=$CORES
}
if [ "${1:-}" = "" -o "${1:-}" = "--help" ]; then
help
exit 0
fi
if [ "$1" = "--version" ]; then
jpeg-recompress --version
exit 0
fi
if [ -f "$1" -o -d "$1" ]; then
# 1st param is a file or a directory: it is not a jpeg-recompress option
ORIGINAL_JPG="$1"
shift
else
help
exit 1
fi
if [ -d "${ORIGINAL_JPG}" ]; then
# directory: optimize JPEG images inside
get_ncpu
echo "Recompressing JPEG files in \"${ORIGINAL_JPG}\" (using ${PARALLELISM} threads)"
find "${ORIGINAL_JPG}" -type f \( -iname '*.jpg' -o -iname '*.jpeg' \) -print0 | xargs -0 --no-run-if-empty -P "${PARALLELISM}" -I "{}" -n 1 "$0" "{}" $@
echo 'Done!'
elif [ -s "${ORIGINAL_JPG}" ]; then
# single file
# We could simply jpeg-recompress "${ORIGINAL_JPG}" "${ORIGINAL_JPG}", but we would lose file meta-data
# TODO: place temp file in /dev/shm for speed
TMP_FILE=$(mktemp -t jpeg-recompress.XXXXX.jpg) || exit 1
trap "rm -f ${TMP_FILE}" EXIT
# FIXME this should be enough (and no need for /dev/shm anymore):
# touch -r "${ORIGINAL_JPG}" "${TMP_FILE}"
# jpeg-recompress ${@:--Q -c -q high --accurate -x 99} "${ORIGINAL_JPG}" "${ORIGINAL_JPG}"
# touch -r "${TMP_FILE}" "${ORIGINAL_JPG}"
# rm "${TMP_FILE}"
jpeg-recompress ${@:--Q -c -q high --accurate -x 99} "${ORIGINAL_JPG}" "${TMP_FILE}"
if [ -s "${TMP_FILE}" ]; then
# move optimized image in place of original
touch -r "${ORIGINAL_JPG}" "${TMP_FILE}"
if [ "$(uname)" = "Linux" ]; then
# Linux-only
chmod --reference="${ORIGINAL_JPG}" "${TMP_FILE}"
chown --reference="${ORIGINAL_JPG}" "${TMP_FILE}"
elif [ "$(uname)" = "Darwin" -o "$(uname)" = "FreeBSD" ]; then
# BSD-only (Linux stat has slightly different syntax):
chmod $( stat -f '%Lp' "${ORIGINAL_JPG}" ) "${TMP_FILE}"
chown $( stat -f '%u:%g' "${ORIGINAL_JPG}" ) "${TMP_FILE}"
fi
mv "${TMP_FILE}" "${ORIGINAL_JPG}"
else
# don't leave empty temporary files lying around
rm "${TMP_FILE}"
fi
else
echo "Can not process ${ORIGINAL_JPG}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment