Created
March 16, 2026 07:23
-
-
Save yszheda/763564e5bb5ce74517e7f0bb3386f151 to your computer and use it in GitHub Desktop.
Convert wav to flac
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
| #!/usr/bin/env python3 | |
| import os | |
| import subprocess | |
| import sys | |
| import argparse | |
| # Default configuration | |
| DEFAULT_COMPRESSION_LEVEL = 8 # 0-8, 8 is maximum | |
| DEFAULT_LOG_NAME = "conversion_log_py.txt" | |
| def parse_args(): | |
| """Parse command line arguments""" | |
| parser = argparse.ArgumentParser( | |
| description="Batch convert WAV files to FLAC using ffmpeg" | |
| ) | |
| parser.add_argument( | |
| "--base-dir", | |
| "-d", | |
| default=os.getcwd(), | |
| help=f'Base directory to search for WAV files (default: current directory "{os.getcwd()}")', | |
| ) | |
| parser.add_argument( | |
| "--log-file", | |
| "-l", | |
| help=f"Path to log file (default: {{base-dir}}/{DEFAULT_LOG_NAME})", | |
| ) | |
| parser.add_argument( | |
| "--compression-level", | |
| "-c", | |
| type=int, | |
| default=DEFAULT_COMPRESSION_LEVEL, | |
| choices=range(0, 9), | |
| help=f"FLAC compression level 0-8 (default: {DEFAULT_COMPRESSION_LEVEL})", | |
| ) | |
| return parser.parse_args() | |
| def get_file_size(file_path): | |
| """Get file size in bytes""" | |
| try: | |
| return os.path.getsize(file_path) | |
| except: | |
| return 0 | |
| def convert_wav_to_flac(wav_path, flac_path, compression_level): | |
| """Convert WAV to FLAC using ffmpeg""" | |
| cmd = [ | |
| "ffmpeg", | |
| "-i", | |
| wav_path, | |
| "-c:a", | |
| "flac", | |
| "-compression_level", | |
| str(compression_level), | |
| "-y", | |
| "-v", | |
| "error", | |
| flac_path, | |
| ] | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| return ( | |
| result.returncode == 0 | |
| and os.path.exists(flac_path) | |
| and os.path.getsize(flac_path) > 0 | |
| ) | |
| def main(): | |
| args = parse_args() | |
| base_dir = os.path.abspath(args.base_dir) | |
| if args.log_file: | |
| log_file = os.path.abspath(args.log_file) | |
| else: | |
| log_file = os.path.join(base_dir, DEFAULT_LOG_NAME) | |
| compression_level = args.compression_level | |
| with open(log_file, "w", encoding="utf-8") as log: | |
| log.write( | |
| f"Starting WAV to FLAC conversion - {os.popen('date').read().strip()}\n" | |
| ) | |
| log.write(f"Base directory: {base_dir}\n") | |
| log.write(f"Compression level: {compression_level}\n\n") | |
| # Find all WAV files | |
| wav_files = [] | |
| for root, dirs, files in os.walk(base_dir): | |
| for file in files: | |
| if file.lower().endswith(".wav"): | |
| wav_path = os.path.join(root, file) | |
| wav_files.append(wav_path) | |
| total_files = len(wav_files) | |
| print(f"Found {total_files} WAV files to convert") | |
| log.write(f"Found {total_files} WAV files\n\n") | |
| print("-" * 50) | |
| converted = 0 | |
| failed = 0 | |
| original_total = 0 | |
| compressed_total = 0 | |
| for idx, wav_path in enumerate(wav_files, 1): | |
| # Create flac path | |
| file_dir = os.path.dirname(wav_path) | |
| file_name = os.path.basename(wav_path) | |
| flac_name = os.path.splitext(file_name)[0] + ".flac" | |
| flac_path = os.path.join(file_dir, flac_name) | |
| print( | |
| f"[{idx}/{total_files}] Converting: {os.path.relpath(wav_path, base_dir)}" | |
| ) | |
| log.write(f"[{idx}/{total_files}] Converting: {wav_path}\n") | |
| log.write(f" Output: {flac_path}\n") | |
| original_size = get_file_size(wav_path) | |
| original_total += original_size | |
| if convert_wav_to_flac(wav_path, flac_path, compression_level): | |
| compressed_size = get_file_size(flac_path) | |
| compressed_total += compressed_size | |
| # Delete original | |
| try: | |
| os.remove(wav_path) | |
| print(f" [OK] Success - Original WAV deleted") | |
| log.write(f" [OK] Success - Original deleted\n") | |
| converted += 1 | |
| except Exception as e: | |
| print(f" [WARN] Converted OK but failed to delete original: {e}") | |
| log.write(f" [WARN] Converted OK but failed to delete original: {e}\n") | |
| converted += 1 | |
| else: | |
| print(f" [FAIL] FAILED to convert") | |
| log.write(f" [FAIL] FAILED to convert\n") | |
| failed += 1 | |
| # Get free space | |
| try: | |
| if os.name == "nt": | |
| # Windows - get free space on the drive containing base_dir | |
| import ctypes | |
| # Get the drive letter from base_dir | |
| drive = os.path.splitdrive(base_dir)[0] + "\\" | |
| free_bytes = ctypes.c_ulonglong(0) | |
| ctypes.windll.kernel32.GetDiskFreeSpaceExW( | |
| ctypes.c_wchar_p(drive), None, None, ctypes.byref(free_bytes) | |
| ) | |
| free_gb = free_bytes.value / (1024**3) | |
| print(f" Free space left: {free_gb:.1f} GB") | |
| except: | |
| pass | |
| log.write("\n") | |
| sys.stdout.flush() | |
| print("-" * 50) | |
| print(f"Conversion completed:") | |
| print(f" Success: {converted} files") | |
| print(f" Failed: {failed} files") | |
| log.write("\n" + "-" * 50 + "\n") | |
| log.write(f"Conversion completed:\n") | |
| log.write(f" Success: {converted} files\n") | |
| log.write(f" Failed: {failed} files\n\n") | |
| if converted > 0: | |
| original_gb = original_total / (1024**3) | |
| compressed_gb = compressed_total / (1024**3) | |
| saved_gb = (original_total - compressed_total) / (1024**3) | |
| saved_percent = ( | |
| (original_total - compressed_total) * 100 / original_total | |
| if original_total > 0 | |
| else 0 | |
| ) | |
| print(f"\nSpace summary:") | |
| print(f" Original total: {original_gb:.2f} GB") | |
| print(f" Compressed total: {compressed_gb:.2f} GB") | |
| print(f" Space saved: {saved_gb:.2f} GB ({saved_percent:.1f}%)") | |
| log.write(f"Space summary:\n") | |
| log.write(f" Original total: {original_gb:.2f} GB\n") | |
| log.write(f" Compressed total: {compressed_gb:.2f} GB\n") | |
| log.write(f" Space saved: {saved_gb:.2f} GB ({saved_percent:.1f}%)\n") | |
| log.write(f"\nConversion finished at {os.popen('date').read().strip()}\n") | |
| if __name__ == "__main__": | |
| main() |
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/bash | |
| # Batch convert WAV to FLAC with ffmpeg | |
| # Correct handling for filenames with spaces | |
| # Convert one by one and delete original after success | |
| # Default configuration | |
| DEFAULT_COMPRESSION_LEVEL=8 # 0-8, 8 is maximum | |
| DEFAULT_LOG_NAME="conversion_log.txt" | |
| # Parse command line arguments | |
| BASE_DIR="" | |
| LOG_FILE="" | |
| COMPRESSION_LEVEL="$DEFAULT_COMPRESSION_LEVEL" | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -d|--base-dir) | |
| BASE_DIR="$2" | |
| shift 2 | |
| ;; | |
| -l|--log-file) | |
| LOG_FILE="$2" | |
| shift 2 | |
| ;; | |
| -c|--compression-level) | |
| COMPRESSION_LEVEL="$2" | |
| shift 2 | |
| ;; | |
| -h|--help) | |
| echo "Usage: $0 [OPTIONS]" | |
| echo "Batch convert WAV files to FLAC using ffmpeg" | |
| echo "" | |
| echo "Options:" | |
| echo " -d, --base-dir PATH Base directory to search for WAV files (default: current directory)" | |
| echo " -l, --log-file PATH Path to log file (default: {base-dir}/$DEFAULT_LOG_NAME)" | |
| echo " -c, --compression-level 0-8 FLAC compression level (default: $DEFAULT_COMPRESSION_LEVEL)" | |
| echo " -h, --help Show this help message" | |
| exit 0 | |
| ;; | |
| *) | |
| echo "Unknown option: $1" >&2 | |
| echo "Use $0 --help for usage" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| # Set defaults if not provided | |
| if [[ -z "$BASE_DIR" ]]; then | |
| BASE_DIR="$(pwd)" | |
| fi | |
| if [[ -z "$LOG_FILE" ]]; then | |
| LOG_FILE="$BASE_DIR/$DEFAULT_LOG_NAME" | |
| fi | |
| echo "Starting WAV to FLAC conversion - $(date)" > "$LOG_FILE" | |
| echo "Base directory: $BASE_DIR" >> "$LOG_FILE" | |
| echo "Compression level: $COMPRESSION_LEVEL" >> "$LOG_FILE" | |
| # Count files first | |
| TOTAL_FILES=$(find "$BASE_DIR" -name "*.wav" -type f | wc -l) | |
| echo "Found $TOTAL_FILES WAV files" >> "$LOG_FILE" | |
| echo "Found $TOTAL_FILES WAV files to convert" | |
| echo "----------------------------------------" | |
| CONVERTED=0 | |
| FAILED=0 | |
| ORIGINAL_TOTAL=0 | |
| COMPRESSED_TOTAL=0 | |
| # Use find with -print0 and properly read null-separated output | |
| find "$BASE_DIR" -name "*.wav" -type f -print0 | while IFS= read -r -d '' WAV_FILE; do | |
| # Create output FLAC path (same directory, .wav -> .flac) | |
| FLAC_FILE="${WAV_FILE%.wav}.flac" | |
| CONV_NUM=$((CONVERTED + FAILED + 1)) | |
| echo "[$CONV_NUM/$TOTAL_FILES] Converting: $WAV_FILE" | |
| echo "Converting: $WAV_FILE -> $FLAC_FILE" >> "$LOG_FILE" | |
| # Get original file size (handle both GNU and BSD stat) | |
| ORIGINAL_SIZE=$(stat -c%s "$WAV_FILE" 2>/dev/null || stat -f%z "$WAV_FILE") | |
| ORIGINAL_TOTAL=$((ORIGINAL_TOTAL + ORIGINAL_SIZE)) | |
| # Convert WAV to FLAC | |
| ffmpeg -i "$WAV_FILE" -c:a flac -compression_level "$COMPRESSION_LEVEL" -y -v error "$FLAC_FILE" | |
| if [ $? -eq 0 ] && [ -f "$FLAC_FILE" ] && [ -s "$FLAC_FILE" ]; then | |
| COMPRESSED_SIZE=$(stat -c%s "$FLAC_FILE" 2>/dev/null || stat -f%z "$FLAC_FILE") | |
| COMPRESSED_TOTAL=$((COMPRESSED_TOTAL + COMPRESSED_SIZE)) | |
| # Delete original WAV to save space | |
| rm -f "$WAV_FILE" | |
| if [ $? -eq 0 ]; then | |
| echo "✓ Success - Original WAV deleted" >> "$LOG_FILE" | |
| echo "✓ Success - Original WAV deleted" | |
| CONVERTED=$((CONVERTED + 1)) | |
| else | |
| echo "⚠ Converted OK but failed to delete original" >> "$LOG_FILE" | |
| echo "⚠ Converted OK but failed to delete original" | |
| CONVERTED=$((CONVERTED + 1)) | |
| fi | |
| else | |
| echo "✗ FAILED to convert $WAV_FILE" | tee -a "$LOG_FILE" | |
| FAILED=$((FAILED + 1)) | |
| fi | |
| # Show free space - get the mount point from base_dir | |
| MOUNT_POINT=$(df "$BASE_DIR" | tail -1 | awk '{print $1}') | |
| FREE_SPACE=$(df -h "$MOUNT_POINT" | tail -1 | awk '{print $4}') | |
| echo "Free space left: $FREE_SPACE" | |
| echo "" >> "$LOG_FILE" | |
| done | |
| echo "----------------------------------------" | tee -a "$LOG_FILE" | |
| echo "Conversion completed:" | tee -a "$LOG_FILE" | |
| echo " Success: $CONVERTED files" | tee -a "$LOG_FILE" | |
| echo " Failed: $FAILED files" | tee -a "$LOG_FILE" | |
| # Calculate total savings if bc is available | |
| if [ $CONVERTED -gt 0 ] && command -v bc >/dev/null 2>&1; then | |
| ORIGINAL_GB=$(echo "scale=2; $ORIGINAL_TOTAL / 1024 / 1024 / 1024" | bc) | |
| COMPRESSED_GB=$(echo "scale=2; $COMPRESSED_TOTAL / 1024 / 1024 / 1024" | bc) | |
| SAVED_GB=$(echo "scale=2; ($ORIGINAL_TOTAL - $COMPRESSED_TOTAL) / 1024 / 1024 / 1024" | bc) | |
| SAVED_PERCENT=$(echo "scale=1; ($ORIGINAL_TOTAL - $COMPRESSED_TOTAL) * 100 / $ORIGINAL_TOTAL" | bc) | |
| echo "" | tee -a "$LOG_FILE" | |
| echo "Space summary:" | tee -a "$LOG_FILE" | |
| echo " Original total: $ORIGINAL_GB GB" | tee -a "$LOG_FILE" | |
| echo " Compressed total: $COMPRESSED_GB GB" | tee -a "$LOG_FILE" | |
| echo " Space saved: $SAVED_GB GB ($SAVED_PERCENT%)" | tee -a "$LOG_FILE" | |
| elif [ $CONVERTED -gt 0 ]; then | |
| ORIGINAL_GB=$((ORIGINAL_TOTAL / 1024 / 1024 / 1024)) | |
| COMPRESSED_GB=$((COMPRESSED_TOTAL / 1024 / 1024 / 1024)) | |
| SAVED_GB=$(((ORIGINAL_TOTAL - COMPRESSED_TOTAL) / 1024 / 1024 / 1024)) | |
| echo "" | tee -a "$LOG_FILE" | |
| echo "Space summary (approx):" | tee -a "$LOG_FILE" | |
| echo " Original total: ~$ORIGINAL_GB GB" | tee -a "$LOG_FILE" | |
| echo " Compressed total: ~$COMPRESSED_GB GB" | tee -a "$LOG_FILE" | |
| echo " Space saved: ~$SAVED_GB GB" | tee -a "$LOG_FILE" | |
| fi | |
| echo "" >> "$LOG_FILE" | |
| echo "Conversion finished at $(date)" >> "$LOG_FILE" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment