rename-ext/rename-ext.sh

312 lines
7.9 KiB
Bash
Raw Normal View History

2023-06-20 17:22:54 +02:00
#!/bin/bash
## rename-ext
# > changes file-extension based on mime-type
## also
# - removes `*?|^:"<>` characters from the file-name
# - reducing multiple spaces, dots, and underscores
# - adds rename-date suffix to ensure uniquness of the filename
# - strips exec-flag from non-executive types (usual leftover from FAT/NTFS)
# - optimizes images while at it (shows reduction size in `kb` and `%`)
## example
# rename-ext /Data/Pictures/_unsorted/**/*
## requires
# - `cargo install sd` better sed
# - pngcrush to compress PNGs (losless)
# // - pngquant to compress PNGs (lossy)
# - jpegoptim to compress JPEGs
ALL_EXTS=`echo \
'gif|jpe?g|jp2|jfif|a?png|web(p|m)|svg
|eps|tga|tiff?|psd|ico|xcf|heic
|eot|otf|ttf|epub|doc|docx|xls|swf|pdf|odt
|flac|opus|ogg|m4a|wav
|mpe?g|mp\d|mov|mkv|avif?|asf|3gpp?
|html?|sh|py|php
' \
| tr -d '[:space:]' \
`
mkdir -p ~/tmp
OPTIONS=''
CHX_CHANGELOG=''
NR='1'
calc_reduction()
{
SIZE_PRE="$1"
SIZE_POST="$2"
PERECENT=` echo "scale=2; 100 - (100 * $SIZE_POST / $SIZE_PRE)" | bc `
RESULT="$(( $SIZE_PRE / 1000 ))-$(( ( $SIZE_PRE - $SIZE_POST ) / 1000 )) kb \
($PERECENT%)
"
echo "$RESULT"
}
ch_x()
{
FILENAME="$1"
# echo "FILENAME = '$FILENAME'"
CHX_CHANGELOG=''
IS_X_INTENDED='0'
if [ ! -z "$2" ]; then
IS_X_INTENDED='1'
fi
# echo "IS_X_INTENDED = '$IS_X_INTENDED'"
IS_X=`stat -c '%A' "$FILENAME" | grep 'x' | wc -l`
# echo "IS_X = '$IS_X'"
MODE='-x'
if [[ '1' == "$IS_X_INTENDED" ]]; then
MODE='+x'
fi
# echo "MODE = '$MODE'"
if [[ ! "$OPTIONS" =~ 'test-run' ]]; then
# echo '(not a test-run)'
if [[ ! "$OPTIONS" =~ 'only-ext' ]]; then
# echo '(not only-ext)'
if [[ ! "$IS_X" == "$IS_X_INTENDED" ]]; then
CHX_CHANGELOG="chmod $MODE"
fi
chmod "$MODE" "$FILENAME"
fi
fi
}
rename_ext()
{
NAME="$1"
EXT="$2"
NOTE=''
TARGET=''
ch_x "$FILENAME"
if [[ "$OPTIONS" =~ 'only-ext' ]]; then
NAME_NO_EXT=` echo "$NAME" \
| sd -f i "(\.+($ALL_EXTS))$" '' \
`
TARGET="$NAME_NO_EXT.$EXT"
else
if [[ ! "$OPTIONS" =~ 'test-run' ]]; then
SIZE_PRE=` stat "$FILENAME" -c '%s' `
[ 'jpg' = "$EXT" ] \
&& jpegoptim "$FILENAME" -qsftp
if [ 'png' = "$EXT" ]; then
mogrify "$FILENAME"
pngcrush -nolimits -s "$FILENAME" "$FILENAME.tmp.png"
# pngquant "$FILENAME" --output "$FILENAME" \
# --quality=100 --force --skip-if-larger --speed 1
if [ -f "$FILENAME.tmp.png" ] \
&& [ -s "$FILENAME.tmp.png" ]
then
mv "$FILENAME.tmp.png" "$FILENAME"
else
rm "$FILENAME.tmp.png"
fi
fi
SIZE_POST=` stat "$FILENAME" -c '%s' `
[ $SIZE_PRE -gt $SIZE_POST ] \
&& NOTE=` calc_reduction "$SIZE_PRE" "$SIZE_POST" `
fi
DATE=` date -r "$NAME" -u "+%Y%m%d%H%M%S" `
TCLEAN=` echo "$NAME" \
| sd '[\\\?\*\|^:"<>]+' '_' \
| sd '_\d{12,}\.' '.' \
| sd -f i "(\.+($ALL_EXTS))+$" '' \
`
TARGET=` echo "${TCLEAN}_$DATE.$EXT" \
| sd '([\s\.,_]){3,}' '$1' \
`
fi
IS_SAME_NAME='0'
if [[ "$NAME" == "$TARGET" ]]; then
IS_SAME_NAME='1'
fi
# echo "IS_SAME_NAME = '$IS_SAME_NAME'"
# echo "CHX_CHANGELOG = '$CHX_CHANGELOG'"
if [[ '1' == "$IS_SAME_NAME" ]] \
&& [ -z "$CHX_CHANGELOG" ]
then
return
fi
echo "$NR:"
NR="$(($NR+1))"
echo " $FILENAME"
if [ ! -z "$CHX_CHANGELOG" ]; then
echo " $CHX_CHANGELOG"
CHX_CHANGELOG=''
fi
if [[ '1' == "$IS_SAME_NAME" ]]; then
return
fi
if [[ "$OPTIONS" =~ 'test-run' ]]; then
if [ -f "$TARGET" ]; then
if [[ "$PARAMS" =~ 'force-replace' ]]; then
echo " >> replacing existing file >>"
else
echo " !! file already exist !!"
fi
fi
echo "$TARGET"
else
if [ ! -f "$TARGET" ]; then
echo " >> $NOTE >>"
echo " $TARGET"
mv "$NAME" "$TARGET"
else
if [[ "$PARAMS" =~ 'force-replace' ]]; then
echo " >> $NOTE ## replacing existing file >>"
echo " $TARGET"
mv "$NAME" "$TARGET"
else
echo ' !! File already exist, use `--force-replace` to overwrite !! '
echo " $TARGET"
fi
fi
fi
}
for PARAM in "$@"; do
# echo "PARAM = '$PARAM'"
if [[ '--only-ext' == "$PARAM" ]]; then
OPTIONS="$OPTIONS;only-ext"
# echo "OPTIONS = '$OPTIONS'"
elif [[ '--test-run' == "$PARAM" ]]; then
OPTIONS="$OPTIONS;test-run"
# echo "OPTIONS = '$OPTIONS'"
elif [[ '--force-replace' == "$PARAM" ]]; then
OPTIONS="$OPTIONS;force-replace"
# echo "OPTIONS = '$OPTIONS'"
fi
done
[ ! -z "$OPTIONS" ] \
&& echo "OPTIONS: '$OPTIONS'"
# echo "@ = '$@'"
for FILENAME in "$@"; do
# echo "FILENAME = '$FILENAME'"
# NOT_F_=`[ ! -f "$FILENAME" ]`
# echo "NOT_F_ = '$NOT_F_'"
# [ ! -f "$FILENAME" ] \
# && continue
FILE_TYPE=` file -b --mime-type "$FILENAME" `
# echo "FILE_TYPE = '$FILE_TYPE'"
case "$FILE_TYPE" in
# pixel-based
'image/gif') rename_ext "$FILENAME" 'gif' ;;
'image/jpeg') rename_ext "$FILENAME" 'jpg' ;;
'image/jp2') rename_ext "$FILENAME" 'jp2' ;;
'image/png') rename_ext "$FILENAME" 'png' ;;
'image/webp') rename_ext "$FILENAME" 'webp' ;;
# vector-based
'image/svg+xml') rename_ext "$FILENAME" 'svg' ;;
'image/x-eps') rename_ext "$FILENAME" 'eps' ;;
# some less common image formats
'image/heic') rename_ext "$FILENAME" 'heic' ;;
'image/tiff') rename_ext "$FILENAME" 'tiff' ;;
'image/vnd.adobe.photoshop') rename_ext "$FILENAME" 'psd' ;;
'image/vnd.microsoft.icon') rename_ext "$FILENAME" 'ico' ;;
'image/x-tga') rename_ext "$FILENAME" 'tga' ;;
'image/x-xcf') rename_ext "$FILENAME" 'xcf' ;;
# fonts
'application/vnd.ms-fontobject') rename_ext "$FILENAME" 'eot' ;;
'application/vnd.ms-opentype') rename_ext "$FILENAME" 'otf' ;;
'font/sfnt') rename_ext "$FILENAME" 'ttf' ;;
# documents
'application/epub+zip') rename_ext "$FILENAME" 'epub' ;;
'application/msword') rename_ext "$FILENAME" 'doc' ;;
'application/pdf') ch_x "$FILENAME" ;; # can be .ai
'application/vnd.ms-excel') rename_ext "$FILENAME" 'xls' ;;
'application/vnd.oasis.opendocument.text') rename_ext "$FILENAME" 'odt' ;;
'application/vnd.openxmlformats-officedocument.wordprocessingml.document') rename_ext "$FILENAME" 'docx' ;;
'application/x-shockwave-flash') rename_ext "$FILENAME" 'swf' ;;
# audio
'audio/flac') rename_ext "$FILENAME" 'flac' ;;
'audio/mpeg') rename_ext "$FILENAME" 'mp3' ;;
'audio/ogg') rename_ext "$FILENAME" 'ogg' ;;
'audio/x-m4a') rename_ext "$FILENAME" 'm4a' ;;
'audio/x-wav') rename_ext "$FILENAME" 'wav' ;;
# video
'video/3gpp') rename_ext "$FILENAME" '3gp' ;;
'video/av1') rename_ext "$FILENAME" 'avi' ;;
'video/MP2T') rename_ext "$FILENAME" 'mp2' ;;
'video/mp4') rename_ext "$FILENAME" 'mp4' ;;
'video/quicktime') rename_ext "$FILENAME" 'mov' ;;
'video/webm') rename_ext "$FILENAME" 'webm' ;;
'video/x-m4v') rename_ext "$FILENAME" 'mp4' ;;
'video/x-matroska') rename_ext "$FILENAME" 'mkv' ;;
'video/x-ms-asf') rename_ext "$FILENAME" 'asf' ;;
# text
'application/json') ch_x "$FILENAME" ;; # can be any other language
'text/html') ch_x "$FILENAME" ;; # can be .htm, .htc, .mht, ...
'text/xml') ch_x "$FILENAME" ;; # can be .xml, .opml, .rss, ...
'text/plain') ;; # can be any file with not enough text
'text/x-python') ;; # can be .py, .py3, have no .ext
'text/x-shellscript') ;; # can be .sh, .zsh, have no .ext
# binady data
'application/x-sqlite3') ch_x "$FILENAME" ;; # can be .db, .sql, .sq3
# special
'application/octet-stream') ;; # can be anything
'inode/directory')
# echo "$FILENAME is a folder"
# echo "about to ch_x"
ch_x "$FILENAME" '+'
;; # a folder
'inode/x-empty') ch_x "$FILENAME" ;; # zero-size
# default
*) echo "?? $FILENAME : $FILE_TYPE" ;;
esac
done
# if [[ "@#" -eq 1 ]]; then
# if [[ `file -b --mime-type "$1"` == "inode/directory" ]]; then
# find "$1"
# fi
# fi