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