Last active 17 hours ago

Revision 27515d5d976250532967aa6f2bb27bd72482b94c

audio_encoder.sh Raw
1#!/usr/bin/env bash
2# Strict mode for robust error handling
3set -euo pipefail
4
5# Default variables
6INPUT_FILE=""
7OUTPUT_FILE=""
8CODEC="opus"
9ENCODER_TYPE="native"
10STREAM_INDEX="0"
11BITRATE_PER_CHANNEL=64 # kbps per channel for Opus
12
13usage() {
14 cat <<EOF
15Usage: $(basename "$0") -i <input_media> [OPTIONS]
16
17A script to extract and encode a specific audio track from a movie file to FLAC or Opus.
18
19Options:
20 -i <file> Input media file (video or audio)
21 -o <file> Output file name (optional; auto-generated if omitted)
22 -c <codec> Target codec: 'opus' or 'flac' (default: opus)
23 -e <type> Encoder type: 'native' (ffmpeg) or 'external' (flac/opusenc) (default: native)
24 -s <index> Audio stream index to select (default: 0, which is the first audio track)
25 -h Show this help message
26
27Examples:
28 $(basename "$0") -i movie.mkv -c opus -e native
29 $(basename "$0") -i movie.mp4 -c flac -s 1 -o my_custom_audio.flac
30EOF
31 exit 1
32}
33
34# Parse command line arguments
35while getopts "i:o:c:e:s:h" opt; do
36 case "$opt" in
37 i) INPUT_FILE="$OPTARG" ;;
38 o) OUTPUT_FILE="$OPTARG" ;;
39 c) CODEC=$(echo "$OPTARG" | tr '[:upper:]' '[:lower:]') ;;
40 e) ENCODER_TYPE=$(echo "$OPTARG" | tr '[:upper:]' '[:lower:]') ;;
41 s) STREAM_INDEX="$OPTARG" ;;
42 h|*) usage ;;
43 esac
44done
45
46# Validate inputs
47if [[ -z "$INPUT_FILE" || ! -f "$INPUT_FILE" ]]; then
48 echo "Error: Input file not found or not specified."
49 usage
50fi
51
52if [[ "$CODEC" != "opus" && "$CODEC" != "flac" ]]; then
53 echo "Error: Codec must be 'opus' or 'flac'."
54 exit 1
55fi
56
57if [[ "$ENCODER_TYPE" != "native" && "$ENCODER_TYPE" != "external" ]]; then
58 echo "Error: Encoder type must be 'native' or 'external'."
59 exit 1
60fi
61
62# Validate stream index is a number
63if ! [[ "$STREAM_INDEX" =~ ^[0-9]+$ ]]; then
64 echo "Error: Stream index must be a non-negative integer."
65 exit 1
66fi
67
68# Check dependencies
69command -v ffmpeg >/dev/null 2>&1 || { echo "Error: ffmpeg is required."; exit 1; }
70command -v ffprobe >/dev/null 2>&1 || { echo "Error: ffprobe is required."; exit 1; }
71
72if [[ "$ENCODER_TYPE" == "external" ]]; then
73 if [[ "$CODEC" == "flac" ]] && ! command -v flac >/dev/null 2>&1; then
74 echo "Error: 'flac' tool is required for external flac encoding."
75 exit 1
76 fi
77 if [[ "$CODEC" == "opus" ]] && ! command -v opusenc >/dev/null 2>&1; then
78 echo "Error: 'opusenc' tool is required for external opus encoding."
79 exit 1
80 fi
81fi
82
83# Determine number of audio channels of the selected audio stream
84CHANNELS=$(ffprobe -v error -select_streams "a:${STREAM_INDEX}" -show_entries stream=channels -of default=noprint_wrappers=1:nokey=1 "$INPUT_FILE")
85if [[ -z "$CHANNELS" ]]; then
86 echo "Error: Could not detect audio channels for stream index a:${STREAM_INDEX} in $INPUT_FILE. (Does that track exist?)"
87 exit 1
88fi
89
90echo "Detected $CHANNELS channel(s) in audio stream ${STREAM_INDEX}."
91
92# Calculate Opus bitrate (ignored for lossless FLAC)
93TOTAL_BITRATE=$(( CHANNELS * BITRATE_PER_CHANNEL ))
94
95# Determine output filename if not provided by user
96if [[ -z "$OUTPUT_FILE" ]]; then
97 OUTPUT_FILE="${INPUT_FILE%.*}_track${STREAM_INDEX}.${CODEC}"
98fi
99
100# Handle channel mapping for native Opus (required for >2 channels)
101# Family 0 is for mono/stereo. Family 1 is for surround (3 to 8 channels).
102MAPPING_FAMILY=0
103if [[ "$CHANNELS" -gt 2 ]]; then
104 MAPPING_FAMILY=1
105fi
106
107# Set FFmpeg logging profiles
108# Native: Hide banner, only show errors, but keep the progress bar (-stats)
109FFMPEG_NATIVE_LOG="-hide_banner -loglevel error -stats"
110# External: Completely silent so it doesn't garble the external tool's output
111FFMPEG_EXT_LOG="-hide_banner -loglevel quiet"
112
113echo "Starting encode: Target=$CODEC | Encoder=$ENCODER_TYPE | Stream=a:${STREAM_INDEX} | Output=$OUTPUT_FILE"
114
115# Execute encoding based on user choices
116if [[ "$CODEC" == "opus" ]]; then
117 echo "Target Bitrate: ${TOTAL_BITRATE}k"
118
119 if [[ "$ENCODER_TYPE" == "native" ]]; then
120 ffmpeg $FFMPEG_NATIVE_LOG -y -i "$INPUT_FILE" -map "0:a:${STREAM_INDEX}" \
121 -c:a libopus -b:a "${TOTAL_BITRATE}k" -vbr on -mapping_family "$MAPPING_FAMILY" \
122 "$OUTPUT_FILE"
123
124 elif [[ "$ENCODER_TYPE" == "external" ]]; then
125 # Pipe 24-bit PCM WAV to opusenc.
126 ffmpeg $FFMPEG_EXT_LOG -y -i "$INPUT_FILE" -map "0:a:${STREAM_INDEX}" -f wav -c:a pcm_s24le - | \
127 opusenc --bitrate "$TOTAL_BITRATE" - "$OUTPUT_FILE"
128 fi
129
130elif [[ "$CODEC" == "flac" ]]; then
131 if [[ "$ENCODER_TYPE" == "native" ]]; then
132 # Native FLAC encoding
133 ffmpeg $FFMPEG_NATIVE_LOG -y -i "$INPUT_FILE" -map "0:a:${STREAM_INDEX}" \
134 -c:a flac -compression_level 8 \
135 "$OUTPUT_FILE"
136
137 elif [[ "$ENCODER_TYPE" == "external" ]]; then
138 # Pipe 24-bit PCM WAV to external FLAC encoder.
139 ffmpeg $FFMPEG_EXT_LOG -y -i "$INPUT_FILE" -map "0:a:${STREAM_INDEX}" -f wav -c:a pcm_s24le - | \
140 flac -8 - -o "$OUTPUT_FILE"
141 fi
142fi
143
144echo -e "\nEncoding complete: $OUTPUT_FILE"