From 66c860bcc5de1ad1b59085a7c95ad793e5ca7104 Mon Sep 17 00:00:00 2001 From: caem Date: Fri, 12 Apr 2024 15:51:46 +0200 Subject: [PATCH] feat: add flac2opus_collection.bash --- README.md | 25 +++++ flac2opus_collection.bash | 195 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 README.md create mode 100755 flac2opus_collection.bash diff --git a/README.md b/README.md new file mode 100644 index 0000000..aab68d4 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# scripts + +This repository contains some of my scripts. Feel free to use, modify or share as you wish. +Quality of the script and or code might or might not be questionable. I am not responsible +for any accidental rimraffing of your home or any other directories nor for any other damage. + +## Index + +### `flac2opus_collection.bash` + +This script converts a copy of a directory containing `.flac` files and converts them all to opus. +The source folder remains unmodified and the whole directory structure, including non-flac files, will +be simply copied over to the output directory. This serves the purpose of being able to compress +your whole lossless library to copy over to devices which are storage limited and or unable to playback +the lossless files at a quality where they are distinguishable from the compressed files. + +#### Overview of arguments +``` +arguments: + in [input directory] (required) {default: none} + out [output directory] (required) {default: none} + verify [on/off] (optional) {default: on} + vbr [on/off] (optional) {default: on} + bitrate [{bitrate}k] (optional) {default: 128k} +``` diff --git a/flac2opus_collection.bash b/flac2opus_collection.bash new file mode 100755 index 0000000..62ada4c --- /dev/null +++ b/flac2opus_collection.bash @@ -0,0 +1,195 @@ +#!/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) {default: none}\n\ + out [output directory] (required) {default: none}\n\ + verify [on/off] (optional) {default: on} + vbr [on/off] (optional) {default: on} + bitrate [{bitrate}k] (optional) {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"/".opus"}" + 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"/".opus"}" + 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\" -vbr \"$VBR\" -b:a \"$BITRATE\" \"$new_file\" > /dev/null") + else + 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 "$@" +