Skip to content

Instantly share code, notes, and snippets.

@AlbertoBarrago
Created October 2, 2025 09:30
Show Gist options
  • Select an option

  • Save AlbertoBarrago/942fabcf5796aff7e300e1754e565ec2 to your computer and use it in GitHub Desktop.

Select an option

Save AlbertoBarrago/942fabcf5796aff7e300e1754e565ec2 to your computer and use it in GitHub Desktop.
Convert FLAC audio files to MP3 format using FFmpeg.
#!/bin/sh
# flac2mp3 - Convert FLAC files to MP3 format (POSIX sh compatible)
# Version: 1.0
# Author: Albz <alberto.barrago@gmail.com>
VERSION="1.0"
BITRATE="320k"
QUIET=false
VERBOSE=false
PRESERVE_FLAC=true
# Color codes for output (if terminal supports it)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
usage() {
cat << EOF
Usage: $(basename "$0") [OPTIONS] FILE|DIRECTORY...
Convert FLAC audio files to MP3 format using FFmpeg.
ARGUMENTS:
FILE|DIRECTORY One or more FLAC files or directories containing FLAC files
OPTIONS:
-b RATE Set MP3 bitrate (default: 320k)
Common values: 128k, 192k, 256k, 320k
-d Delete original FLAC files after conversion
-q Suppress non-error output
-v Show detailed conversion information
-h Display this help message
-V Display version information
EXAMPLES:
$(basename "$0") song.flac
$(basename "$0") -b 192k song1.flac song2.flac
$(basename "$0") -d music_directory/
EOF
exit 0
}
show_version() {
echo "flac2mp3 version $VERSION"
exit 0
}
check_ffmpeg() {
if ! command -v ffmpeg >/dev/null 2>&1; then
printf "${RED}Error: FFmpeg is not installed or not in PATH${NC}\n" >&2
echo "Please install FFmpeg: https://ffmpeg.org/download.html" >&2
exit 1
fi
}
log() {
if [ "$QUIET" = "false" ]; then
printf "%b\n" "$@"
fi
}
convert_file() {
input_file="$1"
output_file="${input_file%.flac}.mp3"
if [ ! -f "$input_file" ]; then
printf "${RED}Error: File not found: %s${NC}\n" "$input_file" >&2
return 1
fi
case "$input_file" in
*.flac) ;;
*)
printf "${YELLOW}Warning: Skipping non-FLAC file: %s${NC}\n" "$input_file" >&2
return 1
;;
esac
if [ -f "$output_file" ]; then
log "${YELLOW}Warning: $output_file already exists, skipping...${NC}"
return 0
fi
log "${GREEN}Converting:${NC} $(basename "$input_file")"
if [ "$VERBOSE" = "false" ]; then
ffmpeg -i "$input_file" -ab "$BITRATE" -map_metadata 0 -id3v2_version 3 -loglevel error "$output_file" 2>&1
else
ffmpeg -i "$input_file" -ab "$BITRATE" -map_metadata 0 -id3v2_version 3 "$output_file" 2>&1
fi
if [ $? -eq 0 ]; then
log "${GREEN}✓ Success:${NC} $(basename "$output_file")"
if [ "$PRESERVE_FLAC" = "false" ]; then
rm "$input_file"
log "${YELLOW} Deleted original FLAC file${NC}"
fi
return 0
else
printf "${RED}✗ Failed:${NC} %s\n" "$(basename "$input_file")" >&2
return 1
fi
}
process_directory() {
dir="$1"
count=0
success=0
failed=0
if [ ! -d "$dir" ]; then
printf "${RED}Error: Directory not found: %s${NC}\n" "$dir" >&2
return 1
fi
log "${GREEN}Processing directory:${NC} $dir"
# Use find with exec to handle files properly
found_files=0
find "$dir" -maxdepth 1 -type f -name "*.flac" -print0 2>/dev/null | while IFS= read -r -d '' file
do
found_files=1
count=$((count + 1))
if convert_file "$file"; then
success=$((success + 1))
else
failed=$((failed + 1))
fi
done
# Alternative method if the above doesn't work - using a different approach
if [ $? -ne 0 ]; then
# Fallback: use ls with proper quoting
find "$dir" -maxdepth 1 -type f -name "*.flac" 2>/dev/null | while IFS= read -r file
do
count=$((count + 1))
if convert_file "$file"; then
success=$((success + 1))
else
failed=$((failed + 1))
fi
done
fi
if [ $count -eq 0 ]; then
log "${YELLOW}No FLAC files found in: $dir${NC}"
return 0
fi
log "\n${GREEN}Directory summary:${NC} $success succeeded, $failed failed out of $count files\n"
[ $failed -eq 0 ] && return 0 || return 1
}
main() {
total_success=0
total_failed=0
has_error=false
check_ffmpeg
if [ $# -eq 0 ]; then
usage
fi
# Parse arguments
while [ $# -gt 0 ]; do
case "$1" in
-h)
usage
;;
-V)
show_version
;;
-b)
shift
if [ -z "$1" ]; then
printf "${RED}Error: -b requires an argument${NC}\n" >&2
exit 1
fi
BITRATE="$1"
;;
-d)
PRESERVE_FLAC=false
;;
-q)
QUIET=true
;;
-v)
VERBOSE=true
;;
-*)
printf "${RED}Error: Unknown option: %s${NC}\n" "$1" >&2
echo "Use -h for usage information"
exit 1
;;
*)
break
;;
esac
shift
done
if [ $# -eq 0 ]; then
printf "${RED}Error: No files or directories specified${NC}\n" >&2
usage
fi
log "${GREEN}Starting conversion with bitrate: $BITRATE${NC}\n"
for item in "$@"; do
if [ -f "$item" ]; then
if convert_file "$item"; then
total_success=$((total_success + 1))
else
total_failed=$((total_failed + 1))
has_error=true
fi
elif [ -d "$item" ]; then
if ! process_directory "$item"; then
has_error=true
fi
else
printf "${RED}Error: Not found: %s${NC}\n" "$item" >&2
has_error=true
fi
done
if [ $total_success -gt 0 ] || [ $total_failed -gt 0 ]; then
log "\n${GREEN}=== Conversion Complete ===${NC}"
[ $total_success -gt 0 ] && log "${GREEN}Successfully converted: $total_success files${NC}"
[ $total_failed -gt 0 ] && log "${RED}Failed conversions: $total_failed files${NC}"
fi
if [ "$has_error" = "true" ]; then
exit 1
fi
exit 0
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment