wav to flac
This commit is contained in:
		
							
								
								
									
										303
									
								
								wav-to-flac.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										303
									
								
								wav-to-flac.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,303 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# Media Library WAV to FLAC Transcoding Script
 | 
			
		||||
# Processes multiple albums in a media library, converting all WAV files to FLAC
 | 
			
		||||
 | 
			
		||||
# Check if ffmpeg is installed
 | 
			
		||||
if ! command -v ffmpeg &> /dev/null; then
 | 
			
		||||
    echo "Error: ffmpeg is not installed or not in PATH"
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Function to display usage
 | 
			
		||||
show_usage() {
 | 
			
		||||
    echo "Usage: $0 [OPTIONS] <media_library_path>"
 | 
			
		||||
    echo ""
 | 
			
		||||
    echo "Options:"
 | 
			
		||||
    echo "  -o, --output DIR     Output directory (default: creates _flac suffix)"
 | 
			
		||||
    echo "  -r, --replace        Replace original files (use with caution)"
 | 
			
		||||
    echo "  -p, --preserve       Preserve original files alongside new ones"
 | 
			
		||||
    echo "  -c, --compression N  FLAC compression level 0-8 (default: 8)"
 | 
			
		||||
    echo "  -t, --test           Test mode - show what would be processed"
 | 
			
		||||
    echo "  -h, --help           Show this help message"
 | 
			
		||||
    echo ""
 | 
			
		||||
    echo "Examples:"
 | 
			
		||||
    echo "  $0 /path/to/music/library"
 | 
			
		||||
    echo "  $0 -o /path/to/output /path/to/music/library"
 | 
			
		||||
    echo "  $0 --test /path/to/music/library"
 | 
			
		||||
    echo "  $0 --replace /path/to/music/library"
 | 
			
		||||
    echo "  $0 -c 5 /path/to/music/library"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Default options
 | 
			
		||||
OUTPUT_DIR=""
 | 
			
		||||
REPLACE_MODE=false
 | 
			
		||||
PRESERVE_MODE=false
 | 
			
		||||
TEST_MODE=false
 | 
			
		||||
COMPRESSION_LEVEL=8
 | 
			
		||||
PROCESSED_COUNT=0
 | 
			
		||||
SKIPPED_COUNT=0
 | 
			
		||||
ERROR_COUNT=0
 | 
			
		||||
 | 
			
		||||
# Parse command line arguments
 | 
			
		||||
while [[ $# -gt 0 ]]; do
 | 
			
		||||
    case $1 in
 | 
			
		||||
        -o|--output)
 | 
			
		||||
            OUTPUT_DIR="$2"
 | 
			
		||||
            shift 2
 | 
			
		||||
            ;;
 | 
			
		||||
        -r|--replace)
 | 
			
		||||
            REPLACE_MODE=true
 | 
			
		||||
            shift
 | 
			
		||||
            ;;
 | 
			
		||||
        -p|--preserve)
 | 
			
		||||
            PRESERVE_MODE=true
 | 
			
		||||
            shift
 | 
			
		||||
            ;;
 | 
			
		||||
        -c|--compression)
 | 
			
		||||
            COMPRESSION_LEVEL="$2"
 | 
			
		||||
            if [[ ! "$COMPRESSION_LEVEL" =~ ^[0-8]$ ]]; then
 | 
			
		||||
                echo "Error: Compression level must be 0-8"
 | 
			
		||||
                exit 1
 | 
			
		||||
            fi
 | 
			
		||||
            shift 2
 | 
			
		||||
            ;;
 | 
			
		||||
        -t|--test)
 | 
			
		||||
            TEST_MODE=true
 | 
			
		||||
            shift
 | 
			
		||||
            ;;
 | 
			
		||||
        -h|--help)
 | 
			
		||||
            show_usage
 | 
			
		||||
            exit 0
 | 
			
		||||
            ;;
 | 
			
		||||
        -*)
 | 
			
		||||
            echo "Unknown option: $1"
 | 
			
		||||
            show_usage
 | 
			
		||||
            exit 1
 | 
			
		||||
            ;;
 | 
			
		||||
        *)
 | 
			
		||||
            MEDIA_LIBRARY="$1"
 | 
			
		||||
            shift
 | 
			
		||||
            ;;
 | 
			
		||||
    esac
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# Check if media library path is provided
 | 
			
		||||
if [ -z "$MEDIA_LIBRARY" ]; then
 | 
			
		||||
    echo "Error: Media library path is required"
 | 
			
		||||
    show_usage
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Check if media library exists
 | 
			
		||||
if [ ! -d "$MEDIA_LIBRARY" ]; then
 | 
			
		||||
    echo "Error: Media library directory '$MEDIA_LIBRARY' not found"
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Set output directory if not specified
 | 
			
		||||
if [ -z "$OUTPUT_DIR" ] && [ "$REPLACE_MODE" = false ]; then
 | 
			
		||||
    OUTPUT_DIR="${MEDIA_LIBRARY}_flac"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Function to check if file needs transcoding
 | 
			
		||||
needs_transcoding() {
 | 
			
		||||
    local file="$1"
 | 
			
		||||
    
 | 
			
		||||
    # Check if file is readable
 | 
			
		||||
    if [ ! -r "$file" ]; then
 | 
			
		||||
        echo "    WARNING: File not readable: $file"
 | 
			
		||||
        return 1
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    # Get file format info
 | 
			
		||||
    local format_info=$(ffprobe -v quiet -select_streams a:0 -show_entries stream=codec_name -of csv=p=0 "$file" 2>/dev/null)
 | 
			
		||||
    
 | 
			
		||||
    if [ -z "$format_info" ]; then
 | 
			
		||||
        echo "    WARNING: Could not determine format for: $(basename "$file")"
 | 
			
		||||
        return 1  # Skip files we can't analyze
 | 
			
		||||
    else
 | 
			
		||||
        echo "    Current format: $format_info"
 | 
			
		||||
        return 0  # Needs transcoding
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Function to process a single WAV file
 | 
			
		||||
process_wav_file() {
 | 
			
		||||
    local input_file="$1"
 | 
			
		||||
    local relative_path="$2"
 | 
			
		||||
    local output_file=""
 | 
			
		||||
    
 | 
			
		||||
    # Determine output file path
 | 
			
		||||
    if [ "$REPLACE_MODE" = true ]; then
 | 
			
		||||
        local dir=$(dirname "$input_file")
 | 
			
		||||
        local filename=$(basename "$input_file" .wav)
 | 
			
		||||
        output_file="$dir/${filename}.flac"
 | 
			
		||||
    elif [ "$PRESERVE_MODE" = true ]; then
 | 
			
		||||
        local dir=$(dirname "$input_file")
 | 
			
		||||
        local filename=$(basename "$input_file" .wav)
 | 
			
		||||
        output_file="$dir/${filename}.flac"
 | 
			
		||||
    else
 | 
			
		||||
        local output_relative_path="${relative_path%.wav}.flac"
 | 
			
		||||
        output_file="$OUTPUT_DIR/$output_relative_path"
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    # Check if transcoding is needed
 | 
			
		||||
    if ! needs_transcoding "$input_file"; then
 | 
			
		||||
        echo "  SKIP: $relative_path (cannot analyze)"
 | 
			
		||||
        ((SKIPPED_COUNT++))
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    # Check if output file already exists and is the same as input (prevent overwriting)
 | 
			
		||||
    if [ "$output_file" = "$input_file" ]; then
 | 
			
		||||
        echo "  SKIP: $relative_path (output would overwrite input)"
 | 
			
		||||
        ((SKIPPED_COUNT++))
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    # Check if output file already exists
 | 
			
		||||
    if [ -f "$output_file" ] && [ "$REPLACE_MODE" = false ]; then
 | 
			
		||||
        echo "  SKIP: $relative_path (output file already exists)"
 | 
			
		||||
        ((SKIPPED_COUNT++))
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    if [ "$TEST_MODE" = true ]; then
 | 
			
		||||
        echo "  WOULD PROCESS: $relative_path → $(basename "$output_file")"
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    # Create output directory if needed
 | 
			
		||||
    local output_dir=$(dirname "$output_file")
 | 
			
		||||
    if [ ! -d "$output_dir" ]; then
 | 
			
		||||
        echo "    Creating directory: $output_dir"
 | 
			
		||||
        if ! mkdir -p "$output_dir"; then
 | 
			
		||||
            echo "  ✗ ERROR: Cannot create output directory: $output_dir"
 | 
			
		||||
            ((ERROR_COUNT++))
 | 
			
		||||
            return
 | 
			
		||||
        fi
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    # Verify input file exists and is readable
 | 
			
		||||
    if [ ! -f "$input_file" ]; then
 | 
			
		||||
        echo "  ✗ ERROR: Input file not found: $input_file"
 | 
			
		||||
        ((ERROR_COUNT++))
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    if [ ! -r "$input_file" ]; then
 | 
			
		||||
        echo "  ✗ ERROR: Input file not readable: $input_file"
 | 
			
		||||
        ((ERROR_COUNT++))
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    # Create temporary file for safe processing
 | 
			
		||||
    local temp_file="${output_file}.tmp.flac"
 | 
			
		||||
    
 | 
			
		||||
    echo "  PROCESSING: $relative_path"
 | 
			
		||||
    
 | 
			
		||||
    # Transcode the file
 | 
			
		||||
    echo "    Command: ffmpeg -i \"$input_file\" -c:a flac -compression_level $COMPRESSION_LEVEL -y \"$temp_file\""
 | 
			
		||||
    
 | 
			
		||||
    if ffmpeg -i "$input_file" -c:a flac -compression_level "$COMPRESSION_LEVEL" -y "$temp_file" 2>"${temp_file}.log"; then
 | 
			
		||||
        # Move temp file to final location
 | 
			
		||||
        mv "$temp_file" "$output_file"
 | 
			
		||||
        echo "  ✓ DONE: $(basename "$output_file")"
 | 
			
		||||
        rm -f "${temp_file}.log"
 | 
			
		||||
        
 | 
			
		||||
        # Remove original file if in replace mode
 | 
			
		||||
        if [ "$REPLACE_MODE" = true ]; then
 | 
			
		||||
            echo "    Removing original: $(basename "$input_file")"
 | 
			
		||||
            rm "$input_file"
 | 
			
		||||
        fi
 | 
			
		||||
        
 | 
			
		||||
        ((PROCESSED_COUNT++))
 | 
			
		||||
    else
 | 
			
		||||
        echo "  ✗ ERROR: Failed to process $relative_path"
 | 
			
		||||
        echo "    Error log saved to: ${temp_file}.log"
 | 
			
		||||
        if [ -f "${temp_file}.log" ]; then
 | 
			
		||||
            echo "    Last few lines of error:"
 | 
			
		||||
            tail -n 5 "${temp_file}.log" | sed 's/^/    /'
 | 
			
		||||
        fi
 | 
			
		||||
        rm -f "$temp_file"
 | 
			
		||||
        ((ERROR_COUNT++))
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Function to process an album directory
 | 
			
		||||
process_album() {
 | 
			
		||||
    local album_dir="$1"
 | 
			
		||||
    local album_name=$(basename "$album_dir")
 | 
			
		||||
    
 | 
			
		||||
    echo "Processing album: $album_name"
 | 
			
		||||
    
 | 
			
		||||
    # Find all WAV files in the album directory
 | 
			
		||||
    local wav_files=()
 | 
			
		||||
    while IFS= read -r -d '' file; do
 | 
			
		||||
        wav_files+=("$file")
 | 
			
		||||
    done < <(find "$album_dir" -name "*.wav" -type f -print0)
 | 
			
		||||
    
 | 
			
		||||
    if [ ${#wav_files[@]} -eq 0 ]; then
 | 
			
		||||
        echo "  No WAV files found in $album_name"
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
    
 | 
			
		||||
    # Process each WAV file
 | 
			
		||||
    for wav_file in "${wav_files[@]}"; do
 | 
			
		||||
        local relative_path="${wav_file#$MEDIA_LIBRARY/}"
 | 
			
		||||
        process_wav_file "$wav_file" "$relative_path"
 | 
			
		||||
    done
 | 
			
		||||
    
 | 
			
		||||
    echo ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Main processing
 | 
			
		||||
echo "Media Library WAV to FLAC Transcoder"
 | 
			
		||||
echo "===================================="
 | 
			
		||||
echo "Input:  $MEDIA_LIBRARY"
 | 
			
		||||
 | 
			
		||||
if [ "$TEST_MODE" = true ]; then
 | 
			
		||||
    echo "Mode:   TEST MODE (no files will be modified)"
 | 
			
		||||
elif [ "$REPLACE_MODE" = true ]; then
 | 
			
		||||
    echo "Mode:   REPLACE (original WAV files will be deleted after conversion)"
 | 
			
		||||
elif [ "$PRESERVE_MODE" = true ]; then
 | 
			
		||||
    echo "Mode:   PRESERVE (FLAC files created alongside originals)"
 | 
			
		||||
else
 | 
			
		||||
    echo "Output: $OUTPUT_DIR"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo "FLAC Compression Level: $COMPRESSION_LEVEL"
 | 
			
		||||
echo ""
 | 
			
		||||
 | 
			
		||||
# Find all directories that contain WAV files (potential albums)
 | 
			
		||||
album_dirs=()
 | 
			
		||||
while IFS= read -r -d '' dir; do
 | 
			
		||||
    if find "$dir" -maxdepth 1 -name "*.wav" -type f | grep -q .; then
 | 
			
		||||
        album_dirs+=("$dir")
 | 
			
		||||
    fi
 | 
			
		||||
done < <(find "$MEDIA_LIBRARY" -type d -print0)
 | 
			
		||||
 | 
			
		||||
if [ ${#album_dirs[@]} -eq 0 ]; then
 | 
			
		||||
    echo "No albums with WAV files found in $MEDIA_LIBRARY"
 | 
			
		||||
    exit 0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo "Found ${#album_dirs[@]} album(s) with WAV files"
 | 
			
		||||
echo ""
 | 
			
		||||
 | 
			
		||||
# Process each album
 | 
			
		||||
for album_dir in "${album_dirs[@]}"; do
 | 
			
		||||
    process_album "$album_dir"
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# Summary
 | 
			
		||||
echo "Processing Summary:"
 | 
			
		||||
echo "=================="
 | 
			
		||||
echo "Processed: $PROCESSED_COUNT files"
 | 
			
		||||
echo "Skipped:   $SKIPPED_COUNT files"
 | 
			
		||||
echo "Errors:    $ERROR_COUNT files"
 | 
			
		||||
 | 
			
		||||
if [ "$ERROR_COUNT" -gt 0 ]; then
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
		Reference in New Issue
	
	Block a user