Last active
February 22, 2024 12:31
-
-
Save pirpyn/717e7680ed1cbabfdcdb8b8849baaf91 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/bash | |
trap_sigint(){ | |
cat << EOF | |
Conversion interrupted. What do you want to do ? | |
s) stop | |
p) show/hide progress | |
EOF | |
read -n 1 reply | |
case $reply in | |
s) exit;; | |
p) | |
if [[ ${log} == /dev/null ]]; then | |
set -x | |
log=/dev/stderr | |
else | |
set +x | |
log=/dev/null | |
fi;; | |
*) trap_sigint;; | |
esac | |
main $@ | |
} | |
trap trap_sigint SIGINT | |
header(){ printf "\033[01;33m$@\033[0m\n" ;} | |
usage() | |
{ | |
cat << EOF | |
$(header "Compress videos & images in the current dir") | |
$(header usage:) | |
$0 -iV [-d dir] -w with -q quality -W width -c crf [-t N] [-M filename] | |
$0 -i [-d dir] -w with -q quality | |
$0 -V [-d dir] -W width -c crf [-t N] [-M filename] | |
$(header options:) | |
-h this message | |
-i compress images | |
-V compress videos | |
-d dir directory to process (default "$dir") | |
-x debug mode, print info during compress phase | |
$(header "image options:") | |
-w width reshape images to width pixels (default $image_width) | |
-q quality reencode images using this quality parameter: <lowest 0 -- 100 highest> (default $quality) | |
$(header "video options:") | |
-W width reshape images to width pixels (default $video_width) | |
-C crf reencode video using crf (quality) parameter: <highest 0 -- 40 lowest> (default $crf) | |
-T N use N threads for videos compressions (default $threads) | |
-M filename after compress, merge the files into filename (default $merged_output) | |
-D Dry run, do not compresss them | |
$(header notes:) | |
for -w & -W options, common screen width: 1024, 1152, 1280, 1366, 1600, 1920 | |
$(header examples:) | |
$0 -i -w 1920 -q 80 | |
$0 -V -W 1280 -c 28 | |
$0 -iV -w 1920 -q 80 -W 1280 -c 28 -M merged_videos.mp4 | |
EOF | |
} | |
stat() | |
{ | |
if [[ -f "${name}" && -f "${output}" ]]; then | |
local s0="$(du -b "${name}" | awk '{print $1}')" | |
local s1="$(du -b "${output}" | awk '{print $1}')" | |
global_before=$((global_before+s0)) | |
global_after=$((global_after+s1)) | |
local ratio=$(echo "scale=4;($s0-$s1)/$s0*100;" | bc) | |
local t0="$(du -h "${name}" | awk '{print $1}')" | |
local t1="$(du -h "${output}" | awk '{print $1}')" | |
printf "\r\033[K$output %s -> %s [%5.2f%% optimized]\n" $t0 $t1 $ratio | |
cumul_stat | |
fi | |
} | |
declare -i global_before=0 | |
declare -i global_after=0 | |
display_human() | |
{ | |
local bytes="$1" | |
local left="$1" | |
local units=(B kB MB GB TB) | |
local pow=1 | |
local idx=0 | |
if [[ "${bytes}" -lt 1024 ]]; then | |
echo "${bytes}B" | |
return | |
fi | |
while [[ $bytes -gt 1024 ]]; do | |
bytes=$((bytes/1024)) | |
idx=$((idx+1)) | |
pow=$((pow*1024)) | |
done | |
echo "${bytes}${units[$idx]} $(display_human $((left - bytes*pow)))" | |
} | |
cumul_stat() | |
{ | |
local ratio=$(echo "scale=4;($global_before-$global_after)/$global_before*100;" | bc) | |
printf "Cumul [%5.2f%% optimized] %s -> %s\n" "$ratio" "$(display_human global_before)" "$(display_human $global_after)" | |
} | |
compress_image() | |
{ | |
set -e | |
local width=${image_width} | |
local name="$1" | |
local output="$(readlink -m "${dir}/.resize/photos/${width}px_m${quality}/${name}")" | |
if [[ -f "${name}" && ! -f "${output}" ]]; then | |
local s0="$(du -b "${name}" | awk '{print $1}')" | |
if [[ $s0 -lt 1048576 ]]; then | |
cp "${name}" "${output}" | |
stat | |
return | |
fi | |
mkdir -p "$(dirname "${output}")" | |
convert "${name}" -resize ${width} "${output}" | |
local s1="$(du -b "${output}" | awk '{print $1}')" | |
jpegoptim -m${quality} "${output}" | |
local s2="$(du -b "${output}" | awk '{print $1}')" | |
local r10=$(echo "width=4;($s0-$s1)/$s0*100;" | bc) | |
local r21=$(echo "width=4;($s1-$s2)/$s1*100;" | bc) | |
local r20=$(echo "width=4;($s0-$s2)/$s0*100;" | bc) | |
printf "[1->2:%5.2f%% 2->3:%5.2f%% 1->3:%5.2f%% optimized]\r" $r10 $r21 $r20 | |
fi | |
set +e | |
stat | |
} | |
copy_video() | |
{ | |
local width=${video_width} | |
local name="$1" | |
local output="$(readlink -m "${dir}/.resize/videos/${width}p_cref${crf}/${name}")" | |
if [[ -f "${name}" && ! -f "${output}" ]]; then | |
mkdir -p "$(dirname "${output}")" | |
cp "${name}" "${output}" | |
fi | |
} | |
compress_video() | |
{ | |
local width=${video_width} | |
local name="$1" | |
local output="$(readlink -m "${dir}/.resize/videos/${width}p_cref${crf}/${name}")" | |
if [[ -f "${name}" && ! -f "${output}" ]]; then | |
local s0="$(du -b "${name}" | awk '{print $1}')" | |
if [[ $s0 -lt 1048576 ]]; then | |
cp "${name}" "${output}" | |
stat | |
return | |
fi | |
mkdir -p "$(dirname "${output}")" | |
echo -n "${name} $(du -h "${name}" | awk '{print $1}') ->" | |
ffmpeg -i "${name}" -threads ${threads} -filter:v scale=${width}:-2 -vcodec libx264 -crf ${crf} "${output}" &> $log | |
local s1="$(du -b "${output}" | awk '{print $1}')" | |
local t1="$(du -h "${output}" | awk '{print $1}')" | |
printf " %s [%5.2f%% optimized]\r" "$t1" $(echo "scale=4;($s0-$s1)/$s0*100;" | bc) | |
fi | |
stat | |
} | |
merged_videos() | |
{ | |
local width=${video_width} | |
local tmp="$(readlink -m "${dir}/.resize/videos/${width}p_cref${crf}/merged_list.txt")" | |
local output="$(readlink -m "${dir}/.resize/videos/${width}p_cref${crf}/${merged_output}")" | |
if [[ -f "${name}" && ! -f "${output}" ]]; then | |
cd ${dir}/.resize/videos/${width}p_cref${crf} | |
for name in ${@:-$(find . -name \*.mp4 -o -name \*.AVI -o -name \*.MOV)}; do | |
echo -e "file '$(readlink -m $name)'"; | |
done > $tmp | |
ffmpeg -f concat -safe 0 -i $tmp -threads ${threads} -c copy "${output}" &> $log | |
rm $tmp | |
fi | |
local name="${output}" | |
stat | |
} | |
mode="" | |
crf=25 | |
image_width=1920 | |
video_width=1280 | |
dir="." | |
quality=90 | |
OPTIND=1 | |
merged_output="merged.mp4" | |
threads=0 | |
log=/dev/null | |
if [[ ${1//-} == help ]]; then | |
usage | |
exit 0 | |
fi | |
while getopts hxd:iw:q:VW:C:T:M:D arg; do | |
case $arg in | |
# common options | |
(h) usage; exit 0;; | |
(d) dir="$(readlink -m ${OPTARG})";; | |
(x) set -x; log=/dev/stderr;; | |
# image options | |
(i) mode+="i";; | |
(w) image_width=${OPTARG};; | |
(q) quality=${OPTARG};; | |
# videos options | |
(V) mode+="v";; | |
(W) video_width=${OPTARG};; | |
(C) crf=${OPTARG};; | |
(T) threads="${OPTARG}";; | |
(M) mode+="m"; merged_output="${OPTARG}";; | |
(D) mode+="d";; | |
(*) usage; exit 1;; | |
esac | |
done | |
shift $(($OPTIND-1)) | |
mode=${mode:-v} | |
main() | |
{ | |
case ${mode} in | |
(*i*) | |
for name in ${@:-$(find -not -wholename \*.resize/\* -and \( -name \*.jpg -o -name \*.JPG \) )}; do | |
compress_image "$name" | |
done | |
;;& # check next case | |
(*d*) | |
for name in ${@:-$(find -not -wholename \*.resize/\* -and \( -name \*.mp4 -o -name \*.AVI -o -name \*.MOV \) )}; do | |
copy_video "$name" | |
done | |
;;& # check next case | |
(*v*) | |
for name in ${@:-$(find -not -wholename \*.resize/\* -and \( -name \*.mp4 -o -name \*.AVI -o -name \*.MOV \) )}; do | |
compress_video "$name" | |
done | |
;;& # check next case | |
(*m*) | |
merged_videos $@ | |
;;& # check next case | |
esac | |
} | |
main $@ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment