rename-ext/rename-ext.sh

314 lines
8.1 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env 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
# - file - to determine file type
# - `cargo install sd` better sed
# - jpegoptim to compress JPEGs
# - imagemagick or graphicsmagic to mogrify PNGs
# - pngcrush to compress PNGs (losless)
# // - pngquant to compress PNGs (lossy)
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?|av1
|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' ;;
'image/avif') rename_ext "$FILENAME" 'avif' ;;
# 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