re-init
This commit is contained in:
parent
948e4b8342
commit
3796f0c9f6
|
@ -0,0 +1,12 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# rename-ext install script
|
||||||
|
|
||||||
|
PREFIX=~/.local
|
||||||
|
|
||||||
|
git clone --depth 1 \
|
||||||
|
'https://source.garden/dym/rename-ext.git' \
|
||||||
|
$PREFIX/src/rename-ext/
|
||||||
|
|
||||||
|
ln -s $PREFIX/src/rename-ext/rename-ext.sh \
|
||||||
|
$PREFIX/bin/rename-ext
|
|
@ -0,0 +1,7 @@
|
||||||
|
title "rename-ext"
|
||||||
|
description "changes file-extension based on mime-type"
|
||||||
|
type "code"
|
||||||
|
tags "script" "bash" "file-management"
|
||||||
|
license "AGPL"
|
||||||
|
homepage "https://dym.sh/rename-ext"
|
||||||
|
source "https://source.garden/dym/rename-ext"
|
|
@ -0,0 +1,51 @@
|
||||||
|
# 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 `%`)
|
||||||
|
|
||||||
|
|
||||||
|
## install
|
||||||
|
|
||||||
|
1. look at [install](./install.sh) file for instructions
|
||||||
|
2. adjust `PREFIX` at wish
|
||||||
|
3. make sure the final directory is on `$PATH`
|
||||||
|
|
||||||
|
|
||||||
|
## use
|
||||||
|
|
||||||
|
`rename-ext /Data/Pictures/_unsorted/**/*`
|
||||||
|
|
||||||
|
to only change extension and nothing else:
|
||||||
|
|
||||||
|
`rename-ext --only-ext /Data/Photos/2020/*`
|
||||||
|
|
||||||
|
to only print a report:
|
||||||
|
|
||||||
|
`rename-ext --test-run /Data/Docs/_dump/*`
|
||||||
|
|
||||||
|
|
||||||
|
## requires
|
||||||
|
|
||||||
|
- [`sd`](https://github.com/chmln/sd) – better sed
|
||||||
|
- [`pngcrush`](https://github.com/Kjuly/pngcrush) – to compress PNGs
|
||||||
|
<!-- - [`pngquant`](https://github.com/kornelski/pngquant) – to compress PNGs -->
|
||||||
|
- [`jpegoptim`](https://github.com/tjko/jpegoptim) – to compress JPEGs
|
||||||
|
|
||||||
|
|
||||||
|
## todo
|
||||||
|
- [ ] find better determination tool for `application/octet-stream`
|
||||||
|
- [ ] leverage `fd` to do recursive paralel execution if some of parameters are folders
|
||||||
|
|
||||||
|
|
||||||
|
## q&a
|
||||||
|
|
||||||
|
**Q**: why not just rename files to hashes of their contents, and store original filenames in some database?
|
||||||
|
|
||||||
|
**A**: good point. that database is in my case a filesystem itself.
|
|
@ -0,0 +1,311 @@
|
||||||
|
#!/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
|
Loading…
Reference in New Issue