Shell/Scripts: moved transcoding functions to separate scripts.
Shell: included FreeBSD and Pacman aliases in main alias file.master
parent
9db5273018
commit
524aaf9042
|
@ -0,0 +1,31 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$(command -v ffmpeg)" ]; then
|
||||||
|
echo "ffmpeg required."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$HOME/.shell.d/titlecase.awk" ]; then
|
||||||
|
echo "AWK titlecase script required."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${0%/*}/tc-audio-transcode" ]; then
|
||||||
|
echo "tc-audio-transcode script required."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
## This will convert all tracks in current folder and subfolders.
|
||||||
|
while read -r i; do
|
||||||
|
"${0%/*}/tc_audio_transcode" "$@" $i
|
||||||
|
done <<EOF
|
||||||
|
$(find "." \( \
|
||||||
|
-iname '*.mp3' -o \
|
||||||
|
-iname '*.flac' -o \
|
||||||
|
-iname '*.wv' -o \
|
||||||
|
-iname '*.aac' -o \
|
||||||
|
-iname '*.wav' -o \
|
||||||
|
-iname '*.ape' -o \
|
||||||
|
-iname '*.mpc' \) )
|
||||||
|
EOF
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$(command -v mediainfo)" ]; then
|
||||||
|
echo "mediainfo required."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
## This will check for badly encoded audio.
|
||||||
|
while read -r i; do
|
||||||
|
BUFFER="$(mediainfo "$i")"
|
||||||
|
if [ -n "$(echo "$BUFFER" | grep "Bit rate mode *: Constant")" ]; then
|
||||||
|
echo "$i : CBR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$(echo "$BUFFER" | grep "Cover *: Yes")" ]; then
|
||||||
|
echo "$i : Cover"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$(echo "$BUFFER" | grep "Track name/Position *: 0+")" ]; then
|
||||||
|
echo "$i : Leading zeros"
|
||||||
|
fi
|
||||||
|
done <<EOF
|
||||||
|
$(find "." \( \
|
||||||
|
-iname '*.mp3' -o \
|
||||||
|
-iname '*.flac' -o \
|
||||||
|
-iname '*.wv' -o \
|
||||||
|
-iname '*.aac' -o \
|
||||||
|
-iname '*.wav' -o \
|
||||||
|
-iname '*.ape' -o \
|
||||||
|
-iname '*.mpc' \) )
|
||||||
|
EOF
|
||||||
|
|
|
@ -0,0 +1,380 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$(command -v ffmpeg)" ]; then
|
||||||
|
echo "ffmpeg required."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${0%/*}/titlecase.awk" ]; then
|
||||||
|
echo "AWK titlecase script required."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## User options
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
## For the sake of simplicity we convert everything to OGG.
|
||||||
|
## You can easily set output folder to current folder with TC_AUDIO_DEST=".".
|
||||||
|
TC_AUDIO_DEST="/media/data1/Musics/"
|
||||||
|
|
||||||
|
## OGG quality ranges from -1 to 10.
|
||||||
|
## -q-1 45 kbit/s
|
||||||
|
## -q0 64 kbit/s
|
||||||
|
## -q1 80 kbit/s
|
||||||
|
## -q2 96 kbit/s
|
||||||
|
## -q3 112 kbit/s
|
||||||
|
## -q4 128 kbit/s
|
||||||
|
## -q5 160 kbit/s
|
||||||
|
## -q6 192 kbit/s
|
||||||
|
## -q7 224 kbit/s
|
||||||
|
## -q8 256 kbit/s
|
||||||
|
## -q9 320 kbit/s
|
||||||
|
## -q10 500 kbit/s
|
||||||
|
|
||||||
|
## Artists: we use same value for artist and album_artist.
|
||||||
|
## Genres: since this is not universal by nature, we do not put a genre in tags.
|
||||||
|
## Composer: not universal neither, we prefer ARTIST over COMPOSER, so COMPOSER will be empty.
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## End of user options
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
## This function is supposed to work on a per-album basis. Folders will not be
|
||||||
|
## accepted as argument. This function will work best if all tracks of a folder
|
||||||
|
## belong to the same album. Theres is no way to handle covers raliably, so you
|
||||||
|
## should leave the only covers you want to keep in the working folder.
|
||||||
|
|
||||||
|
## TODO: cover resolution check. Should be > 100x100. Above <1000x1000 should give a warning.
|
||||||
|
## TODO: extract cover from tags if checksum does not match any from current folder.
|
||||||
|
|
||||||
|
_printhelp ()
|
||||||
|
{
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $1 [OPTIONS] FILE
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-p : preview (do not change file)
|
||||||
|
-s : skip encoding
|
||||||
|
|
||||||
|
Tags:
|
||||||
|
-a : artist
|
||||||
|
-b : bitrate
|
||||||
|
-d : date
|
||||||
|
-g : genre
|
||||||
|
-l : album
|
||||||
|
-n : track number
|
||||||
|
-t : title
|
||||||
|
|
||||||
|
You can use the following variables to refer to existing tags:
|
||||||
|
\$ALBUM
|
||||||
|
\$ALBUM_ARTIST
|
||||||
|
\$ARTIST
|
||||||
|
\$COMPOSER
|
||||||
|
\$DATE
|
||||||
|
\$TYER
|
||||||
|
\$GENRE
|
||||||
|
\$TRACK
|
||||||
|
|
||||||
|
Default output folder:
|
||||||
|
OUTPUT="\$TC_AUDIO_DEST/\$OUTPUT_ARTIST/\${OUTPUT_ALBUM:+\${OUTPUT_DATE:+\$OUTPUT_DATE - }\$OUTPUT_ALBUM/}"
|
||||||
|
|
||||||
|
Default output file:
|
||||||
|
OUTPUT_FILE="\$OUTPUT\$OUTPUT_ARTIST - \${OUTPUT_TRACK:+\$OUTPUT_TRACK - }\$OUTPUT_TITLE.\$OUTPUT_FORMAT"
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
Set the 'artist' tag and reencode:
|
||||||
|
$1 -a 'Franz Lizst' file.mp3
|
||||||
|
|
||||||
|
Set 'artist' to be 'composer', and 'title' to be preceeded by 'artist', do not reencode:
|
||||||
|
$1 -s -a '\$COMPOSER' -t '\$ARTIST - \$TITLE' file.ogg
|
||||||
|
|
||||||
|
IMPORTANT: you *must* use single quotes when using variables.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
SKIP=0
|
||||||
|
|
||||||
|
PREVIEW=0
|
||||||
|
|
||||||
|
## TAGS
|
||||||
|
OUTPUT_ALBUM='$ALBUM'
|
||||||
|
OUTPUT_ARTIST='$ARTIST'
|
||||||
|
OUTPUT_DATE='$DATE'
|
||||||
|
OUTPUT_GENRE='$GENRE'
|
||||||
|
OUTPUT_TITLE='$TITLE'
|
||||||
|
OUTPUT_TRACK='$TRACK'
|
||||||
|
|
||||||
|
## PROPERTIES
|
||||||
|
OUTPUT_BITRATE=0
|
||||||
|
|
||||||
|
## Non-cli-option data. Modifying these imply modifications in code below.
|
||||||
|
OUTPUT_FORMAT="ogg"
|
||||||
|
OGG_PARAM="-c:a libvorbis -b:a ${OUTPUT_BITRATE}k"
|
||||||
|
|
||||||
|
## These ones are not cli-options either, but this could be easily changed.
|
||||||
|
OUTPUT='$TC_AUDIO_DEST/$OUTPUT_ARTIST/${OUTPUT_ALBUM:+${OUTPUT_DATE:+$OUTPUT_DATE - }$OUTPUT_ALBUM/}'
|
||||||
|
OUTPUT_FILE='$OUTPUT$OUTPUT_ARTIST - ${OUTPUT_PADDEDTRACK:+$OUTPUT_PADDEDTRACK - }$OUTPUT_TITLE'
|
||||||
|
|
||||||
|
while getopts ":a:b:d:g:l:n:t:hps" opt; do
|
||||||
|
case $opt in
|
||||||
|
|
||||||
|
a) OUTPUT_ARTIST=$OPTARG ;;
|
||||||
|
b) OUTPUT_BITRATE=$OPTARG ;;
|
||||||
|
d) OUTPUT_DATE=$OPTARG ;;
|
||||||
|
g) OUTPUT_GENRE=$OPTARG ;;
|
||||||
|
l) OUTPUT_ALBUM=$OPTARG ;;
|
||||||
|
n) OUTPUT_TRACK=$OPTARG ;;
|
||||||
|
t) OUTPUT_TITLE=$OPTARG ;;
|
||||||
|
|
||||||
|
h)
|
||||||
|
_printhelp "$0"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
p)
|
||||||
|
PREVIEW=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
s)
|
||||||
|
SKIP=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
?)
|
||||||
|
_printhelp "$0"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
:)
|
||||||
|
echo "Missing argument."
|
||||||
|
echo "Use $0 -h for help."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
shift $(($OPTIND - 1))
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
_printhelp "$0"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
##================================================================================
|
||||||
|
## Get metadata.
|
||||||
|
STREAM=$(ffmpeg -i "$1" 2>&1)
|
||||||
|
METADATA=$(echo "$STREAM" | sed -n '/Metadata/ ! d; /Metada/{b cont}; :cont ; {n;p;b cont}')
|
||||||
|
STREAM=$(echo "$STREAM" | grep "Stream")
|
||||||
|
|
||||||
|
if [ -z "$STREAM" ]; then
|
||||||
|
echo "Non-audio file."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
## We get format from extension, because codec is not reliable either.
|
||||||
|
FORMAT="${1##*.}"
|
||||||
|
## CODEC is unused for now.
|
||||||
|
CODEC=$(echo "$STREAM" | sed -n '/Audio:/ {s/.*Audio: \([^,]*\),.*/\1/;p}')
|
||||||
|
BITRATE=$(echo "$STREAM" | sed -n '/kb\/s/ {s/.*, \(.*\) kb\/s/\1/;p}')
|
||||||
|
|
||||||
|
if [ -z "$FORMAT" ]; then
|
||||||
|
echo "ERROR: non-audio file."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
_metadata_filter()
|
||||||
|
{
|
||||||
|
echo "$METADATA" | grep -i "\<$1\>" | sed 's/[^:]* : //g'
|
||||||
|
}
|
||||||
|
|
||||||
|
TITLE=$(_metadata_filter "title")
|
||||||
|
ARTIST=$(_metadata_filter "artist")
|
||||||
|
ALBUM=$(_metadata_filter "album")
|
||||||
|
ALBUM_ARTIST=$(_metadata_filter "album_artist")
|
||||||
|
COMPOSER=$(_metadata_filter "composer")
|
||||||
|
DISC=$(_metadata_filter "disc")
|
||||||
|
GENRE=$(_metadata_filter "genre")
|
||||||
|
|
||||||
|
TRACK=$(_metadata_filter "track")
|
||||||
|
DATE=$(_metadata_filter "date")
|
||||||
|
TYER=$(_metadata_filter "TYER")
|
||||||
|
|
||||||
|
##================================================================================
|
||||||
|
## OUTPUT variables.
|
||||||
|
|
||||||
|
## We use the AWK script to set title case. The script contains
|
||||||
|
## exceptions that can be configured. We fix some chars with sed.
|
||||||
|
# ’ => '
|
||||||
|
# : => -
|
||||||
|
# / => -
|
||||||
|
_string_cleanser()
|
||||||
|
{
|
||||||
|
echo "$@" | awk -f "${0%/*}/titlecase.awk" \
|
||||||
|
| sed -n -e "s/’/'/g ; s/ *: */ - /g ; s| */ *| - |g; p; q"
|
||||||
|
}
|
||||||
|
|
||||||
|
OUTPUT_TITLE=$(eval _string_cleanser $OUTPUT_TITLE)
|
||||||
|
OUTPUT_TITLE="${OUTPUT_TITLE:-Unknown Title}"
|
||||||
|
|
||||||
|
## We use album artist if artist is empty.
|
||||||
|
OUTPUT_ARTIST=$(eval _string_cleanser $OUTPUT_ARTIST)
|
||||||
|
[ -z "$OUTPUT_ARTIST" ] && [ -n "$ALBUM_ARTIST" ] && OUTPUT_ARTIST="$ALBUM_ARTIST"
|
||||||
|
OUTPUT_ARTIST="${OUTPUT_ARTIST:-Unknown Artist}"
|
||||||
|
|
||||||
|
OUTPUT_ALBUM=$(eval _string_cleanser $OUTPUT_ALBUM)
|
||||||
|
[ -z "$OUTPUT_ALBUM" ] && echo "${#OUTPUT_ALBUM}${OUTPUT_ALBUM}"
|
||||||
|
|
||||||
|
## We put genre in lower case and underscore to ease matching. If it
|
||||||
|
## matches, we use the Title Case match. If it does not, we set it to empty.
|
||||||
|
OUTPUT_GENRE=$(eval _string_cleanser $OUTPUT_GENRE | tr '[:upper:] ' '[:lower:]_')
|
||||||
|
case $OUTPUT_GENRE in
|
||||||
|
ost) OUTPUT_GENRE="Soundtrack" ;;
|
||||||
|
soundtrack) OUTPUT_GENRE="Soundtrack";;
|
||||||
|
original_soundtrack) OUTPUT_GENRE="Soundtrack";;
|
||||||
|
classical) OUTPUT_GENRE="Classical";;
|
||||||
|
classics) OUTPUT_GENRE="Classical";;
|
||||||
|
classic) OUTPUT_GENRE="Classical";;
|
||||||
|
*) OUTPUT_GENRE="";;
|
||||||
|
esac
|
||||||
|
|
||||||
|
## We remove the track count if any, we suppress leading zeros.
|
||||||
|
OUTPUT_TRACK=$(eval _string_cleanser $OUTPUT_TRACK | sed -e 's/^0*//' -e 's|/.*||')
|
||||||
|
|
||||||
|
## If DATE is not a year, we use TYER if it is a year.
|
||||||
|
OUTPUT_DATE=$(eval _string_cleanser $OUTPUT_DATE)
|
||||||
|
TYER_REG=$(echo "$TYER" | sed -n 's/.*\([[:digit:]]\{4\}\).*/\1/p')
|
||||||
|
if [ ${#DATE} -ne 4 ] && [ ${#TYER_REG} -eq 4 ]; then
|
||||||
|
OUTPUT_DATE="$TYER_REG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
## QUALITY
|
||||||
|
## If bitrate argumument is not provided, we use BITRATE value.
|
||||||
|
[ $OUTPUT_BITRATE -eq 0 ] && OUTPUT_BITRATE=${BITRATE}
|
||||||
|
## TODO: test if table is useful at all.
|
||||||
|
# if [ $OUTPUT_BITRATE -eq 0 ]; then
|
||||||
|
# [ $BITRATE -le 45 ] && OUTPUT_BITRATE=45k
|
||||||
|
# [ $BITRATE -gt 45 ] && OUTPUT_BITRATE=64k
|
||||||
|
# [ $BITRATE -gt 64 ] && OUTPUT_BITRATE=80k
|
||||||
|
# [ $BITRATE -gt 80 ] && OUTPUT_BITRATE=96k
|
||||||
|
# [ $BITRATE -gt 96 ] && OUTPUT_BITRATE=112k
|
||||||
|
# [ $BITRATE -gt 112 ] && OUTPUT_BITRATE=128k
|
||||||
|
# [ $BITRATE -gt 128 ] && OUTPUT_BITRATE=160k
|
||||||
|
# [ $BITRATE -gt 160 ] && OUTPUT_BITRATE=192k
|
||||||
|
# [ $BITRATE -gt 192 ] && OUTPUT_BITRATE=224k
|
||||||
|
# [ $BITRATE -gt 224 ] && OUTPUT_BITRATE=256k
|
||||||
|
# [ $BITRATE -gt 256 ] && OUTPUT_BITRATE=320k
|
||||||
|
# [ $BITRATE -gt 320 ] && OUTPUT_BITRATE=500k
|
||||||
|
# fi
|
||||||
|
|
||||||
|
## Only reencode if not in OGG and if SKIP not set.
|
||||||
|
OGG_PARAM="-c:a libvorbis -b:a ${OUTPUT_BITRATE}k"
|
||||||
|
[ $SKIP -ne 0 ] && OGG_PARAM="-c:a copy" && OUTPUT_FORMAT="$FORMAT"
|
||||||
|
[ "$FORMAT" = "ogg" ] && OGG_PARAM="-c:a copy"
|
||||||
|
|
||||||
|
## Make sure track number has two digits for file name only.
|
||||||
|
OUTPUT_PADDEDTRACK
|
||||||
|
OUTPUT_PADDEDTRACK=$OUTPUT_TRACK
|
||||||
|
if [ -n "$OUTPUT_PADDEDTRACK" ]; then
|
||||||
|
[ ${OUTPUT_PADDEDTRACK} -lt 10 ] && OUTPUT_PADDEDTRACK="0$OUTPUT_PADDEDTRACK"
|
||||||
|
fi
|
||||||
|
|
||||||
|
OUTPUT=$(eval echo $OUTPUT)
|
||||||
|
OUTPUT_FILE=$(eval echo $OUTPUT_FILE)
|
||||||
|
|
||||||
|
## IF file exist, we append a uniq timestamp to the name.
|
||||||
|
[ -e "${OUTPUT_FILE}.$OUTPUT_FORMAT" ] && OUTPUT_FILE="$OUTPUT_FILE-$(date '+%F-%H%M%S')"
|
||||||
|
|
||||||
|
##==============================================================================
|
||||||
|
## PREVIEW
|
||||||
|
|
||||||
|
echo "==> BEFORE"
|
||||||
|
echo "ALBUM [$ALBUM]"
|
||||||
|
echo "ALBUM_ARTIST [$ALBUM_ARTIST]"
|
||||||
|
echo "ARTIST [$ARTIST]"
|
||||||
|
echo "BITRATE [$BITRATE]"
|
||||||
|
echo "COMPOSER [$COMPOSER]"
|
||||||
|
echo "DATE [$DATE]"
|
||||||
|
echo "DISC [$DISC]"
|
||||||
|
echo "FORMAT [$FORMAT]"
|
||||||
|
echo "GENRE [$GENRE]"
|
||||||
|
echo "TITLE [$TITLE]"
|
||||||
|
echo "TRACK [$TRACK]"
|
||||||
|
echo "TYER [$TYER]"
|
||||||
|
echo
|
||||||
|
echo "==> AFTER"
|
||||||
|
echo "ALBUM [$OUTPUT_ALBUM]"
|
||||||
|
echo "ARTIST [$OUTPUT_ARTIST]"
|
||||||
|
echo "BITRATE [$OUTPUT_BITRATE]"
|
||||||
|
echo "DATE [$OUTPUT_DATE]"
|
||||||
|
echo "FORMAT [$OUTPUT_FORMAT]"
|
||||||
|
echo "GENRE [$OUTPUT_GENRE]"
|
||||||
|
echo "TITLE [$OUTPUT_TITLE]"
|
||||||
|
echo "TRACK [$OUTPUT_TRACK]"
|
||||||
|
echo
|
||||||
|
echo "==> OUTPUT"
|
||||||
|
echo "[$OUTPUT_FILE.$OUTPUT_FORMAT]"
|
||||||
|
# echo "[$OGG_PARAM]"
|
||||||
|
echo
|
||||||
|
|
||||||
|
[ $PREVIEW -ne 0 ] && exit
|
||||||
|
|
||||||
|
##==============================================================================
|
||||||
|
## RUN PROCESS
|
||||||
|
|
||||||
|
## Make sure directory exists.
|
||||||
|
mkdir -p "$OUTPUT"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "ERROR: could not create output folder"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
## COVER
|
||||||
|
## We copy cover only if it does not already exist.
|
||||||
|
while read -r i; do
|
||||||
|
OUTPUT_COVER="$OUTPUT/${OUTPUT_ALBUM:+$OUTPUT_ALBUM - }Cover"
|
||||||
|
OUTPUT_COVERFILE="$OUTPUT_COVER.${i##*.}"
|
||||||
|
COVER_COUNTER=1
|
||||||
|
|
||||||
|
## Same cover is already in target folder.
|
||||||
|
if [ -e "$OUTPUT_COVERFILE" ] && \
|
||||||
|
[ "$(sha1sum $OUTPUT_COVERFILE | cut -f1 -d' ')" = "$(sha1sum $i | cut -f1 -d' ')" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Different cover with same name is in target folder. We append a number.
|
||||||
|
while [ -e "$OUTPUT_COVERFILE" ] && \
|
||||||
|
[ ! "$(sha1sum $OUTPUT_COVERFILE | cut -f1 -d' ')" = "$(sha1sum $i | cut -f1 -d' ')" ]; do
|
||||||
|
OUTPUT_COVERFILE="${OUTPUT_COVER} $COVER_COUNTER.${i##*.}"
|
||||||
|
COVER_COUNTER=$(($COVER_COUNTER+1))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "==> COVER"
|
||||||
|
cp -nv "$i" "$OUTPUT_COVERFILE"
|
||||||
|
echo
|
||||||
|
done <<EOF
|
||||||
|
$(find "." \( -iname '*.png' -o -iname '*.jpg' \) )
|
||||||
|
EOF
|
||||||
|
|
||||||
|
## Zsh compatibility. We need it otherwise word splitting of parameter like
|
||||||
|
## OGG_PARAM will not work.
|
||||||
|
STATUS="$(set -o | grep 'shwordsplit' | awk '{print $2}')"
|
||||||
|
[ "$STATUS" = "off" ] && set -o shwordsplit
|
||||||
|
|
||||||
|
## TAG/RECODE
|
||||||
|
ffmpeg -i "$1" $OGG_PARAM \
|
||||||
|
-metadata title="$OUTPUT_TITLE" \
|
||||||
|
-metadata artist="$OUTPUT_ARTIST" \
|
||||||
|
-metadata track="$OUTPUT_TRACK" \
|
||||||
|
-metadata date="$OUTPUT_DATE" \
|
||||||
|
-metadata album="$OUTPUT_ALBUM" \
|
||||||
|
-metadata album_artist="$OUTPUT_ARTIST" \
|
||||||
|
-metadata genre="$OUTPUT_GENRE" \
|
||||||
|
-metadata composer="" \
|
||||||
|
-metadata TYER="" \
|
||||||
|
-metadata disc="" \
|
||||||
|
"$OUTPUT_FILE.$OUTPUT_FORMAT"
|
||||||
|
|
||||||
|
## Restore Zsh previous options. This will not turn off shwordsplit if it
|
||||||
|
## was on before calling the function.
|
||||||
|
[ "$STATUS" = "off" ] && set +o shwordsplit
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$(command -v recode)" ]; then
|
||||||
|
echo "recode needed."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
## Convert all 'bad' encoding to UTF-8/LF. WARNING: It will fail for encodings
|
||||||
|
## other the one explicitly supported below.
|
||||||
|
while read -r i; do
|
||||||
|
CODING=$(file "$i")
|
||||||
|
|
||||||
|
if [ -n "$(echo $CODING | grep 'ISO-8859')" ]; then
|
||||||
|
echo "ISO-8859: [$i]"
|
||||||
|
recode latin1..utf-8 "$i"
|
||||||
|
|
||||||
|
elif [ -n "$(echo $CODING | grep 'Non-ISO extended-ASCII')" ]; then
|
||||||
|
echo "cp1252: [$i]"
|
||||||
|
recode cp1252..utf-8 "$i"
|
||||||
|
|
||||||
|
elif [ -n "$(echo $CODING | grep 'UTF-16 Unicode text')" ]; then
|
||||||
|
echo "UTF-16: [$i]"
|
||||||
|
recode utf-16..utf-8 "$i"
|
||||||
|
|
||||||
|
elif [ -n "$(echo $CODING | grep 'UTF-8 Unicode (with BOM)')" ]; then
|
||||||
|
echo "UTF-8 BOM: [$i]"
|
||||||
|
sed -i '1s/^.//' "$i"
|
||||||
|
## sed -i is not the fastest depending on the implementations. The
|
||||||
|
## following commands work, but may be overkill.
|
||||||
|
# dd iflag=skip_bytes skip=3 if=file.srt of=temp.srt
|
||||||
|
# dd bs=1 skip=3 if=file.srt of=temp.srt
|
||||||
|
# tail -c +32 file.srt > temp.srt
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$(echo $CODING | grep 'CRLF')" ]; then
|
||||||
|
echo "CRLF: [$i]"
|
||||||
|
sed -i 's/\r//g' "$i"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done <<EOF
|
||||||
|
$(find . -type f -size -50M -print)
|
||||||
|
EOF
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
while read -r i; do
|
||||||
|
echo -n "# "
|
||||||
|
file "$i"
|
||||||
|
grep -m1 "\`" "$i"
|
||||||
|
grep -m1 "oe" "$i"
|
||||||
|
done <<EOF
|
||||||
|
$(find . -type f -size -50M -print)
|
||||||
|
EOF
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$(command -v ffmpeg)" ]; then
|
||||||
|
echo "ffmpeg required."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Usage: ${0##*/} VIDEO"
|
||||||
|
fi
|
||||||
|
|
||||||
|
## TODO: getopt support for audio quality.
|
||||||
|
TC_VIDEO_AUDIO_QUAL=192k
|
||||||
|
|
||||||
|
echo "================================================================================"
|
||||||
|
ffmpeg -i "$1" -vn -c:a libvorbis -b:a $TC_VIDEO_AUDIO_QUAL "${1%.*}-$(date '+%F-%H%M%S').ogg"
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$(command -v ffmpeg)" ]; then
|
||||||
|
echo "ffmpeg required."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $# -ne 2 ]; then
|
||||||
|
echo "Usage: ${0##*/} VIDEO AUDIO_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ffmpeg -i "$1" -vn -sn -c:a copy -map 0:1 "$2"
|
|
@ -0,0 +1,158 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## User options
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
## To check what fmmpeg supports:
|
||||||
|
# ffmpeg -codecs
|
||||||
|
# ffmpeg -formats
|
||||||
|
|
||||||
|
## Useful guides
|
||||||
|
## x264: http://ffmpeg.org/trac/ffmpeg/wiki/x264EncodingGuide
|
||||||
|
## ID3 details: http://en.wikipedia.org/wiki/ID3
|
||||||
|
|
||||||
|
TC_VIDEO_AUDIO_QUAL=192k
|
||||||
|
|
||||||
|
## quality*size ~= 1/speed
|
||||||
|
# ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo
|
||||||
|
TC_VIDEO_PRESET=slow
|
||||||
|
|
||||||
|
## Overall quality. Logarithmic scale.
|
||||||
|
# 18 is near perfection.
|
||||||
|
# 22 is really good compression, while a bit more blurry than the original.
|
||||||
|
# 20 is a good compromise.
|
||||||
|
TC_VIDEO_QUAL=20
|
||||||
|
|
||||||
|
## x264 tuning (presets).
|
||||||
|
## Possible values: film, animation, grain, ...
|
||||||
|
## See x264 --fullhelp.
|
||||||
|
## No tuning by default.
|
||||||
|
# TC_VIDEO_TUNE="-tune film"
|
||||||
|
TC_VIDEO_TUNE=""
|
||||||
|
|
||||||
|
## x264 options.
|
||||||
|
## Possible values: hex, umh...
|
||||||
|
## me=umh is default.
|
||||||
|
# TC_VIDEO_OPTS="-x264opts me=hex"
|
||||||
|
TC_VIDEO_OPTS=""
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## End of user options
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
## TODO: audio quality. Copy if AAC or OGG <320k.
|
||||||
|
## TODO: chapters support. Already OK?
|
||||||
|
## TODO: crop options.
|
||||||
|
## TODO: custom mapping.
|
||||||
|
## TODO: handle srt encoding.
|
||||||
|
## TODO: option to replace original file.
|
||||||
|
## TODO: workout -threads option.
|
||||||
|
## TODO: separate functions:
|
||||||
|
## tc_video_batch
|
||||||
|
## tc_video_transcode
|
||||||
|
|
||||||
|
if [ -z "$(command -v ffmpeg)" ]; then
|
||||||
|
echo "ffmpeg required."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
_printhelp()
|
||||||
|
{
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $1 [OPTIONS] FILES|FOLDERS
|
||||||
|
|
||||||
|
Transcode FILES or files found in FOLDERS to .mkv with x264 and ogg. Output
|
||||||
|
files are the same as the original, with time appended. You can customize
|
||||||
|
encoding with the TC_* variables.
|
||||||
|
|
||||||
|
-f: Overwrite existing file if any.
|
||||||
|
-h: Display this help.
|
||||||
|
-s: Sample of ten minutes.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
## What to do if file exists:
|
||||||
|
# -y overwrite
|
||||||
|
# -n do not overwrite.
|
||||||
|
TC_OVERWRITE="-n"
|
||||||
|
|
||||||
|
TC_SAMPLE=""
|
||||||
|
|
||||||
|
while getopts ":fhs" opt; do
|
||||||
|
case $opt in
|
||||||
|
f)
|
||||||
|
TC_OVERWRITE="-y"
|
||||||
|
;;
|
||||||
|
h)
|
||||||
|
_printhelp "$0"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
s)
|
||||||
|
TC_SAMPLE="-ss 60 -t 600"
|
||||||
|
;;
|
||||||
|
?)
|
||||||
|
_printhelp "$0"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
:)
|
||||||
|
echo "Missing argument."
|
||||||
|
echo "Use $0 -h for help."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
shift $(($OPTIND - 1))
|
||||||
|
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
tc_transcode .
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Zsh compatibility. We need it otherwise word splitting of parameter like
|
||||||
|
## TC_SAMPLE will not work.
|
||||||
|
STATUS="$(set -o | grep 'shwordsplit' | awk '{print $2}')"
|
||||||
|
[ "$STATUS" = "off" ] && set -o shwordsplit
|
||||||
|
|
||||||
|
_tc_transcode()
|
||||||
|
{
|
||||||
|
echo "================================================================================"
|
||||||
|
ffmpeg $TC_OVERWRITE $TC_SAMPLE -i "$1" \
|
||||||
|
-c:v libx264 -preset $TC_VIDEO_PRESET -crf $TC_VIDEO_QUAL $TC_VIDEO_TUNE $TC_VIDEO_OPTS \
|
||||||
|
-c:a libvorbis -b:a $TC_VIDEO_AUDIO_QUAL \
|
||||||
|
-c:s copy \
|
||||||
|
-map 0 "${1%.*}-$(date '+%F-%H%M%S').mkv"
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in "$@"; do
|
||||||
|
|
||||||
|
## Argument is a folder. We search for all video files in there.
|
||||||
|
if [ -d "$i" ]; then
|
||||||
|
|
||||||
|
## TODO: provide max-depth as option.
|
||||||
|
while read -r j; do
|
||||||
|
_tc_transcode $j
|
||||||
|
done <<EOF
|
||||||
|
$(find "$i" \( \
|
||||||
|
-iname '*.mkv' -o \
|
||||||
|
-iname '*.mp4' -o \
|
||||||
|
-iname '*.avi' -o \
|
||||||
|
-iname '*.webm' -o \
|
||||||
|
-iname '*.flv' -o \
|
||||||
|
-iname '*.wmv' -o \
|
||||||
|
-iname '*.mpg' \) )
|
||||||
|
EOF
|
||||||
|
|
||||||
|
## Argument is a regular file.
|
||||||
|
else
|
||||||
|
_tc_transcode "$i"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
## Restore Zsh previous options. This will not turn off shwordsplit if it
|
||||||
|
## was on before calling the function.
|
||||||
|
[ "$STATUS" = "off" ] && set +o shwordsplit
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
## -*- mode:sh -*- #
|
|
||||||
|
|
||||||
alias pa='sudo pkg_add -r'
|
|
||||||
# alias pd='sudo pkg_delete'
|
|
||||||
alias pd='sudo pkg_deinstall -cd'
|
|
||||||
alias pi='pkg_info -RrcdsD -l "::"'
|
|
||||||
alias pil='pkg_info -L'
|
|
||||||
|
|
||||||
## Origin of file
|
|
||||||
alias piw='pkg_info -W'
|
|
||||||
|
|
||||||
## Search
|
|
||||||
alias pis='pkg_info -Qox'
|
|
||||||
# alias pvs='pkg_version -Is'
|
|
||||||
alias psr='pkg_search -r'
|
|
||||||
|
|
||||||
## Out-of-date packages.
|
|
||||||
alias pv='pkg_version -Il "<"'
|
|
||||||
|
|
||||||
## Port tree
|
|
||||||
alias mi='sudo make install'
|
|
||||||
alias mid='sudo make install distclean'
|
|
||||||
alias mc='sudo make config'
|
|
|
@ -1,26 +0,0 @@
|
||||||
## -*- mode:sh -*- #
|
|
||||||
|
|
||||||
PACMAN_FRONTEND='pacman'
|
|
||||||
|
|
||||||
## Use of pacman-color
|
|
||||||
if [ -e "/usr/bin/pacman-color" ]; then
|
|
||||||
PACMAN_FRONTEND='pacman-color'
|
|
||||||
|
|
||||||
if [ "zsh" = "$SHELL_CURRENT" ]; then
|
|
||||||
compdef _pacman pacman-color
|
|
||||||
elif [ "bash" = "$SHELL_CURRENT" ]; then
|
|
||||||
complete -o default -o nospace -F _pacman pacman-color
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
## The eval trick lets us define the alias after the variable has been expanded.
|
|
||||||
## Bash will not auto-complete aliases.
|
|
||||||
eval 'alias pc="sudo $PACMAN_FRONTEND -Sc"'
|
|
||||||
eval 'alias pi="sudo $PACMAN_FRONTEND -S --needed"'
|
|
||||||
eval 'alias pqi="$PACMAN_FRONTEND -Qi"'
|
|
||||||
eval 'alias pqo="$PACMAN_FRONTEND -Qo"'
|
|
||||||
eval 'alias pqs="$PACMAN_FRONTEND -Qs"'
|
|
||||||
eval 'alias pr="sudo $PACMAN_FRONTEND -Rs"'
|
|
||||||
eval 'alias psi="$PACMAN_FRONTEND -Si"'
|
|
||||||
eval 'alias pss="$PACMAN_FRONTEND -Ss"'
|
|
||||||
eval 'alias pu="sudo $PACMAN_FRONTEND -Syu"'
|
|
|
@ -4,10 +4,6 @@
|
||||||
## Date 2013-01-04
|
## Date 2013-01-04
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
##==============================================================================
|
|
||||||
## General
|
|
||||||
##==============================================================================
|
|
||||||
|
|
||||||
## Warning: if this does not work if you change shell without relogging, use
|
## Warning: if this does not work if you change shell without relogging, use
|
||||||
## 'source .<shell>rc' instead.
|
## 'source .<shell>rc' instead.
|
||||||
|
|
||||||
|
@ -21,6 +17,7 @@ if [ "zsh" = "$SHELL_CURRENT" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
##==============================================================================
|
##==============================================================================
|
||||||
|
## General
|
||||||
##==============================================================================
|
##==============================================================================
|
||||||
|
|
||||||
## 'ls' redefinition
|
## 'ls' redefinition
|
||||||
|
@ -94,15 +91,6 @@ alias xmorphzi='xrandr --output $(xrandr | grep "\<connected\>" | cut -f1 -d" ")
|
||||||
|
|
||||||
alias xres='xrandr -s $(xrandr | awk '"'"'/^ / {print $1;exit}'"'"')'
|
alias xres='xrandr -s $(xrandr | awk '"'"'/^ / {print $1;exit}'"'"')'
|
||||||
|
|
||||||
##==============================================================================
|
|
||||||
## Emacs
|
|
||||||
##==============================================================================
|
|
||||||
# See .homeinit
|
|
||||||
if [ -f /bin/em ]; then
|
|
||||||
alias ema='emacs -q -l ~/.emacs-light'
|
|
||||||
alias emacs-reload="emacsclient -e '(kill-emacs)' >/dev/null 2>&1; emacs --daemon"
|
|
||||||
fi
|
|
||||||
|
|
||||||
##==============================================================================
|
##==============================================================================
|
||||||
## Zsh power aliases
|
## Zsh power aliases
|
||||||
##==============================================================================
|
##==============================================================================
|
||||||
|
@ -117,15 +105,71 @@ fi
|
||||||
|
|
||||||
# fi
|
# fi
|
||||||
|
|
||||||
|
##==============================================================================
|
||||||
|
## Emacs
|
||||||
|
##==============================================================================
|
||||||
|
# See .homeinit
|
||||||
|
if [ -f /bin/em ]; then
|
||||||
|
alias ema='emacs -q -l ~/.emacs-light'
|
||||||
|
alias emacs-reload="emacsclient -e '(kill-emacs)' >/dev/null 2>&1; emacs --daemon"
|
||||||
|
fi
|
||||||
|
|
||||||
##==============================================================================
|
##==============================================================================
|
||||||
## Pacman aliases
|
## Pacman aliases
|
||||||
##==============================================================================
|
##==============================================================================
|
||||||
[ -e "/usr/bin/pacman" ] && . ~/.shell.d/alias_pacman
|
if [ -e "/usr/bin/pacman" ]; then
|
||||||
|
PACMAN_FRONTEND='pacman'
|
||||||
|
|
||||||
|
## Use of pacman-color
|
||||||
|
if [ -e "/usr/bin/pacman-color" ]; then
|
||||||
|
PACMAN_FRONTEND='pacman-color'
|
||||||
|
|
||||||
|
if [ "zsh" = "$SHELL_CURRENT" ]; then
|
||||||
|
compdef _pacman pacman-color
|
||||||
|
elif [ "bash" = "$SHELL_CURRENT" ]; then
|
||||||
|
complete -o default -o nospace -F _pacman pacman-color
|
||||||
|
fi
|
||||||
|
|
||||||
|
## The eval trick lets us define the alias after the variable has been expanded.
|
||||||
|
## Bash will not auto-complete aliases.
|
||||||
|
eval 'alias pc="sudo $PACMAN_FRONTEND -Sc"'
|
||||||
|
eval 'alias pi="sudo $PACMAN_FRONTEND -S --needed"'
|
||||||
|
eval 'alias pqi="$PACMAN_FRONTEND -Qi"'
|
||||||
|
eval 'alias pqo="$PACMAN_FRONTEND -Qo"'
|
||||||
|
eval 'alias pqs="$PACMAN_FRONTEND -Qs"'
|
||||||
|
eval 'alias pr="sudo $PACMAN_FRONTEND -Rs"'
|
||||||
|
eval 'alias psi="$PACMAN_FRONTEND -Si"'
|
||||||
|
eval 'alias pss="$PACMAN_FRONTEND -Ss"'
|
||||||
|
eval 'alias pu="sudo $PACMAN_FRONTEND -Syu"'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
##==============================================================================
|
##==============================================================================
|
||||||
## FreeBSD aliases
|
## FreeBSD aliases
|
||||||
##==============================================================================
|
##==============================================================================
|
||||||
[ "$(uname)" = "FreeBSD" ] && . ~/.shell.d/alias_freebsd
|
if [ "$(uname)" = "FreeBSD" ]; then
|
||||||
|
alias pa='sudo pkg_add -r'
|
||||||
|
# alias pd='sudo pkg_delete'
|
||||||
|
alias pd='sudo pkg_deinstall -cd'
|
||||||
|
alias pi='pkg_info -RrcdsD -l "::"'
|
||||||
|
alias pil='pkg_info -L'
|
||||||
|
|
||||||
|
## Origin of file
|
||||||
|
alias piw='pkg_info -W'
|
||||||
|
|
||||||
|
## Search
|
||||||
|
alias pis='pkg_info -Qox'
|
||||||
|
# alias pvs='pkg_version -Is'
|
||||||
|
alias psr='pkg_search -r'
|
||||||
|
|
||||||
|
## Out-of-date packages.
|
||||||
|
alias pv='pkg_version -Il "<"'
|
||||||
|
|
||||||
|
## Port tree
|
||||||
|
alias mi='sudo make install'
|
||||||
|
alias mid='sudo make install distclean'
|
||||||
|
alias mc='sudo make config'
|
||||||
|
fi
|
||||||
|
|
||||||
##==============================================================================
|
##==============================================================================
|
||||||
## TeXlive aliases
|
## TeXlive aliases
|
||||||
|
|
|
@ -965,8 +965,3 @@ if [ -d "/usr/local/texlive" ]; then
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
##==============================================================================
|
|
||||||
## Transcoding functions
|
|
||||||
##==============================================================================
|
|
||||||
|
|
||||||
[ -n "$(command -v ffmpeg)" ] && . "${SHELL_DIR}/funs_transcode"
|
|
||||||
|
|
|
@ -1,694 +0,0 @@
|
||||||
## -*- mode:sh -*- #
|
|
||||||
################################################################################
|
|
||||||
## Shell -- Trancode function set.
|
|
||||||
## Date 2013-01-27
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
## Tool list: ffmpeg, recode, titlecase.awk
|
|
||||||
|
|
||||||
## Support:
|
|
||||||
# ffmpeg -codecs
|
|
||||||
# ffmpeg -formats
|
|
||||||
|
|
||||||
## Useful guides
|
|
||||||
## x264: http://ffmpeg.org/trac/ffmpeg/wiki/x264EncodingGuide
|
|
||||||
## ID3 details: http://en.wikipedia.org/wiki/ID3
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
## Text
|
|
||||||
|
|
||||||
tc_text_scan ()
|
|
||||||
{
|
|
||||||
while read -r i; do
|
|
||||||
echo -n "# "
|
|
||||||
file "$i"
|
|
||||||
grep -m1 "\`" "$i"
|
|
||||||
grep -m1 "oe" "$i"
|
|
||||||
done <<EOF
|
|
||||||
$(find . -type f -size -50M -print)
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
## Confert all 'bad' encoding to UTF-8/LF.
|
|
||||||
## WARNING: It will fail for encoding other than ISO-8859-1 and cp1252.
|
|
||||||
tc_text_2utf8 ()
|
|
||||||
{
|
|
||||||
local CODING
|
|
||||||
|
|
||||||
while read -r i; do
|
|
||||||
CODING=$(file "$i")
|
|
||||||
|
|
||||||
if [ -n "$(echo $CODING | grep 'ISO-8859')" ]; then
|
|
||||||
echo "ISO-8859: [$i]"
|
|
||||||
recode latin1..utf-8 "$i"
|
|
||||||
|
|
||||||
elif [ -n "$(echo $CODING | grep 'Non-ISO extended-ASCII')" ]; then
|
|
||||||
echo "cp1252: [$i]"
|
|
||||||
recode cp1252..utf-8 "$i"
|
|
||||||
|
|
||||||
elif [ -n "$(echo $CODING | grep 'UTF-16 Unicode text')" ]; then
|
|
||||||
echo "UTF-16: [$i]"
|
|
||||||
recode utf-16..utf-8 "$i"
|
|
||||||
|
|
||||||
elif [ -n "$(echo $CODING | grep 'UTF-8 Unicode (with BOM)')" ]; then
|
|
||||||
echo "UTF-8 BOM: [$i]"
|
|
||||||
sed -i '1s/^.//' "$i"
|
|
||||||
## The following commands work, but may be overkill.
|
|
||||||
# dd iflag=skip_bytes skip=3 if=file.srt of=temp.srt
|
|
||||||
# dd bs=1 skip=3 if=file.srt of=temp.srt
|
|
||||||
# tail -c +32 file.srt > temp.srt
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$(echo $CODING | grep 'CRLF')" ]; then
|
|
||||||
echo "CRLF: [$i]"
|
|
||||||
sed -i 's/\r//g' "$i"
|
|
||||||
fi
|
|
||||||
|
|
||||||
done <<EOF
|
|
||||||
$(find . -type f -size -50M -print)
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
## Audio
|
|
||||||
|
|
||||||
## For the sake of simplicity we convert everything to OGG.
|
|
||||||
## You can easily set output folder to current folder with TC_AUDIO_DEST=".".
|
|
||||||
TC_AUDIO_DEST="/media/data1/Musics/"
|
|
||||||
|
|
||||||
## OGG quality ranges from -1 to 10.
|
|
||||||
## -q-1 45 kbit/s
|
|
||||||
## -q0 64 kbit/s
|
|
||||||
## -q1 80 kbit/s
|
|
||||||
## -q2 96 kbit/s
|
|
||||||
## -q3 112 kbit/s
|
|
||||||
## -q4 128 kbit/s
|
|
||||||
## -q5 160 kbit/s
|
|
||||||
## -q6 192 kbit/s
|
|
||||||
## -q7 224 kbit/s
|
|
||||||
## -q8 256 kbit/s
|
|
||||||
## -q9 320 kbit/s
|
|
||||||
## -q10 500 kbit/s
|
|
||||||
|
|
||||||
## Artists: we use same value for artist and album_artist.
|
|
||||||
## Genres: since this is not universal by nature, we do not put a genre in tags.
|
|
||||||
## Composer: not universal neither, we prefer ARTIST over COMPOSER, so COMPOSER will be empty.
|
|
||||||
|
|
||||||
tc_audio_scan()
|
|
||||||
{
|
|
||||||
local BUFFER
|
|
||||||
|
|
||||||
while read -r i; do
|
|
||||||
BUFFER="$(mediainfo "$i")"
|
|
||||||
if [ -n "$(echo "$BUFFER" | grep "Bit rate mode *: Constant")" ]; then
|
|
||||||
echo "$i : CBR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$(echo "$BUFFER" | grep "Cover *: Yes")" ]; then
|
|
||||||
echo "$i : Cover"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$(echo "$BUFFER" | grep "Track name/Position *: 0+")" ]; then
|
|
||||||
echo "$i : Leading zeros"
|
|
||||||
fi
|
|
||||||
done <<EOF
|
|
||||||
$(find "." \( \
|
|
||||||
-iname '*.mp3' -o \
|
|
||||||
-iname '*.flac' -o \
|
|
||||||
-iname '*.wv' -o \
|
|
||||||
-iname '*.aac' -o \
|
|
||||||
-iname '*.wav' -o \
|
|
||||||
-iname '*.ape' -o \
|
|
||||||
-iname '*.mpc' \) )
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
## This will convert all tracks in current folder and subfolders.
|
|
||||||
tc_audio_batch()
|
|
||||||
{
|
|
||||||
while read -r i; do
|
|
||||||
tc_audio_transcode "$@" $i
|
|
||||||
done <<EOF
|
|
||||||
$(find "." \( \
|
|
||||||
-iname '*.mp3' -o \
|
|
||||||
-iname '*.flac' -o \
|
|
||||||
-iname '*.wv' -o \
|
|
||||||
-iname '*.aac' -o \
|
|
||||||
-iname '*.wav' -o \
|
|
||||||
-iname '*.ape' -o \
|
|
||||||
-iname '*.mpc' \) )
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
## This function is supposed to work on a per-album basis. Folders will not be
|
|
||||||
## accepted as argument. This function will work best if all tracks of a folder
|
|
||||||
## belong to the same album. Theres is no way to handle covers raliably, so you
|
|
||||||
## should leave the only covers you want to keep in the working folder.
|
|
||||||
|
|
||||||
## TODO: cover resolution check. Should be > 100x100. Above <1000x1000 should give a warning.
|
|
||||||
## TODO: extract cover from tags if checksum does not match any from current folder.
|
|
||||||
tc_audio_transcode()
|
|
||||||
{
|
|
||||||
_printhelp ()
|
|
||||||
{
|
|
||||||
cat <<EOF
|
|
||||||
Usage: $1 [OPTIONS] FILE
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-p : preview (do not change file)
|
|
||||||
-s : skip encoding
|
|
||||||
|
|
||||||
Tags:
|
|
||||||
-a : artist
|
|
||||||
-b : bitrate
|
|
||||||
-d : date
|
|
||||||
-g : genre
|
|
||||||
-l : album
|
|
||||||
-n : track number
|
|
||||||
-t : title
|
|
||||||
|
|
||||||
You can use the following variables to refer to existing tags:
|
|
||||||
\$ALBUM
|
|
||||||
\$ALBUM_ARTIST
|
|
||||||
\$ARTIST
|
|
||||||
\$COMPOSER
|
|
||||||
\$DATE
|
|
||||||
\$TYER
|
|
||||||
\$GENRE
|
|
||||||
\$TRACK
|
|
||||||
|
|
||||||
Default output folder:
|
|
||||||
OUTPUT="\$TC_AUDIO_DEST/\$OUTPUT_ARTIST/\${OUTPUT_ALBUM:+\${OUTPUT_DATE:+\$OUTPUT_DATE - }\$OUTPUT_ALBUM/}"
|
|
||||||
|
|
||||||
Default output file:
|
|
||||||
OUTPUT_FILE="\$OUTPUT\$OUTPUT_ARTIST - \${OUTPUT_TRACK:+\$OUTPUT_TRACK - }\$OUTPUT_TITLE.\$OUTPUT_FORMAT"
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
Set the 'artist' tag and reencode:
|
|
||||||
$1 -a 'Franz Lizst' file.mp3
|
|
||||||
|
|
||||||
Set 'artist' to be 'composer', and 'title' to be preceeded by 'artist', do not reencode:
|
|
||||||
$1 -s -a '\$COMPOSER' -t '\$ARTIST - \$TITLE' file.ogg
|
|
||||||
|
|
||||||
IMPORTANT: you *must* use single quotes when using variables.
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
## OPTIONS
|
|
||||||
local SKIP
|
|
||||||
SKIP=0
|
|
||||||
|
|
||||||
local PREVIEW
|
|
||||||
PREVIEW=0
|
|
||||||
|
|
||||||
## TAGS
|
|
||||||
local OUTPUT_ALBUM
|
|
||||||
OUTPUT_ALBUM='$ALBUM'
|
|
||||||
local OUTPUT_ARTIST
|
|
||||||
OUTPUT_ARTIST='$ARTIST'
|
|
||||||
local OUTPUT_DATE
|
|
||||||
OUTPUT_DATE='$DATE'
|
|
||||||
local OUTPUT_GENRE
|
|
||||||
OUTPUT_GENRE='$GENRE'
|
|
||||||
local OUTPUT_TITLE
|
|
||||||
OUTPUT_TITLE='$TITLE'
|
|
||||||
local OUTPUT_TRACK
|
|
||||||
OUTPUT_TRACK='$TRACK'
|
|
||||||
|
|
||||||
## PROPERTIES
|
|
||||||
local OUTPUT_BITRATE
|
|
||||||
OUTPUT_BITRATE=0
|
|
||||||
|
|
||||||
## Non-cli-option data. Modifying these imply modifications in code below.
|
|
||||||
local OUTPUT_FORMAT
|
|
||||||
OUTPUT_FORMAT="ogg"
|
|
||||||
local OGG_PARAM
|
|
||||||
OGG_PARAM="-c:a libvorbis -b:a ${OUTPUT_BITRATE}k"
|
|
||||||
|
|
||||||
## These ones are not cli-options either, but this could be easily changed.
|
|
||||||
local OUTPUT
|
|
||||||
local OUTPUT_FILE
|
|
||||||
OUTPUT='$TC_AUDIO_DEST/$OUTPUT_ARTIST/${OUTPUT_ALBUM:+${OUTPUT_DATE:+$OUTPUT_DATE - }$OUTPUT_ALBUM/}'
|
|
||||||
OUTPUT_FILE='$OUTPUT$OUTPUT_ARTIST - ${OUTPUT_PADDEDTRACK:+$OUTPUT_PADDEDTRACK - }$OUTPUT_TITLE'
|
|
||||||
|
|
||||||
while getopts ":a:b:d:g:l:n:t:hps" opt; do
|
|
||||||
case $opt in
|
|
||||||
|
|
||||||
a) OUTPUT_ARTIST=$OPTARG ;;
|
|
||||||
b) OUTPUT_BITRATE=$OPTARG ;;
|
|
||||||
d) OUTPUT_DATE=$OPTARG ;;
|
|
||||||
g) OUTPUT_GENRE=$OPTARG ;;
|
|
||||||
l) OUTPUT_ALBUM=$OPTARG ;;
|
|
||||||
n) OUTPUT_TRACK=$OPTARG ;;
|
|
||||||
t) OUTPUT_TITLE=$OPTARG ;;
|
|
||||||
|
|
||||||
h)
|
|
||||||
_printhelp "$0"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
p)
|
|
||||||
PREVIEW=1
|
|
||||||
;;
|
|
||||||
|
|
||||||
s)
|
|
||||||
SKIP=1
|
|
||||||
;;
|
|
||||||
|
|
||||||
?)
|
|
||||||
_printhelp "$0"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
:)
|
|
||||||
echo "Missing argument."
|
|
||||||
echo "Use $0 -h for help."
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
shift $(($OPTIND - 1))
|
|
||||||
if [ $# -eq 0 ]; then
|
|
||||||
_printhelp "$0"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
##================================================================================
|
|
||||||
## Get metadata.
|
|
||||||
local METADATA
|
|
||||||
local STREAM
|
|
||||||
STREAM=$(ffmpeg -i "$1" 2>&1)
|
|
||||||
METADATA=$(echo "$STREAM" | sed -n '/Metadata/ ! d; /Metada/{b cont}; :cont ; {n;p;b cont}')
|
|
||||||
STREAM=$(echo "$STREAM" | grep "Stream")
|
|
||||||
|
|
||||||
if [ -z "$STREAM" ]; then
|
|
||||||
echo "Non-audio file."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
## We get format from extension, because codec is not reliable either.
|
|
||||||
local FORMAT
|
|
||||||
FORMAT="${1##*.}"
|
|
||||||
## CODEC is unused for now.
|
|
||||||
local CODEC
|
|
||||||
CODEC=$(echo "$STREAM" | sed -n '/Audio:/ {s/.*Audio: \([^,]*\),.*/\1/;p}')
|
|
||||||
local BITRATE
|
|
||||||
BITRATE=$(echo "$STREAM" | sed -n '/kb\/s/ {s/.*, \(.*\) kb\/s/\1/;p}')
|
|
||||||
|
|
||||||
if [ -z "$FORMAT" ]; then
|
|
||||||
echo "ERROR: non-audio file."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
_metadata_filter()
|
|
||||||
{
|
|
||||||
echo "$METADATA" | grep -i "\<$1\>" | sed 's/[^:]* : //g'
|
|
||||||
}
|
|
||||||
|
|
||||||
local TITLE
|
|
||||||
TITLE=$(_metadata_filter "title")
|
|
||||||
local ARTIST
|
|
||||||
ARTIST=$(_metadata_filter "artist")
|
|
||||||
local ALBUM
|
|
||||||
ALBUM=$(_metadata_filter "album")
|
|
||||||
local ALBUM_ARTIST
|
|
||||||
ALBUM_ARTIST=$(_metadata_filter "album_artist")
|
|
||||||
local COMPOSER
|
|
||||||
COMPOSER=$(_metadata_filter "composer")
|
|
||||||
local DISC
|
|
||||||
DISC=$(_metadata_filter "disc")
|
|
||||||
local GENRE
|
|
||||||
GENRE=$(_metadata_filter "genre")
|
|
||||||
|
|
||||||
local TRACK
|
|
||||||
TRACK=$(_metadata_filter "track")
|
|
||||||
local DATE
|
|
||||||
DATE=$(_metadata_filter "date")
|
|
||||||
local TYER
|
|
||||||
TYER=$(_metadata_filter "TYER")
|
|
||||||
|
|
||||||
##================================================================================
|
|
||||||
## OUTPUT variables.
|
|
||||||
|
|
||||||
_string_cleanser()
|
|
||||||
{
|
|
||||||
## We use the AWK script to set title case. The script contains
|
|
||||||
## exceptions that can be configured. We fix some chars with sed.
|
|
||||||
|
|
||||||
# ’ => '
|
|
||||||
# : => -
|
|
||||||
# / => -
|
|
||||||
|
|
||||||
awk -f "$HOME/.shell.d/titlecase.awk" \
|
|
||||||
<(echo "$@" | sed -n -e "s/’/'/g ; s/ *: */ - /g ; s| */ *| - |g; p; q")
|
|
||||||
}
|
|
||||||
|
|
||||||
OUTPUT_TITLE=$(eval _string_cleanser $OUTPUT_TITLE)
|
|
||||||
OUTPUT_TITLE="${OUTPUT_TITLE:-Unknown Title}"
|
|
||||||
|
|
||||||
## We use album artist if artist is empty.
|
|
||||||
OUTPUT_ARTIST=$(eval _string_cleanser $OUTPUT_ARTIST)
|
|
||||||
[ -z "$OUTPUT_ARTIST" ] && [ -n "$ALBUM_ARTIST" ] && OUTPUT_ARTIST="$ALBUM_ARTIST"
|
|
||||||
OUTPUT_ARTIST="${OUTPUT_ARTIST:-Unknown Artist}"
|
|
||||||
|
|
||||||
OUTPUT_ALBUM=$(eval _string_cleanser $OUTPUT_ALBUM)
|
|
||||||
[ -z "$OUTPUT_ALBUM" ] && echo "${#OUTPUT_ALBUM}${OUTPUT_ALBUM}"
|
|
||||||
|
|
||||||
## We put genre in lower case and underscore to ease matching. If it
|
|
||||||
## matches, we use the Title Case match. If it does not, we set it to empty.
|
|
||||||
OUTPUT_GENRE=$(eval _string_cleanser $OUTPUT_GENRE | tr '[:upper:] ' '[:lower:]_')
|
|
||||||
case $OUTPUT_GENRE in
|
|
||||||
ost) OUTPUT_GENRE="Soundtrack" ;;
|
|
||||||
soundtrack) OUTPUT_GENRE="Soundtrack";;
|
|
||||||
original_soundtrack) OUTPUT_GENRE="Soundtrack";;
|
|
||||||
classical) OUTPUT_GENRE="Classical";;
|
|
||||||
classics) OUTPUT_GENRE="Classical";;
|
|
||||||
classic) OUTPUT_GENRE="Classical";;
|
|
||||||
*) OUTPUT_GENRE="";;
|
|
||||||
esac
|
|
||||||
|
|
||||||
## We remove the track count if any, we suppress leading zeros.
|
|
||||||
OUTPUT_TRACK=$(eval _string_cleanser $OUTPUT_TRACK | sed -e 's/^0*//' -e 's|/.*||')
|
|
||||||
|
|
||||||
## If DATE is not a year, we use TYER if it is a year.
|
|
||||||
OUTPUT_DATE=$(eval _string_cleanser $OUTPUT_DATE)
|
|
||||||
local TYER_REG=$(echo "$TYER" | sed -n 's/.*\([[:digit:]]\{4\}\).*/\1/p')
|
|
||||||
if [ ${#DATE} -ne 4 ] && [ ${#TYER_REG} -eq 4 ]; then
|
|
||||||
OUTPUT_DATE="$TYER_REG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
## QUALITY
|
|
||||||
## If bitrate argumument is not provided, we use BITRATE value.
|
|
||||||
[ $OUTPUT_BITRATE -eq 0 ] && OUTPUT_BITRATE=${BITRATE}
|
|
||||||
## TODO: test if table is useful at all.
|
|
||||||
# if [ $OUTPUT_BITRATE -eq 0 ]; then
|
|
||||||
# [ $BITRATE -le 45 ] && OUTPUT_BITRATE=45k
|
|
||||||
# [ $BITRATE -gt 45 ] && OUTPUT_BITRATE=64k
|
|
||||||
# [ $BITRATE -gt 64 ] && OUTPUT_BITRATE=80k
|
|
||||||
# [ $BITRATE -gt 80 ] && OUTPUT_BITRATE=96k
|
|
||||||
# [ $BITRATE -gt 96 ] && OUTPUT_BITRATE=112k
|
|
||||||
# [ $BITRATE -gt 112 ] && OUTPUT_BITRATE=128k
|
|
||||||
# [ $BITRATE -gt 128 ] && OUTPUT_BITRATE=160k
|
|
||||||
# [ $BITRATE -gt 160 ] && OUTPUT_BITRATE=192k
|
|
||||||
# [ $BITRATE -gt 192 ] && OUTPUT_BITRATE=224k
|
|
||||||
# [ $BITRATE -gt 224 ] && OUTPUT_BITRATE=256k
|
|
||||||
# [ $BITRATE -gt 256 ] && OUTPUT_BITRATE=320k
|
|
||||||
# [ $BITRATE -gt 320 ] && OUTPUT_BITRATE=500k
|
|
||||||
# fi
|
|
||||||
|
|
||||||
## Only reencode if not in OGG and if SKIP not set.
|
|
||||||
OGG_PARAM="-c:a libvorbis -b:a ${OUTPUT_BITRATE}k"
|
|
||||||
[ $SKIP -ne 0 ] && OGG_PARAM="-c:a copy" && OUTPUT_FORMAT="$FORMAT"
|
|
||||||
[ "$FORMAT" = "ogg" ] && OGG_PARAM="-c:a copy"
|
|
||||||
|
|
||||||
## Make sure track number has two digits for file name only.
|
|
||||||
local OUTPUT_PADDEDTRACK
|
|
||||||
OUTPUT_PADDEDTRACK=$OUTPUT_TRACK
|
|
||||||
if [ -n "$OUTPUT_PADDEDTRACK" ]; then
|
|
||||||
[ ${OUTPUT_PADDEDTRACK} -lt 10 ] && OUTPUT_PADDEDTRACK="0$OUTPUT_PADDEDTRACK"
|
|
||||||
fi
|
|
||||||
|
|
||||||
OUTPUT=$(eval echo $OUTPUT)
|
|
||||||
OUTPUT_FILE=$(eval echo $OUTPUT_FILE)
|
|
||||||
|
|
||||||
## IF file exist, we append a uniq timestamp to the name.
|
|
||||||
[ -e "${OUTPUT_FILE}.$OUTPUT_FORMAT" ] && OUTPUT_FILE="$OUTPUT_FILE-$(date '+%F-%H%M%S')"
|
|
||||||
|
|
||||||
##==============================================================================
|
|
||||||
## PREVIEW
|
|
||||||
|
|
||||||
echo "==> BEFORE"
|
|
||||||
echo "ALBUM [$ALBUM]"
|
|
||||||
echo "ALBUM_ARTIST [$ALBUM_ARTIST]"
|
|
||||||
echo "ARTIST [$ARTIST]"
|
|
||||||
echo "BITRATE [$BITRATE]"
|
|
||||||
echo "COMPOSER [$COMPOSER]"
|
|
||||||
echo "DATE [$DATE]"
|
|
||||||
echo "DISC [$DISC]"
|
|
||||||
echo "FORMAT [$FORMAT]"
|
|
||||||
echo "GENRE [$GENRE]"
|
|
||||||
echo "TITLE [$TITLE]"
|
|
||||||
echo "TRACK [$TRACK]"
|
|
||||||
echo "TYER [$TYER]"
|
|
||||||
echo
|
|
||||||
echo "==> AFTER"
|
|
||||||
echo "ALBUM [$OUTPUT_ALBUM]"
|
|
||||||
echo "ARTIST [$OUTPUT_ARTIST]"
|
|
||||||
echo "BITRATE [$OUTPUT_BITRATE]"
|
|
||||||
echo "DATE [$OUTPUT_DATE]"
|
|
||||||
echo "FORMAT [$OUTPUT_FORMAT]"
|
|
||||||
echo "GENRE [$OUTPUT_GENRE]"
|
|
||||||
echo "TITLE [$OUTPUT_TITLE]"
|
|
||||||
echo "TRACK [$OUTPUT_TRACK]"
|
|
||||||
echo
|
|
||||||
echo "==> OUTPUT"
|
|
||||||
echo "[$OUTPUT_FILE.$OUTPUT_FORMAT]"
|
|
||||||
# echo "[$OGG_PARAM]"
|
|
||||||
echo
|
|
||||||
|
|
||||||
[ $PREVIEW -ne 0 ] && return
|
|
||||||
|
|
||||||
##==============================================================================
|
|
||||||
## RUN PROCESS
|
|
||||||
|
|
||||||
## Make sure directory exists.
|
|
||||||
mkdir -p "$OUTPUT"
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "ERROR: could not create output folder"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
## COVER
|
|
||||||
## We copy cover only if it does not already exist.
|
|
||||||
local OUTPUT_COVER
|
|
||||||
local COVER_COUNTER
|
|
||||||
local OUTPUT_COVERFILE
|
|
||||||
|
|
||||||
while read -r i; do
|
|
||||||
OUTPUT_COVER="$OUTPUT/${OUTPUT_ALBUM:+$OUTPUT_ALBUM - }Cover"
|
|
||||||
OUTPUT_COVERFILE="$OUTPUT_COVER.${i##*.}"
|
|
||||||
COVER_COUNTER=1
|
|
||||||
|
|
||||||
## Same cover is already in target folder.
|
|
||||||
if [ -e "$OUTPUT_COVERFILE" ] && \
|
|
||||||
[ "$(sha1sum $OUTPUT_COVERFILE | cut -f1 -d' ')" = "$(sha1sum $i | cut -f1 -d' ')" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Different cover with same name is in target folder. We append a number.
|
|
||||||
while [ -e "$OUTPUT_COVERFILE" ] && \
|
|
||||||
[ ! "$(sha1sum $OUTPUT_COVERFILE | cut -f1 -d' ')" = "$(sha1sum $i | cut -f1 -d' ')" ]; do
|
|
||||||
OUTPUT_COVERFILE="${OUTPUT_COVER} $COVER_COUNTER.${i##*.}"
|
|
||||||
COVER_COUNTER=$(($COVER_COUNTER+1))
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "==> COVER"
|
|
||||||
cp -nv "$i" "$OUTPUT_COVERFILE"
|
|
||||||
echo
|
|
||||||
done <<EOF
|
|
||||||
$(find "." \( -iname '*.png' -o -iname '*.jpg' \) )
|
|
||||||
EOF
|
|
||||||
|
|
||||||
## Zsh compatibility. We need it otherwise word splitting of parameter like
|
|
||||||
## OGG_PARAM will not work.
|
|
||||||
local STATUS
|
|
||||||
STATUS="$(set -o | grep 'shwordsplit' | awk '{print $2}')"
|
|
||||||
[ "$STATUS" = "off" ] && set -o shwordsplit
|
|
||||||
|
|
||||||
## TAG/RECODE
|
|
||||||
ffmpeg -i "$1" $OGG_PARAM \
|
|
||||||
-metadata title="$OUTPUT_TITLE" \
|
|
||||||
-metadata artist="$OUTPUT_ARTIST" \
|
|
||||||
-metadata track="$OUTPUT_TRACK" \
|
|
||||||
-metadata date="$OUTPUT_DATE" \
|
|
||||||
-metadata album="$OUTPUT_ALBUM" \
|
|
||||||
-metadata album_artist="$OUTPUT_ARTIST" \
|
|
||||||
-metadata genre="$OUTPUT_GENRE" \
|
|
||||||
-metadata composer="" \
|
|
||||||
-metadata TYER="" \
|
|
||||||
-metadata disc="" \
|
|
||||||
"$OUTPUT_FILE.$OUTPUT_FORMAT"
|
|
||||||
|
|
||||||
## Restore Zsh previous options. This will not turn off shwordsplit if it
|
|
||||||
## was on before calling the function.
|
|
||||||
[ "$STATUS" = "off" ] && set +o shwordsplit
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
## Video
|
|
||||||
|
|
||||||
## TODO: audio quality. Copy if AAC or OGG <320k.
|
|
||||||
## TODO: chapters support. Already OK?
|
|
||||||
## TODO: crop options.
|
|
||||||
## TODO: custom mapping.
|
|
||||||
## TODO: handle srt encoding.
|
|
||||||
## TODO: option to replace original file.
|
|
||||||
## TODO: work with -threads option.
|
|
||||||
|
|
||||||
## Vars
|
|
||||||
TC_VIDEO_AUDIO_QUAL=192k
|
|
||||||
|
|
||||||
## quality*size ~= 1/speed
|
|
||||||
# ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo
|
|
||||||
TC_VIDEO_PRESET=slow
|
|
||||||
|
|
||||||
## Overall quality. Logarithmic scale.
|
|
||||||
# 18 is near perfection.
|
|
||||||
# 22 is really good compression, while a bit more blurry than the original.
|
|
||||||
# 20 is a good compromise.
|
|
||||||
TC_VIDEO_QUAL=20
|
|
||||||
|
|
||||||
## x264 tuning (presets).
|
|
||||||
## Possible values: film, animation, grain, ...
|
|
||||||
## See x264 --fullhelp.
|
|
||||||
## No tuning by default.
|
|
||||||
# TC_VIDEO_TUNE="-tune film"
|
|
||||||
TC_VIDEO_TUNE=""
|
|
||||||
|
|
||||||
## x264 options.
|
|
||||||
## Possible values: hex, umh...
|
|
||||||
## me=umh is default.
|
|
||||||
# TC_VIDEO_OPTS="-x264opts me=hex"
|
|
||||||
TC_VIDEO_OPTS=""
|
|
||||||
|
|
||||||
tc_video_demux_audio()
|
|
||||||
{
|
|
||||||
ffmpeg -i "$1" -vn -sn -c:a copy -map 0:1 "$2"
|
|
||||||
}
|
|
||||||
|
|
||||||
tc_video_audio2ogg()
|
|
||||||
{
|
|
||||||
echo "================================================================================"
|
|
||||||
ffmpeg -threads 4 -i "$1" -vn -c:a libvorbis -b:a $TC_VIDEO_AUDIO_QUAL "${1%.*}-$(date '+%F-%H%M%S').ogg"
|
|
||||||
}
|
|
||||||
|
|
||||||
## TODO: separate functions.
|
|
||||||
## tc_video_batch
|
|
||||||
## tc_video_transcode
|
|
||||||
tc_video_transcode()
|
|
||||||
{
|
|
||||||
_printhelp()
|
|
||||||
{
|
|
||||||
cat <<EOF
|
|
||||||
Usage: $1 [OPTIONS] FILES|FOLDERS
|
|
||||||
|
|
||||||
Transcode FILES or files found in FOLDERS to .mkv with x264 and ogg. Output
|
|
||||||
files are the same as the original, with time appended. You can customize
|
|
||||||
encoding with the TC_* variables.
|
|
||||||
|
|
||||||
-f: Overwrite existing file if any.
|
|
||||||
-h: Display this help.
|
|
||||||
-s: Sample of ten minutes.
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
## What to do if file exists:
|
|
||||||
# -y overwrite
|
|
||||||
# -n do not overwrite.
|
|
||||||
local TC_OVERWRITE
|
|
||||||
TC_OVERWRITE="-n"
|
|
||||||
|
|
||||||
local TC_SAMPLE
|
|
||||||
TC_SAMPLE=""
|
|
||||||
|
|
||||||
while getopts ":fhs" opt; do
|
|
||||||
case $opt in
|
|
||||||
f)
|
|
||||||
TC_OVERWRITE="-y"
|
|
||||||
;;
|
|
||||||
h)
|
|
||||||
_printhelp "$0"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
s)
|
|
||||||
TC_SAMPLE="-ss 60 -t 600"
|
|
||||||
;;
|
|
||||||
?)
|
|
||||||
_printhelp "$0"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
:)
|
|
||||||
echo "Missing argument."
|
|
||||||
echo "Use $0 -h for help."
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
shift $(($OPTIND - 1))
|
|
||||||
|
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
|
||||||
tc_transcode .
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Zsh compatibility. We need it otherwise word splitting of parameter like
|
|
||||||
## TC_SAMPLE will not work.
|
|
||||||
local STATUS
|
|
||||||
STATUS="$(set -o | grep 'shwordsplit' | awk '{print $2}')"
|
|
||||||
[ "$STATUS" = "off" ] && set -o shwordsplit
|
|
||||||
|
|
||||||
_tc_transcode()
|
|
||||||
{
|
|
||||||
echo "================================================================================"
|
|
||||||
ffmpeg $TC_OVERWRITE $TC_SAMPLE -i "$1" \
|
|
||||||
-c:v libx264 -preset $TC_VIDEO_PRESET -crf $TC_VIDEO_QUAL $TC_VIDEO_TUNE $TC_VIDEO_OPTS \
|
|
||||||
-c:a libvorbis -b:a $TC_VIDEO_AUDIO_QUAL \
|
|
||||||
-c:s copy \
|
|
||||||
-map 0 "${1%.*}-$(date '+%F-%H%M%S').mkv"
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in "$@"; do
|
|
||||||
|
|
||||||
## Argument is a folder. We search for all video files in there.
|
|
||||||
if [ -d "$i" ]; then
|
|
||||||
|
|
||||||
## TODO: provide max-depth as option.
|
|
||||||
while read -r j; do
|
|
||||||
_tc_transcode $j
|
|
||||||
done <<EOF
|
|
||||||
$(find "$i" \( \
|
|
||||||
-iname '*.mkv' -o \
|
|
||||||
-iname '*.mp4' -o \
|
|
||||||
-iname '*.avi' -o \
|
|
||||||
-iname '*.webm' -o \
|
|
||||||
-iname '*.flv' -o \
|
|
||||||
-iname '*.wmv' -o \
|
|
||||||
-iname '*.mpg' \) )
|
|
||||||
EOF
|
|
||||||
|
|
||||||
## Argument is a regular file.
|
|
||||||
else
|
|
||||||
_tc_transcode "$i"
|
|
||||||
fi
|
|
||||||
|
|
||||||
done
|
|
||||||
|
|
||||||
## Restore Zsh previous options. This will not turn off shwordsplit if it
|
|
||||||
## was on before calling the function.
|
|
||||||
[ "$STATUS" = "off" ] && set +o shwordsplit
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
## OLD
|
|
||||||
|
|
||||||
## High in Handbrake:
|
|
||||||
# b-adapt=2:rc-lookahead=50:me=umh
|
|
||||||
|
|
||||||
## Mine in Handbrake:
|
|
||||||
# ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0:8x8dct=0
|
|
||||||
|
|
||||||
## Mine with details:
|
|
||||||
# cabac=1:ref=1:deblock=1:0:0:analyse=0x1:0x111:me=hex:subme=2:psy=1:psy_rd=1.00:0.00:mixed_ref=0:me_range=16:chroma_me=1:trellis=0:8x8dct=0:cqm=0:deadzone=21,11:fast_pskip=1:chroma_qp_offset=0:threads=6:sliced_threads=0:nr=0:decimate=1:interlaced=0:bluray_compat=0:constrained_intra=0:bframes=3:b_pyramid=2:b_adapt=1:b_bias=0:direct=1:weightb=1:open_gop=0:weightp=1:keyint=240:keyint_min=24:scenecut=40:intra_refresh=0:rc_lookahead=10:rc=crf:mbtree=1:crf=20.0:qcomp=0.60:qpmin=3:qpmax=69:qpstep=4:ip_ratio=1.40:aq=1:1.00
|
|
||||||
|
|
||||||
## Mine diff with default:
|
|
||||||
# ref=1:analyse=0x1:0x111:me=hex:subme=2:trellis=0:8x8dct=0:chroma_qp_offset=0:b_adapt=1:direct=1:weightp=1:keyint=240:rc_lookahead=10
|
|
||||||
|
|
||||||
|
|
||||||
## Best for bad quality movies.
|
|
||||||
# me=hex
|
|
Loading…
Reference in New Issue