scripts/flac2opus_collection.bash
2024-05-14 23:29:00 +02:00

200 lines
6 KiB
Bash
Executable file

#!/usr/bin/env bash
# This code is thrown together lazily to just work and never be touched again.
# Do NOT try to learn from this piece of shit
check_dep() {
if [ -z "$(command -v "$1")" ]; then
echo "'$1' is required but not installed."
exit 1
fi
}
check_deps() {
deps=("ffmpeg" "opusinfo" "xargs")
for dep in "${deps[@]}"; do
check_dep "$dep"
done
}
args() {
while [ -n "$1" ];do
case "$1" in
"help")
printf "\n\
arguments:\n\
in [input directory] (required) <The flac directory> {default: none}\n\
out [output directory] (required) <The opus directory> {default: none}\n\
verify [on/off] (optional) <Check converted files for corruption> {default: on}
vbr [on/off] (optional) <Toggle variable bit rate> {default: on}
bitrate [{bitrate}k] (optional) <Set the bitrate to be passed to ffmpeg> {default: 128k}\n\n"
exit 0
;;
"in")
if [ -z "$2" ]; then
echo "option '$1' requires an additional argument."
exit 1
fi
INPUT_DIR="$(realpath "$2")"
shift
;;
"out")
if [ -z "$2" ]; then
echo "option '$1' requires an additional argument."
exit 1
fi
OUTPUT_DIR="$(realpath "$2")"
shift
;;
"vbr")
if [ -z "$2" ]; then
echo "option '$1' requires an additional argument."
exit 1
fi
if [ "$2" != "on" ] && [ "$2" != "off" ]; then
echo "Invalid option '$2' for $1. Only accepted options are 'on' or 'off'."
exit 1
fi
VBR="$2"
shift
;;
"verify")
if [ -z "$2" ]; then
echo "option '$1' requires an additional argument."
exit 1
fi
if [ "$2" != "on" ] && [ "$2" != "off" ]; then
echo "Invalid option '$2' for $1. Only accepted options are 'on' or 'off'."
exit 1
fi
VERIFY="$2"
shift
;;
"bitrate")
if [ -z "$2" ]; then
echo "option '$1' requires an additional argument."
exit 1
fi
BITRATE="$2"
if [[ ! $2 =~ ^[0-9]+k$ ]]; then
echo "Invalid bitrate '$2', only accepts string matching pattern '[0-9]\\+k'"
fi
shift
;;
*)
echo "Invalid argument '$1', skipping"
;;
esac
shift
done
if [ -z "$INPUT_DIR" ]; then
echo "Missing input directory"
exit 1
fi
if [ -z "$OUTPUT_DIR" ]; then
echo "Missing input directory"
exit 1
fi
if [ -z "$BITRATE" ]; then
BITRATE="128k"
fi
if [ -z "$VBR" ]; then
VBR="on"
fi
if [ -z "$VERIFY" ]; then
VERIFY="on"
fi
}
get_total_count() {
local -n ref_total_count="$2"
total_count="$ref_total_count"
local dir="$1"
for item in "$dir"/*; do
if [ -f "$item" ]; then
new_file="${item/"$INPUT_DIR"/"$OUTPUT_DIR"}"
new_file="${new_file/".flac"/".oga"}"
if [ ! -f "$new_file" ] && [[ "$item" == *".flac" ]]; then
((total_count+=1))
elif [[ "$item" == *".flac" ]]; then
if [ "$VERIFY" = "on" ]; then
printf "Checking \e[93m'%s'\e[0m: " "$(basename "$new_file")"
if ! opusinfo "$new_file" > /dev/null 2>&1; then
((total_count+=1))
printf "\e[31mbad\e[0m\n"
rm "$new_file"
else
printf "\e[32mgood\e[0m\n"
fi
fi
fi
elif [ -d "$item" ]; then
get_total_count "$item" "total_count"
fi
done
}
iterate_dirs() {
local dir="$1"
local conversions=()
local -n ref_count="$2"
local total_count="$3"
count="$ref_count"
for item in "$dir"/*; do
local item="$(echo "$item" | sed "s/'/'\\\\'/g")"
if [ -f "$item" ]; then
new_file="${item/"$INPUT_DIR"/"$OUTPUT_DIR"}"
new_file="${new_file/.flac/.oga}"
if [ ! -f "$new_file" ]; then
if [[ "$item" == *".flac" ]]; then
((count+=1))
new_file="${new_file/".flac"/".oga"}"
conversions+=("printf '[%s/%s] \e[92m%s\e[0m -> \e[93m%s\e[0m\n' $count $total_count '$item' '$new_file' && \
ffmpeg -hide_banner -loglevel error -i '$item' -map_metadata 0 -map_metadata 0:s:0 -id3v2_version 3 -c:a libopus \
-vbr \"$VBR\" -b:a \"$BITRATE\" '$new_file' > /dev/null")
else
new_dir=$(dirname "${item/"$INPUT_DIR"/"$OUTPUT_DIR"}")
if [ ! -d "$new_dir" ]; then
mkdir -p "$new_dir"
fi
echo "Copied $(basename "$item")"
cp -f "$item" "$new_file"
fi
fi
elif [ -d "$item" ]; then
new_dir="${item/"$INPUT_DIR"/"$OUTPUT_DIR"}"
mkdir -p "$new_dir"
iterate_dirs "$item" "count" "$total_count"
fi
done
if [ ${#conversions[@]} -gt 0 ]; then
printf '%s\0' "${conversions[@]}" | xargs -0 -P "$(nproc)" -I {} bash -c '{}'
fi
}
main () {
check_deps
args "$@"
count="0"
total_count="0"
get_total_count "$INPUT_DIR" "total_count"
iterate_dirs "$INPUT_DIR" "count" "$total_count"
}
main "$@"