scripts/flac2opus_collection.bash
caem 39a5c0ca67
Use the ogg container instead and fix an edge case
Using the ogg container allows to also embed cover images while still
using the opus codec.

Also fixed an edge case that would make the script fail if the first
thing it iterated on was a regular file which didn't require conversion
if the parent directory wasn't already created.
2024-05-06 18:31:43 +02:00

199 lines
5.8 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"/".ogg"}"
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
item="${item/\$/"\\$"}"
if [ -f "$item" ]; then
new_file="${item/"$INPUT_DIR"/"$OUTPUT_DIR"}"
new_file="${new_file/".flac"/".ogg"}"
if [ ! -f "$new_file" ]; then
if [[ "$item" == *".flac" ]]; then
((count+=1))
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\" -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"
}
set -e
main "$@"