2024-09-03 15:40:16 +03:00
|
|
|
|
#!/bin/bash
|
2024-09-18 20:17:38 +03:00
|
|
|
|
|
|
|
|
|
# Настройки
|
|
|
|
|
IMAGE_DIR="./images"
|
|
|
|
|
COMP_DIR="$IMAGE_DIR/comp"
|
|
|
|
|
WEBP_DIR="$IMAGE_DIR/webp"
|
2024-09-18 20:41:18 +03:00
|
|
|
|
# Автоматическое определение количества ядер процессора
|
|
|
|
|
THREADS=$(getconf _NPROCESSORS_ONLN)
|
|
|
|
|
echo "Используется $THREADS потоков для обработки."
|
2024-09-18 20:17:38 +03:00
|
|
|
|
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Файлы логирования
|
|
|
|
|
LOG_FILE="./compression.log"
|
|
|
|
|
ERROR_LOG_FILE="./error.log"
|
|
|
|
|
|
|
|
|
|
# Инициализируем файлы логов (удалены строки, чтобы не затирать логи)
|
|
|
|
|
# : > "$LOG_FILE"
|
|
|
|
|
# : > "$ERROR_LOG_FILE"
|
|
|
|
|
|
2024-09-18 20:17:38 +03:00
|
|
|
|
# Экспортируем необходимые переменные и функции для использования в subshell
|
2024-09-18 20:31:03 +03:00
|
|
|
|
export IMAGE_DIR COMP_DIR WEBP_DIR LOG_FILE ERROR_LOG_FILE
|
|
|
|
|
|
|
|
|
|
# Функция для определения размера файла
|
|
|
|
|
get_file_size() {
|
|
|
|
|
local file="$1"
|
|
|
|
|
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
|
|
|
stat -c%s "$file"
|
|
|
|
|
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
|
|
|
|
stat -f%z "$file"
|
|
|
|
|
else
|
|
|
|
|
# Попытка использовать GNU stat, если установлен
|
|
|
|
|
if command -v gstat &> /dev/null; then
|
|
|
|
|
gstat -c%s "$file"
|
|
|
|
|
else
|
|
|
|
|
# В качестве альтернативы используем wc -c
|
|
|
|
|
wc -c < "$file" | tr -d ' '
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export -f get_file_size
|
|
|
|
|
|
|
|
|
|
# Функция для логирования успеха
|
|
|
|
|
log_success() {
|
|
|
|
|
local message="$1"
|
|
|
|
|
echo "$message"
|
|
|
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S') $message" >> "$LOG_FILE"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Функция для логирования ошибок
|
|
|
|
|
log_error() {
|
|
|
|
|
local message="$1"
|
|
|
|
|
echo "$message" >&2
|
|
|
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S') $message" >> "$ERROR_LOG_FILE"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Экспортируем функции логирования
|
|
|
|
|
export -f log_success
|
|
|
|
|
export -f log_error
|
2024-09-18 20:17:38 +03:00
|
|
|
|
|
|
|
|
|
# Функция для обработки PNG файлов
|
|
|
|
|
process_png() {
|
|
|
|
|
local input_file="$1"
|
|
|
|
|
local relative_path="${input_file#$IMAGE_DIR/}"
|
|
|
|
|
local output_file="$COMP_DIR/$relative_path"
|
2024-09-18 20:31:03 +03:00
|
|
|
|
local output_dir
|
|
|
|
|
output_dir="$(dirname "$output_file")"
|
2024-09-18 20:17:38 +03:00
|
|
|
|
|
|
|
|
|
mkdir -p "$output_dir"
|
|
|
|
|
|
|
|
|
|
# Проверка хеша файла
|
|
|
|
|
local hash_file="${output_file}.md5"
|
|
|
|
|
local current_hash
|
|
|
|
|
current_hash="$(md5sum "$input_file" | awk '{print $1}')"
|
|
|
|
|
|
|
|
|
|
if [ -f "$hash_file" ]; then
|
|
|
|
|
local previous_hash
|
|
|
|
|
previous_hash="$(cat "$hash_file")"
|
|
|
|
|
if [ "$current_hash" == "$previous_hash" ]; then
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Файл не изменился, ничего не делаем
|
2024-09-18 20:17:38 +03:00
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
cp "$input_file" "$output_file"
|
|
|
|
|
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Размер до сжатия
|
|
|
|
|
local original_size
|
|
|
|
|
original_size=$(get_file_size "$output_file")
|
|
|
|
|
|
2024-09-18 21:10:47 +03:00
|
|
|
|
# Используем pngquant
|
|
|
|
|
if ! pngquant --quality=90-100 --speed 1 --output "$output_file" --force "$input_file"; then
|
|
|
|
|
log_error "Ошибка при сжатии $input_file с помощью pngquant"
|
2024-09-18 20:17:38 +03:00
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
2024-09-18 21:10:47 +03:00
|
|
|
|
# Дополнительная оптимизация с помощью zopflipng
|
|
|
|
|
if ! zopflipng -y "$output_file" "$output_file"; then
|
|
|
|
|
log_error "Ошибка при оптимизации $output_file с помощью zopflipng"
|
2024-09-18 20:17:38 +03:00
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Размер после сжатия
|
|
|
|
|
local new_size
|
|
|
|
|
new_size=$(get_file_size "$output_file")
|
|
|
|
|
|
|
|
|
|
# Проверка, что original_size не равен нулю
|
|
|
|
|
if [ "$original_size" -eq 0 ]; then
|
|
|
|
|
log_error "Ошибка: размер оригинального файла равен 0 для $output_file"
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Процент сжатия
|
|
|
|
|
local reduction
|
|
|
|
|
reduction=$(awk "BEGIN {printf \"%.2f\", (($original_size - $new_size) / $original_size) * 100}")
|
|
|
|
|
|
|
|
|
|
log_success "Сжат PNG файл: $input_file на $reduction% ($original_size байт -> $new_size байт)"
|
|
|
|
|
|
2024-09-18 20:17:38 +03:00
|
|
|
|
echo "$current_hash" > "$hash_file"
|
|
|
|
|
}
|
2024-09-18 20:31:03 +03:00
|
|
|
|
export -f process_png
|
2024-09-18 20:17:38 +03:00
|
|
|
|
|
|
|
|
|
# Функция для обработки JPEG файлов
|
|
|
|
|
process_jpeg() {
|
|
|
|
|
local input_file="$1"
|
|
|
|
|
local relative_path="${input_file#$IMAGE_DIR/}"
|
|
|
|
|
local output_file="$COMP_DIR/$relative_path"
|
2024-09-18 20:31:03 +03:00
|
|
|
|
local output_dir
|
|
|
|
|
output_dir="$(dirname "$output_file")"
|
2024-09-18 20:17:38 +03:00
|
|
|
|
|
|
|
|
|
mkdir -p "$output_dir"
|
|
|
|
|
|
|
|
|
|
# Проверка хеша файла
|
|
|
|
|
local hash_file="${output_file}.md5"
|
|
|
|
|
local current_hash
|
|
|
|
|
current_hash="$(md5sum "$input_file" | awk '{print $1}')"
|
|
|
|
|
|
|
|
|
|
if [ -f "$hash_file" ]; then
|
|
|
|
|
local previous_hash
|
|
|
|
|
previous_hash="$(cat "$hash_file")"
|
|
|
|
|
if [ "$current_hash" == "$previous_hash" ]; then
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Файл не изменился, ничего не делаем
|
2024-09-18 20:17:38 +03:00
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
cp "$input_file" "$output_file"
|
|
|
|
|
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Размер до сжатия
|
|
|
|
|
local original_size
|
|
|
|
|
original_size=$(get_file_size "$output_file")
|
|
|
|
|
|
2024-09-18 21:10:47 +03:00
|
|
|
|
# Используем mozjpeg
|
|
|
|
|
if ! mozjpeg -quality 95 -progressive -optimize -outfile "$output_file" "$input_file"; then
|
|
|
|
|
log_error "Ошибка при сжатии $input_file с помощью mozjpeg"
|
2024-09-18 20:31:03 +03:00
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Размер после сжатия
|
|
|
|
|
local new_size
|
|
|
|
|
new_size=$(get_file_size "$output_file")
|
|
|
|
|
|
|
|
|
|
# Проверка, что original_size не равен нулю
|
|
|
|
|
if [ "$original_size" -eq 0 ]; then
|
|
|
|
|
log_error "Ошибка: размер оригинального файла равен 0 для $output_file"
|
2024-09-18 20:17:38 +03:00
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Процент сжатия
|
|
|
|
|
local reduction
|
|
|
|
|
reduction=$(awk "BEGIN {printf \"%.2f\", (($original_size - $new_size) / $original_size) * 100}")
|
|
|
|
|
|
|
|
|
|
log_success "Сжат JPEG файл: $input_file на $reduction% ($original_size байт -> $new_size байт)"
|
|
|
|
|
|
2024-09-18 20:17:38 +03:00
|
|
|
|
echo "$current_hash" > "$hash_file"
|
|
|
|
|
}
|
2024-09-18 20:31:03 +03:00
|
|
|
|
export -f process_jpeg
|
2024-09-18 20:17:38 +03:00
|
|
|
|
|
|
|
|
|
# Функция для конвертации в WebP
|
|
|
|
|
process_webp() {
|
|
|
|
|
local input_file="$1"
|
|
|
|
|
local relative_path="${input_file#$COMP_DIR/}"
|
|
|
|
|
local output_file="$WEBP_DIR/$relative_path"
|
|
|
|
|
output_file="${output_file%.*}.webp"
|
2024-09-18 20:31:03 +03:00
|
|
|
|
local output_dir
|
|
|
|
|
output_dir="$(dirname "$output_file")"
|
2024-09-18 20:17:38 +03:00
|
|
|
|
|
|
|
|
|
mkdir -p "$output_dir"
|
|
|
|
|
|
|
|
|
|
# Проверка хеша файла
|
|
|
|
|
local hash_file="${output_file}.md5"
|
|
|
|
|
local current_hash
|
|
|
|
|
current_hash="$(md5sum "$input_file" | awk '{print $1}')"
|
|
|
|
|
|
|
|
|
|
if [ -f "$hash_file" ]; then
|
|
|
|
|
local previous_hash
|
|
|
|
|
previous_hash="$(cat "$hash_file")"
|
|
|
|
|
if [ "$current_hash" == "$previous_hash" ]; then
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Файл не изменился, ничего не делаем
|
2024-09-18 20:17:38 +03:00
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Размер до конвертации
|
|
|
|
|
local original_size
|
|
|
|
|
original_size=$(get_file_size "$input_file")
|
|
|
|
|
|
2024-09-18 20:33:18 +03:00
|
|
|
|
if ! cwebp -mt -af -quiet -m 6 -q 95 -pass 10 "$input_file" -o "$output_file"; then
|
2024-09-18 20:31:03 +03:00
|
|
|
|
log_error "Ошибка при конвертации $input_file в WebP"
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Размер после конвертации
|
|
|
|
|
local new_size
|
|
|
|
|
new_size=$(get_file_size "$output_file")
|
|
|
|
|
|
|
|
|
|
# Проверка, что original_size не равен нулю
|
|
|
|
|
if [ "$original_size" -eq 0 ]; then
|
|
|
|
|
log_error "Ошибка: размер оригинального файла равен 0 для $input_file"
|
2024-09-18 20:17:38 +03:00
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
2024-09-18 20:31:03 +03:00
|
|
|
|
# Процент сжатия
|
|
|
|
|
local reduction
|
|
|
|
|
reduction=$(awk "BEGIN {printf \"%.2f\", (($original_size - $new_size) / $original_size) * 100}")
|
|
|
|
|
|
|
|
|
|
log_success "Конвертирован в WebP: $input_file на $reduction% ($original_size байт -> $new_size байт)"
|
|
|
|
|
|
2024-09-18 20:17:38 +03:00
|
|
|
|
echo "$current_hash" > "$hash_file"
|
|
|
|
|
}
|
|
|
|
|
export -f process_webp
|
|
|
|
|
|
|
|
|
|
# Обработка PNG файлов
|
|
|
|
|
find "$IMAGE_DIR" -type f \
|
|
|
|
|
-not -path "$COMP_DIR/*" \
|
|
|
|
|
! -name "*-no-comp.*" \
|
|
|
|
|
-iname "*.png" -print0 | \
|
|
|
|
|
xargs -0 -P "$THREADS" -I {} bash -c 'process_png "$@"' _ {}
|
|
|
|
|
|
|
|
|
|
# Обработка JPEG файлов
|
|
|
|
|
find "$IMAGE_DIR" -type f \
|
|
|
|
|
-not -path "$COMP_DIR/*" \
|
|
|
|
|
! -name "*-no-comp.*" \
|
|
|
|
|
-iregex '.*\.\(jpg\|jpeg\)' -print0 | \
|
|
|
|
|
xargs -0 -P "$THREADS" -I {} bash -c 'process_jpeg "$@"' _ {}
|
|
|
|
|
|
|
|
|
|
# Конвертация в WebP
|
2024-09-18 21:46:57 +03:00
|
|
|
|
find ./images/comp -type f \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' \) \
|
2024-09-18 20:17:38 +03:00
|
|
|
|
-print0 | \
|
|
|
|
|
xargs -0 -P "$THREADS" -I {} bash -c 'process_webp "$@"' _ {}
|