add truck post
This commit is contained in:
+173
-25
@@ -116,6 +116,52 @@ human_size() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Manifest: track which images have been optimized (skip re-processing)
|
||||
# ---------------------------------------------------------------------------
|
||||
MANIFEST_FILE=".image-manifest"
|
||||
|
||||
manifest_hash() {
|
||||
sha256sum "$1" | cut -d' ' -f1
|
||||
}
|
||||
|
||||
# Look up a file's stored hash; prints it or empty string if not found
|
||||
manifest_lookup() {
|
||||
local file="$1"
|
||||
if [[ -f "$MANIFEST_FILE" ]]; then
|
||||
grep -F " $file" "$MANIFEST_FILE" 2>/dev/null | head -1 | awk '{print $1}' || true
|
||||
fi
|
||||
}
|
||||
|
||||
# Update or add a file's hash in the manifest
|
||||
manifest_update() {
|
||||
local file="$1"
|
||||
local hash
|
||||
hash=$(manifest_hash "$file")
|
||||
|
||||
if [[ ! -f "$MANIFEST_FILE" ]]; then
|
||||
echo "# fosscat.com image optimization manifest — do not edit manually" > "$MANIFEST_FILE"
|
||||
fi
|
||||
|
||||
# Remove existing entry for this file (if any)
|
||||
if grep -qF " $file" "$MANIFEST_FILE" 2>/dev/null; then
|
||||
grep -vF " $file" "$MANIFEST_FILE" > "${MANIFEST_FILE}.tmp"
|
||||
mv "${MANIFEST_FILE}.tmp" "$MANIFEST_FILE"
|
||||
fi
|
||||
|
||||
# Append new entry
|
||||
echo "$hash $file" >> "$MANIFEST_FILE"
|
||||
}
|
||||
|
||||
# Remove a file's entry from the manifest
|
||||
manifest_remove() {
|
||||
local file="$1"
|
||||
if [[ -f "$MANIFEST_FILE" ]] && grep -qF " $file" "$MANIFEST_FILE" 2>/dev/null; then
|
||||
grep -vF " $file" "$MANIFEST_FILE" > "${MANIFEST_FILE}.tmp"
|
||||
mv "${MANIFEST_FILE}.tmp" "$MANIFEST_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Utility: confirm prompt (respects --yes and --dry-run)
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -394,6 +440,9 @@ phase_strip_metadata() {
|
||||
exiftool -all= -overwrite_original "$img" 2>/dev/null
|
||||
stripped=$((stripped + 1))
|
||||
echo -e " ${GREEN}$fname${NC} — metadata stripped"
|
||||
|
||||
# Update manifest since file content changed
|
||||
manifest_update "$img"
|
||||
done
|
||||
|
||||
echo ""
|
||||
@@ -423,6 +472,7 @@ phase_convert() {
|
||||
echo -e " ${DIM}(dry-run) Would delete: $(basename "$img") ($(human_size "$fsize"))${NC}"
|
||||
else
|
||||
rm -f "$img"
|
||||
manifest_remove "$img"
|
||||
echo -e " ${RED}Deleted:${NC} $(basename "$img") ($(human_size "$fsize"))"
|
||||
fi
|
||||
done
|
||||
@@ -430,12 +480,14 @@ phase_convert() {
|
||||
fi
|
||||
|
||||
echo -e "${BOLD}Converting images to WebP (quality $WEBP_QUALITY, max ${MAX_WIDTH}x${MAX_HEIGHT})${NC}"
|
||||
printf " %-40s %-12s %-12s %s\n" "FILENAME" "BEFORE" "AFTER" "SAVINGS"
|
||||
printf " %-40s %-12s %-12s %s\n" "--------" "------" "-----" "-------"
|
||||
printf " %-40s %-12s %-12s %s\n" "FILENAME" "BEFORE" "AFTER" "STATUS"
|
||||
printf " %-40s %-12s %-12s %s\n" "--------" "------" "-----" "------"
|
||||
|
||||
local total_before=0
|
||||
local total_after=0
|
||||
local converted=0
|
||||
local skipped=0
|
||||
local resized_only=0
|
||||
|
||||
for img in "${image_files[@]}"; do
|
||||
[[ -z "$img" ]] && continue
|
||||
@@ -446,6 +498,7 @@ phase_convert() {
|
||||
fname=$(basename "$img")
|
||||
local ext="${fname##*.}"
|
||||
local base="${fname%.*}"
|
||||
local ext_lower
|
||||
ext_lower=$(echo "$ext" | tr '[:upper:]' '[:lower:]')
|
||||
local webp_path="$IMAGES_DIR/${base}.webp"
|
||||
|
||||
@@ -453,14 +506,33 @@ phase_convert() {
|
||||
before_size=$(stat -c%s "$img" 2>/dev/null || stat -f%z "$img" 2>/dev/null)
|
||||
total_before=$((total_before + before_size))
|
||||
|
||||
# --- Manifest-based skip logic ---
|
||||
local current_hash stored_hash
|
||||
current_hash=$(manifest_hash "$img")
|
||||
stored_hash=$(manifest_lookup "$img")
|
||||
|
||||
if [[ "$ext_lower" == "webp" ]] && [[ "$current_hash" == "$stored_hash" ]] && [[ -n "$stored_hash" ]]; then
|
||||
# Already optimized, hash unchanged — skip entirely
|
||||
total_after=$((total_after + before_size))
|
||||
skipped=$((skipped + 1))
|
||||
printf " %-40s %-12s %-12s ${DIM}%s${NC}\n" \
|
||||
"$fname" "$(human_size "$before_size")" "—" "already optimized"
|
||||
continue
|
||||
fi
|
||||
|
||||
if $DRY_RUN; then
|
||||
echo -e " ${DIM}(dry-run) Would convert: $fname -> ${base}.webp${NC}"
|
||||
# Estimate: assume 80% reduction for JPEGs, 70% for PNGs, 10% for existing WebP
|
||||
if [[ "$ext_lower" == "webp" ]] && [[ -n "$stored_hash" ]] && [[ "$current_hash" != "$stored_hash" ]]; then
|
||||
echo -e " ${DIM}(dry-run) $fname — edited, would check dimensions only${NC}"
|
||||
elif [[ "$ext_lower" == "webp" ]] && [[ -z "$stored_hash" ]]; then
|
||||
echo -e " ${DIM}(dry-run) $fname — new webp, would record in manifest${NC}"
|
||||
else
|
||||
echo -e " ${DIM}(dry-run) Would convert: $fname -> ${base}.webp${NC}"
|
||||
fi
|
||||
local est_after=$before_size
|
||||
case "$ext_lower" in
|
||||
jpg|jpeg) est_after=$((before_size / 5)) ;;
|
||||
png) est_after=$((before_size / 3)) ;;
|
||||
webp) est_after=$((before_size * 9 / 10)) ;;
|
||||
webp) est_after=$before_size ;;
|
||||
esac
|
||||
total_after=$((total_after + est_after))
|
||||
converted=$((converted + 1))
|
||||
@@ -476,7 +548,47 @@ phase_convert() {
|
||||
needs_resize=true
|
||||
fi
|
||||
|
||||
# Determine the input for cwebp
|
||||
# --- Already WebP (edited or new): skip lossy re-compression ---
|
||||
if [[ "$ext_lower" == "webp" ]]; then
|
||||
if $needs_resize; then
|
||||
# Resize oversized WebP via ImageMagick (lossless resize, no re-encoding)
|
||||
local tmp_resized
|
||||
tmp_resized=$(mktemp /tmp/optimg_XXXXXX.webp)
|
||||
magick "$img" -resize "${MAX_WIDTH}x${MAX_HEIGHT}>" "$tmp_resized"
|
||||
local new_dims
|
||||
new_dims=$(magick identify -format '%wx%h' "$tmp_resized")
|
||||
mv "$tmp_resized" "$img"
|
||||
info " Resized $fname: ${cur_width}x${cur_height} -> $new_dims"
|
||||
resized_only=$((resized_only + 1))
|
||||
fi
|
||||
|
||||
# Record in manifest (whether resized or just newly tracked)
|
||||
manifest_update "$img"
|
||||
|
||||
local after_size
|
||||
after_size=$(stat -c%s "$img" 2>/dev/null || stat -f%z "$img" 2>/dev/null)
|
||||
total_after=$((total_after + after_size))
|
||||
|
||||
if $needs_resize; then
|
||||
local savings=0
|
||||
if (( before_size > 0 )); then
|
||||
savings=$(( (before_size - after_size) * 100 / before_size ))
|
||||
fi
|
||||
printf " %-40s %-12s %-12s ${CYAN}%s${NC}\n" \
|
||||
"$fname" "$(human_size "$before_size")" "$(human_size "$after_size")" "resized (${savings}%)"
|
||||
elif [[ -z "$stored_hash" ]]; then
|
||||
printf " %-40s %-12s %-12s ${GREEN}%s${NC}\n" \
|
||||
"$fname" "$(human_size "$before_size")" "—" "recorded"
|
||||
else
|
||||
printf " %-40s %-12s %-12s ${CYAN}%s${NC}\n" \
|
||||
"$fname" "$(human_size "$before_size")" "—" "updated (edited)"
|
||||
fi
|
||||
|
||||
converted=$((converted + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# --- Non-WebP image: full convert + compress pipeline ---
|
||||
local cwebp_input="$img"
|
||||
local tmp_resized=""
|
||||
|
||||
@@ -488,24 +600,17 @@ phase_convert() {
|
||||
cwebp_input="$tmp_resized"
|
||||
fi
|
||||
|
||||
# Convert to WebP via cwebp (handles JPEG/PNG/WebP input natively)
|
||||
if [[ "$ext_lower" == "webp" ]] && [[ "$img" == "$webp_path" ]]; then
|
||||
# Same input and output: use temp output
|
||||
local tmp_webp
|
||||
tmp_webp=$(mktemp /tmp/optimg_XXXXXX.webp)
|
||||
cwebp -q "$WEBP_QUALITY" "$cwebp_input" -o "$tmp_webp" 2>/dev/null
|
||||
mv "$tmp_webp" "$webp_path"
|
||||
else
|
||||
cwebp -q "$WEBP_QUALITY" "$cwebp_input" -o "$webp_path" 2>/dev/null
|
||||
fi
|
||||
# Convert to WebP via cwebp
|
||||
cwebp -q "$WEBP_QUALITY" "$cwebp_input" -o "$webp_path" 2>/dev/null
|
||||
|
||||
# Cleanup temp file if we resized
|
||||
[[ -n "$tmp_resized" ]] && rm -f "$tmp_resized"
|
||||
|
||||
# Step 3: Delete original if it's not already .webp
|
||||
if [[ "$ext_lower" != "webp" ]]; then
|
||||
rm -f "$img"
|
||||
fi
|
||||
# Delete original non-WebP source
|
||||
rm -f "$img"
|
||||
|
||||
# Record the new WebP in the manifest
|
||||
manifest_update "$webp_path"
|
||||
|
||||
local after_size
|
||||
after_size=$(stat -c%s "$webp_path" 2>/dev/null || stat -f%z "$webp_path" 2>/dev/null)
|
||||
@@ -529,16 +634,27 @@ phase_convert() {
|
||||
|
||||
echo ""
|
||||
local total_savings=0
|
||||
if (( total_before > 0 )); then
|
||||
if (( total_before > 0 )) && (( total_after > 0 )); then
|
||||
total_savings=$(( (total_before - total_after) * 100 / total_before ))
|
||||
fi
|
||||
info "Converted $converted image(s)"
|
||||
info "Total: $(human_size $total_before) -> $(human_size $total_after) (${total_savings}% reduction)"
|
||||
if (( skipped > 0 )); then
|
||||
info "Skipped $skipped already-optimized image(s)"
|
||||
fi
|
||||
if (( resized_only > 0 )); then
|
||||
info "Resized $resized_only edited image(s) (no re-compression)"
|
||||
fi
|
||||
info "Processed $converted image(s)"
|
||||
if (( total_before > total_after )); then
|
||||
info "Total: $(human_size $total_before) -> $(human_size $total_after) (${total_savings}% reduction)"
|
||||
else
|
||||
info "Total size: $(human_size $total_after)"
|
||||
fi
|
||||
|
||||
# Save totals for summary
|
||||
echo "$total_before" > /tmp/optimg_total_before.txt
|
||||
echo "$total_after" > /tmp/optimg_total_after.txt
|
||||
echo "$converted" > /tmp/optimg_converted.txt
|
||||
echo "$skipped" > /tmp/optimg_skipped.txt
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -656,10 +772,11 @@ phase_summary() {
|
||||
echo ""
|
||||
fi
|
||||
|
||||
local total_before total_after converted
|
||||
local total_before total_after converted skipped
|
||||
total_before=$(cat /tmp/optimg_total_before.txt 2>/dev/null || cat /tmp/optimg_total_size.txt 2>/dev/null || echo 0)
|
||||
total_after=$(cat /tmp/optimg_total_after.txt 2>/dev/null || echo 0)
|
||||
converted=$(cat /tmp/optimg_converted.txt 2>/dev/null || echo 0)
|
||||
skipped=$(cat /tmp/optimg_skipped.txt 2>/dev/null || echo 0)
|
||||
|
||||
local savings=0
|
||||
if (( total_before > 0 )) && (( total_after > 0 )); then
|
||||
@@ -667,10 +784,41 @@ phase_summary() {
|
||||
fi
|
||||
|
||||
echo -e " Images processed: ${BOLD}$converted${NC}"
|
||||
if (( total_after > 0 )); then
|
||||
if (( skipped > 0 )); then
|
||||
echo -e " Images skipped: ${BOLD}$skipped${NC} (already optimized)"
|
||||
fi
|
||||
if (( total_after > 0 )) && (( total_before > total_after )); then
|
||||
echo -e " Size before: ${BOLD}$(human_size "$total_before")${NC}"
|
||||
echo -e " Size after: ${BOLD}$(human_size "$total_after")${NC}"
|
||||
echo -e " Total reduction: ${BOLD}${GREEN}${savings}%${NC}"
|
||||
elif (( total_after > 0 )); then
|
||||
echo -e " Total size: ${BOLD}$(human_size "$total_after")${NC}"
|
||||
fi
|
||||
|
||||
# Clean up stale manifest entries (files that no longer exist)
|
||||
if [[ -f "$MANIFEST_FILE" ]] && ! $DRY_RUN; then
|
||||
local stale=0
|
||||
local manifest_tmp
|
||||
manifest_tmp=$(mktemp /tmp/optimg_manifest_XXXXXX.txt)
|
||||
while IFS= read -r line; do
|
||||
# Keep comment lines
|
||||
if [[ "$line" == \#* ]]; then
|
||||
echo "$line" >> "$manifest_tmp"
|
||||
continue
|
||||
fi
|
||||
# Extract filename (second field, after hash + two spaces)
|
||||
local entry_file
|
||||
entry_file=$(echo "$line" | awk '{print $2}')
|
||||
if [[ -z "$entry_file" ]] || [[ -f "$entry_file" ]]; then
|
||||
echo "$line" >> "$manifest_tmp"
|
||||
else
|
||||
stale=$((stale + 1))
|
||||
fi
|
||||
done < "$MANIFEST_FILE"
|
||||
mv "$manifest_tmp" "$MANIFEST_FILE"
|
||||
if (( stale > 0 )); then
|
||||
info "Removed $stale stale manifest entry/entries"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user