#!/bin/bash # Media Library FLAC Downsampling Script # Processes multiple albums in a media library, downsampling all FLAC files to 44.1 kHz # 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] " echo "" echo "Options:" echo " -o, --output DIR Output directory (default: creates _44k suffix)" echo " -r, --replace Replace original files (use with caution)" echo " -p, --preserve Preserve original files alongside new ones" 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" } # Default options OUTPUT_DIR="" REPLACE_MODE=false PRESERVE_MODE=false TEST_MODE=false 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 ;; -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}_44k" fi # Function to check if file needs downsampling needs_downsampling() { local file="$1" # Check if file is readable if [ ! -r "$file" ]; then echo " WARNING: File not readable: $file" return 1 fi local sample_rate=$(ffprobe -v quiet -select_streams a:0 -show_entries stream=sample_rate -of csv=p=0 "$file" 2>/dev/null) if [ -z "$sample_rate" ]; then echo " WARNING: Could not determine sample rate for: $(basename "$file")" return 1 # Skip files we can't analyze elif [ "$sample_rate" = "44100" ]; then return 1 # No downsampling needed else echo " Current sample rate: ${sample_rate} Hz" return 0 # Needs downsampling fi } # Function to process a single FLAC file process_flac_file() { local input_file="$1" local relative_path="$2" local output_file="" # Determine output file path if [ "$REPLACE_MODE" = true ]; then output_file="$input_file" elif [ "$PRESERVE_MODE" = true ]; then local dir=$(dirname "$input_file") local filename=$(basename "$input_file" .flac) output_file="$dir/${filename}_44k.flac" else output_file="$OUTPUT_DIR/$relative_path" fi # Check if downsampling is needed if ! needs_downsampling "$input_file"; then echo " SKIP: $relative_path (already 44.1 kHz)" ((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 (use .flac extension) local temp_file="${output_file}.tmp.flac" echo " PROCESSING: $relative_path" # Downsample the file echo " Command: ffmpeg -i \"$input_file\" -ar 44100 -c:a flac -compression_level 8 -y \"$temp_file\"" if ffmpeg -i "$input_file" -ar 44100 -c:a flac -compression_level 8 -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" ((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 FLAC files in the album directory local flac_files=() while IFS= read -r -d '' file; do flac_files+=("$file") done < <(find "$album_dir" -name "*.flac" -type f -print0) if [ ${#flac_files[@]} -eq 0 ]; then echo " No FLAC files found in $album_name" return fi # Process each FLAC file for flac_file in "${flac_files[@]}"; do local relative_path="${flac_file#$MEDIA_LIBRARY/}" process_flac_file "$flac_file" "$relative_path" done echo "" } # Main processing echo "Media Library FLAC Downsampler" 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 files will be overwritten)" elif [ "$PRESERVE_MODE" = true ]; then echo "Mode: PRESERVE (44k files created alongside originals)" else echo "Output: $OUTPUT_DIR" fi echo "" # Find all directories that contain FLAC files (potential albums) album_dirs=() while IFS= read -r -d '' dir; do if find "$dir" -maxdepth 1 -name "*.flac" -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 FLAC files found in $MEDIA_LIBRARY" exit 0 fi echo "Found ${#album_dirs[@]} album(s) with FLAC 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 (already 44.1 kHz)" echo "Errors: $ERROR_COUNT files" if [ "$ERROR_COUNT" -gt 0 ]; then exit 1 fi