Commit | Line | Data |
---|---|---|
a4b60a6d SBS |
1 | #!/bin/bash |
2 | # Desc: Converts a directory of mp3s into a single mkv file | |
3 | # Usage: mp3s_to_mkv.sh [DIR in] [DIR out] [BITRATE] | |
4 | # Example: mp3s_to_mkv.sh ./dir_source ./dir_output 48k | |
5 | # Depends: GNU Coretils 8.32 (date) | |
42560010 | 6 | # Version: 0.0.2 |
a4b60a6d SBS |
7 | |
8 | # plumbing | |
9 | opus_bitrate="$3"; # e.g. "48k" | |
10 | script_rundate="$(date +%s)"; | |
11 | dir_tmp="/dev/shm"; | |
12 | dir_in="$(readlink -f "$1")"; | |
13 | dir_out="$(readlink -f "$2")"; | |
14 | file_flist="$dir_tmp"/"$script_rundate"..flist.txt; | |
15 | file_out_mkv="$dir_out"/output.mkv; | |
16 | file_albumart="$dir_out"/albumart.png; | |
17 | ||
18 | yell() { echo "$0: $*" >&2; } # print script path and all args to stderr | |
19 | die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status | |
20 | must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails | |
21 | show_usage() { | |
22 | # Desc: Display script usage information | |
23 | # Usage: showUsage | |
24 | # Version 0.0.2 | |
25 | # Input: none | |
26 | # Output: stdout | |
27 | # Depends: GNU-coreutils 8.30 (cat) | |
28 | cat <<'EOF' | |
29 | USAGE: | |
30 | mp3s_to_mkv.sh [DIR in] [DIR out] [BITRATE] | |
31 | ||
32 | EXAMPLE: | |
33 | mp3s_to_mkv.sh ./source_dir ./out_dir 48k | |
34 | EOF | |
35 | } # Display information on how to use this script. | |
36 | check_depends() { | |
37 | if ! command -v ffmpeg; then show_usage; die "FATAL:Missing ffmpeg."; fi; | |
38 | }; # check dependencies | |
39 | check_plumbing() { | |
40 | if [[ $# -ne 3 ]]; then show_usage; die "FATAL:Invalid arg count:$#"; fi; | |
41 | if [[ ! -d "$dir_in" ]]; then show_usage; die "FATAL:Not a dir:$dir_in"; fi; | |
42 | if [[ ! -d "$dir_out" ]]; then mkdir -p "$2"; fi; | |
43 | }; # check arguments | |
44 | build_filelist() { | |
45 | # Depends: var dir_tmp temporary directory | |
46 | # var dir_in input dir | |
47 | # var file_flist path file list | |
48 | # Output: file: $file_flist list of mp3 files for ffmpeg | |
49 | ||
50 | # Change directory to input dir | |
51 | pushd "$dir_in" || die "FATAL:Directory error:$(pwd)"; | |
52 | ||
53 | while read -r line; do | |
54 | yell "$(printf "file '%s'\n" "${line#./}")"; | |
55 | printf "file '%s'\n" "${line#./}" >> "$file_flist"; | |
56 | done < <(find "$dir_in" -type f -iname "*.mp3" | sort); | |
57 | ||
58 | # Return to original dir | |
59 | popd || die "FATAL:Directory error:$(pwd)"; | |
60 | ||
61 | }; # build file list for ffmpeg | |
62 | ffmpeg_convert() { | |
63 | # Depends: var dir_tmp | |
64 | # dir_in | |
65 | # dir_out | |
66 | # Input: file $file_flist list of mp3 files for ffmpeg | |
67 | ||
68 | # Change directory to input dir | |
69 | pushd "$dir_in" || die "FATAL:Directory error:$(pwd)"; | |
70 | ||
71 | # Concatenate mp3 files into a single WAV file | |
72 | # # Convert WAV to 48 kbps opus mkv file | |
42560010 | 73 | ffmpeg -nostdin -f concat -safe 0 -i "$file_flist" -c:a pcm_s24le -rf64 auto -f wav - | \ |
a4b60a6d SBS |
74 | ffmpeg -i - -c:a libopus -b:a "$opus_bitrate" "$file_out_mkv"; |
75 | ||
76 | # Return to original dir | |
77 | popd || die "FATAL:Directory error:$(pwd)"; | |
78 | }; # convert mp3s to opus mkv via ffmpeg | |
79 | save_albumart() { | |
80 | local file | |
81 | file="$(find "$dir_in" -type f -iname "*.mp3" | sort | head -n1)"; | |
82 | file="$(readlink -f "$file")"; | |
42560010 | 83 | ffmpeg -nostdin -i "$file" -an -vcodec copy "$file_albumart"; |
a4b60a6d SBS |
84 | }; # save album art from an mp3 to output dir |
85 | main() { | |
86 | check_depends && yell "DEBUG:check_depends OK"; | |
87 | check_plumbing "$@" && yell "DEBUG:check_plumbing OK"; | |
88 | build_filelist "$@" && yell "DEBUG:build_filelist OK"; | |
89 | ffmpeg_convert "$@" && yell "DEBUG:ffmpeg_convert OK"; | |
90 | save_albumart "$@" && yell "DEBUG:save_albumart OK"; | |
91 | }; # main program | |
92 | ||
93 | main "$@"; | |
94 |