695 lines
20 KiB
Bash
695 lines
20 KiB
Bash
## -*- 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 -i"
|
||
;;
|
||
?)
|
||
_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
|