Files
gnommo/to_mp3.sh
T

118 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# to_mp3.sh — Convert .m4a and .wav files to .mp3
#
# Usage:
# ./to_mp3.sh <file_or_folder> [options]
#
# Options:
# --quality N VBR quality 0-9 (default: 2 ≈ 190kbps; lower = better)
# --bitrate N CBR bitrate e.g. 192k (overrides --quality)
# --replace Delete originals after successful conversion
# --dry-run Show what would be converted without doing anything
#
# Examples:
# ./to_mp3.sh recordings/
# ./to_mp3.sh interview.m4a
# ./to_mp3.sh recordings/ --replace
# ./to_mp3.sh recordings/ --bitrate 128k
set -euo pipefail
QUALITY=2
BITRATE=""
REPLACE=false
DRY_RUN=false
TARGET=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--quality) QUALITY="$2"; shift 2 ;;
--bitrate) BITRATE="$2"; shift 2 ;;
--replace) REPLACE=true; shift ;;
--dry-run) DRY_RUN=true; shift ;;
-*) echo "Unknown option: $1" >&2; exit 1 ;;
*) TARGET="$1"; shift ;;
esac
done
if [[ -z "$TARGET" ]]; then
echo "Usage: $(basename "$0") <file_or_folder> [--quality N] [--bitrate N] [--replace] [--dry-run]"
exit 1
fi
if ! command -v ffmpeg &>/dev/null; then
echo "Error: ffmpeg not found." >&2
exit 1
fi
# Collect files
files=()
if [[ -f "$TARGET" ]]; then
files=("$TARGET")
elif [[ -d "$TARGET" ]]; then
while IFS= read -r -d '' f; do
files+=("$f")
done < <(find "$TARGET" -maxdepth 1 -type f \( -iname "*.m4a" -o -iname "*.wav" \) -print0 | sort -z)
else
echo "Error: '$TARGET' is not a file or directory." >&2
exit 1
fi
if [[ ${#files[@]} -eq 0 ]]; then
echo "No .m4a or .wav files found in: $TARGET"
exit 0
fi
# Build audio quality flags
if [[ -n "$BITRATE" ]]; then
audio_flags=(-b:a "$BITRATE")
quality_desc="CBR ${BITRATE}"
else
audio_flags=(-q:a "$QUALITY")
quality_desc="VBR quality ${QUALITY}"
fi
echo "Converting ${#files[@]} file(s) to MP3 (${quality_desc})"
[[ "$REPLACE" == true ]] && echo " Originals will be deleted after conversion."
[[ "$DRY_RUN" == true ]] && echo " Dry-run mode — no files will be written."
echo ""
converted=0
skipped=0
errors=0
for src in "${files[@]}"; do
out="${src%.*}.mp3"
if [[ -f "$out" && "$DRY_RUN" == false ]]; then
echo " $(basename "$src"): output already exists, skipping"
((skipped++)) || true
continue
fi
size_mb=$(( $(stat -f%z "$src" 2>/dev/null || stat -c%s "$src") / 1048576 ))
printf " %-40s (%d MB)" "$(basename "$src")" "$size_mb"
if [[ "$DRY_RUN" == true ]]; then
echo " [dry-run] → $(basename "$out")"
continue
fi
if ffmpeg -i "$src" -vn "${audio_flags[@]}" -y "$out" -loglevel error; then
out_kb=$(( $(stat -f%z "$out" 2>/dev/null || stat -c%s "$out") / 1024 ))
echo "$(basename "$out") (${out_kb} KB)"
((converted++)) || true
if [[ "$REPLACE" == true ]]; then
rm "$src"
fi
else
echo " ERROR"
((errors++)) || true
[[ -f "$out" ]] && rm "$out"
fi
done
echo ""
echo "Done: ${converted} converted, ${skipped} skipped, ${errors} errors."