Last active
July 27, 2024 08:39
-
-
Save javabean/9ef827ea82268c1a9868ec617de3a1d8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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