Skip to content

Instantly share code, notes, and snippets.

@pirpyn
Last active February 22, 2024 12:31
Show Gist options
  • Save pirpyn/717e7680ed1cbabfdcdb8b8849baaf91 to your computer and use it in GitHub Desktop.
Save pirpyn/717e7680ed1cbabfdcdb8b8849baaf91 to your computer and use it in GitHub Desktop.
#!/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