Created
October 2, 2025 09:30
-
-
Save AlbertoBarrago/942fabcf5796aff7e300e1754e565ec2 to your computer and use it in GitHub Desktop.
Convert FLAC audio files to MP3 format using FFmpeg.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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