From: Steven Baltakatei Sandoval Date: Wed, 26 Apr 2023 11:36:53 +0000 (+0000) Subject: feat(user/mp3s_to_opus.sh):Add script to combine mp3s into an opus X-Git-Tag: 0.8.2^2~12 X-Git-Url: https://zdv2.bktei.com/gitweb/BK-2020-03.git/commitdiff_plain/89f378c6582e7482934a45d8180a6fa53fea5318?ds=inline feat(user/mp3s_to_opus.sh):Add script to combine mp3s into an opus - Note: Useful for combining audiobooks split across many mp3 files. --- diff --git a/user/mp3s_to_opus.sh b/user/mp3s_to_opus.sh new file mode 100755 index 0000000..0af92cc --- /dev/null +++ b/user/mp3s_to_opus.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# Desc: Converts a directory of mp3s into a single opus file +# Usage: mp3s_to_opus.sh [DIR in] [DIR out] [BITRATE] +# Example: mp3s_to_opus.sh ./dir_source ./dir_output 48k +# Depends: GNU Coretils 8.32 (date) +# Version: 0.0.3 + +# plumbing +opus_bitrate="$3"; # e.g. "48k" +script_rundate="$(date +%s)"; +dir_tmp="/dev/shm"; +dir_in="$(readlink -f "$1")"; +dir_out="$(readlink -f "$2")"; +file_flist="$dir_tmp"/"$script_rundate"..flist.txt; +file_out_opus="$dir_out"/output.opus; +file_albumart="$dir_out"/albumart.png; + +yell() { echo "$0: $*" >&2; } # print script path and all args to stderr +die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status +must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails +show_usage() { + # Desc: Display script usage information + # Usage: showUsage + # Version 0.0.2 + # Input: none + # Output: stdout + # Depends: GNU-coreutils 8.30 (cat) + cat <<'EOF' + USAGE: + mp3s_to_opus.sh [DIR in] [DIR out] [BITRATE] + + EXAMPLE: + mp3s_to_opus.sh ./source_dir ./out_dir 48k +EOF +} # Display information on how to use this script. +check_depends() { + if ! command -v ffmpeg; then show_usage; die "FATAL:Missing ffmpeg."; fi; +}; # check dependencies +check_plumbing() { + if [[ $# -ne 3 ]]; then show_usage; die "FATAL:Invalid arg count:$#"; fi; + if [[ ! -d "$dir_in" ]]; then show_usage; die "FATAL:Not a dir:$dir_in"; fi; + if [[ ! -d "$dir_out" ]]; then mkdir -p "$2"; fi; +}; # check arguments +build_filelist() { + # Depends: var dir_tmp temporary directory + # var dir_in input dir + # var file_flist path file list + # Output: file: $file_flist list of mp3 files for ffmpeg + + # Change directory to input dir + pushd "$dir_in" || die "FATAL:Directory error:$(pwd)"; + + while read -r line; do + yell "$(printf "file '%s'\n" "${line#./}")"; + printf "file '%s'\n" "${line#./}" >> "$file_flist"; + done < <(find "$dir_in" -type f -iname "*.mp3" | sort); + + # Return to original dir + popd || die "FATAL:Directory error:$(pwd)"; + +}; # build file list for ffmpeg +ffmpeg_convert() { + # Depends: var dir_tmp + # dir_in + # dir_out + # Input: file $file_flist list of mp3 files for ffmpeg + + # Change directory to input dir + pushd "$dir_in" || die "FATAL:Directory error:$(pwd)"; + + # Concatenate mp3 files into a single WAV file + # # Convert WAV to 48 kbps opus file + ffmpeg -f concat -safe 0 -i "$file_flist" -c:a pcm_s24le -rf64 auto -f wav - | \ + ffmpeg -i - -c:a libopus -b:a "$opus_bitrate" "$file_out_opus"; + + # Return to original dir + popd || die "FATAL:Directory error:$(pwd)"; +}; # convert mp3s to opus via ffmpeg +save_albumart() { + local file + file="$(find "$dir_in" -type f -iname "*.mp3" | sort | head -n1)"; + file="$(readlink -f "$file")"; + ffmpeg -i "$file" -an -vcodec copy "$file_albumart"; +}; # save album art from an mp3 to output dir +main() { + check_depends && yell "DEBUG:check_depends OK"; + check_plumbing "$@" && yell "DEBUG:check_plumbing OK"; + build_filelist "$@" && yell "DEBUG:build_filelist OK"; + ffmpeg_convert "$@" && yell "DEBUG:ffmpeg_convert OK"; + save_albumart "$@" && yell "DEBUG:save_albumart OK"; +}; # main program + +main "$@"; +