-
-
Notifications
You must be signed in to change notification settings - Fork 101
Expand file tree
/
Copy pathoptimize-image
More file actions
executable file
·178 lines (151 loc) · 4.94 KB
/
optimize-image
File metadata and controls
executable file
·178 lines (151 loc) · 4.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/bin/bash
#
# Convert images to modern formats (WebP, AVIF) and keep the smallest result.
# > Automatically optimizes for best compression while maintaining quality.
#
# - Optimize all images in current folder:
#
# `optimize`
#
# - Optimize a specific image:
#
# `optimize {{file.jpg}}`
set -euo pipefail
# Utils
RED="$(tput setaf 1)"
GREEN="$(tput setaf 2)"
CYAN="$(tput setaf 6)"
BOLD="$(tput bold)"
YELLOW="$(tput setaf 3)"
UNDERLINE="$(tput sgr 0 1)"
NOCOLOR="$(tput sgr0)"
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
bin="$root/node_modules/.bin"
function error() {
echo -e "$UNDERLINE$RED$1$NOCOLOR\n"
}
# Check required tools
if ! command -v cwebp &> /dev/null; then
error "Webp is not installed: brew install webp"
exit 1
fi
if [[ ! -x "$bin/avif" ]]; then
error "avif is not installed: npm install avif"
exit 1
fi
# Pretty print bytes
pretty_bytes() {
local bytes=$1
if (( bytes >= 1073741824 )); then
# GB
local gb=$((bytes * 10 / 1073741824))
printf "%d.%dGB" $((gb / 10)) $((gb % 10))
elif (( bytes >= 1048576 )); then
# MB
local mb=$((bytes * 10 / 1048576))
printf "%d.%dMB" $((mb / 10)) $((mb % 10))
elif (( bytes >= 1024 )); then
# KB
local kb=$((bytes * 10 / 1024))
printf "%d.%dKB" $((kb / 10)) $((kb % 10))
else
printf "%dB" "$bytes"
fi
}
# Print conversion result
print_result() {
local filename="$1"
local original_size=$2
local optimized_size=$3
local new_format="$4"
local difference=$((original_size - optimized_size))
local ratio=$((10000 * difference / original_size))
printf "${BOLD}%s${NOCOLOR}: %s → ${CYAN}%s${NOCOLOR} ${GREEN}(%d.%02d%% saved, %s smaller)${NOCOLOR}\n" \
"$filename" \
"$(pretty_bytes $original_size)" \
"$(pretty_bytes $optimized_size)" \
$((ratio / 100)) \
$((ratio % 100)) \
"$(pretty_bytes $difference)"
}
optimize_image() {
local input="$1"
local basename="${input%.*}"
local ext="${input##*.}"
local directory="$(dirname "$input")"
# Skip if not an image or already optimized
if [[ "$ext" =~ ^(avif|webp|AVIF|WEBP)$ ]]; then
echo "${YELLOW}$(basename "$input"): Already optimized, skipping${NOCOLOR}"
return
fi
# Skip if not a supported source format
if [[ ! "$ext" =~ ^(jpg|jpeg|png|gif|bmp|tiff|JPG|JPEG|PNG|GIF|BMP|TIFF)$ ]]; then
return
fi
# Check if optimized versions already exist
if [[ -f "${basename}.avif" ]] || [[ -f "${basename}.webp" ]]; then
echo "${YELLOW}$(basename "$input"): Already has optimized version, skipping${NOCOLOR}"
return
fi
local tempdir=$(mktemp -d)
trap "rm -rf '$tempdir'" RETURN
# Convert to different formats
local webp="$tempdir/output.webp"
local avif="$tempdir/output.avif"
# WebP conversion (quality 75, max compression)
cwebp -q 75 -m 6 "$input" -o "$webp" 2>/dev/null
# AVIF conversion (quality 50, effort 8)
$bin/avif --quality 50 --effort 8 --input="$input" --output="$tempdir" 2>/dev/null
# avif CLI outputs with same basename, different extension
mv "$tempdir/$(basename "$basename").avif" "$avif" 2>/dev/null || true
# Debug
# echo " WebP: $(pretty_bytes $(stat -f%z "$webp" 2>/dev/null || stat -c%s "$webp" 2>/dev/null || echo 0))"
# echo " AVIF: $(pretty_bytes $(stat -f%z "$avif" 2>/dev/null || stat -c%s "$avif" 2>/dev/null || echo 0))"
# Find smallest file
local candidates=("$input")
[[ -f "$webp" ]] && candidates+=("$webp")
[[ -f "$avif" ]] && candidates+=("$avif")
local smallest="$input"
local smallest_size=$(stat -f%z "$input" 2>/dev/null || stat -c%s "$input")
for file in "${candidates[@]}"; do
if [[ -f "$file" ]]; then
local size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file")
if (( size < smallest_size )); then
smallest="$file"
smallest_size="$size"
fi
fi
done
# Only save converted files if they're smaller than original
if [[ "$smallest" == "$input" ]]; then
echo "${YELLOW}$(basename "$input"): Original was smallest ($(pretty_bytes $smallest_size)), no conversion needed${NOCOLOR}"
else
local original_size=$(stat -f%z "$input" 2>/dev/null || stat -c%s "$input")
local new_ext="${smallest##*.}"
local output="$directory/$(basename "$basename").${new_ext}"
# Copy converted file, keep original untouched
cp "$smallest" "$output"
print_result "$(basename "$input")" "$original_size" "$smallest_size" "$new_ext"
fi
}
if [[ $# -eq 0 ]]; then
# Optimize all images in current directory
echo "Optimizing all images in current directory..."
shopt -s nullglob
for img in *.{jpg,jpeg,png,gif,bmp,tiff,JPG,JPEG,PNG,GIF,BMP,TIFF}; do
[[ -f "$img" ]] && optimize_image "$img"
done
elif [[ $# -eq 1 ]]; then
# Optimize specific file
if [[ ! -f "$1" ]]; then
error "File '$1' not found"
exit 1
fi
optimize_image "$1"
else
echo "Usage: $0 [file]"
echo " No arguments: optimize all images in current directory"
echo " One argument: optimize specific image file"
exit 1
fi
echo "🐠 Done!"