Skip to content

Instantly share code, notes, and snippets.

@raghavmri
Last active August 21, 2025 13:33
Show Gist options
  • Save raghavmri/f901b3ff0daee01165285587f84b40e7 to your computer and use it in GitHub Desktop.
Save raghavmri/f901b3ff0daee01165285587f84b40e7 to your computer and use it in GitHub Desktop.
#!/bin/bash
set -euo pipefail
# --- Helper: detect package manager ---
detect_pkg_mgr() {
if command -v apt-get &>/dev/null; then echo "apt-get"
elif command -v yum &>/dev/null; then echo "yum"
elif command -v dnf &>/dev/null; then echo "dnf"
elif command -v pacman &>/dev/null; then echo "pacman"
elif command -v zypper &>/dev/null; then echo "zypper"
else echo "unknown"; fi
}
# --- Helper: ensure a command exists ---
ensure_command() {
local cmd="$1"; local pkg="$2"
if command -v "$cmd" &>/dev/null; then return 0; fi
echo "$cmd not found. Attempting to install..."
PKG_MGR="$(detect_pkg_mgr)"
case "$PKG_MGR" in
apt-get) sudo apt-get update && sudo apt-get install -y "$pkg" ;;
yum) sudo yum install -y "$pkg" ;;
dnf) sudo dnf install -y "$pkg" ;;
pacman) sudo pacman -Sy --noconfirm "$pkg" ;;
zypper) sudo zypper install -y "$pkg" ;;
*) echo "Could not detect package manager. Please install $cmd manually." >&2; exit 1 ;;
esac
}
# Ensure ffmpeg exists
ensure_command ffmpeg ffmpeg
# Detect CPU count
if command -v nproc &>/dev/null; then
MAX_PROCS=$(nproc)
elif [ -f /proc/cpuinfo ]; then
MAX_PROCS=$(grep -c '^processor' /proc/cpuinfo)
else
MAX_PROCS=4
fi
echo "Using $MAX_PROCS parallel jobs."
# Output directory (absolute path)
CONVERTED_DEST="$(pwd)/m4a_outputs"
mkdir -p "$CONVERTED_DEST"
echo "Output directory: $CONVERTED_DEST"
# Create log directory
LOG_DIR="$(pwd)/conversion_logs"
mkdir -p "$LOG_DIR"
echo "Log directory: $LOG_DIR"
# Find MP4 files first
echo "Searching for MP4 files..."
MP4_FILES="$(mktemp)"
find "." -type f -iname '*.mp4' > "$MP4_FILES"
FILE_COUNT=$(wc -l < "$MP4_FILES")
if [ "$FILE_COUNT" -eq 0 ]; then
echo "No MP4 files found in current directory and subdirectories."
rm -f "$MP4_FILES"
exit 0
fi
echo "Found $FILE_COUNT MP4 files to convert:"
cat "$MP4_FILES"
echo "===================="
# Create results file
RESULTS="$(mktemp)"
echo "Starting parallel conversion..."
# Process files with better parallel handling
echo "Using xargs for processing..."
# Create a wrapper script for the function
WRAPPER_SCRIPT="$(mktemp)"
cat > "$WRAPPER_SCRIPT" << 'EOF'
#!/bin/bash
file="$1"
CONVERTED_DEST="$2"
LOG_DIR="$3"
filename="$(basename "$file")"
base="${filename%.*}"
out="$CONVERTED_DEST/${base}.m4a"
log_file="$LOG_DIR/${base}.log"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting: $file" | tee -a "$log_file"
# Check if input file exists and is readable
if [ ! -f "$file" ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Input file not found: $file" | tee -a "$log_file"
echo "FAIL:$file:File not found"
exit 1
fi
# Run ffmpeg with full logging
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Converting: $file -> $out" | tee -a "$log_file"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Running: ffmpeg -i \"$file\" -vn -c:a aac -b:a 192k -y \"$out\"" | tee -a "$log_file"
if ffmpeg -i "$file" -vn -c:a aac -b:a 192k -y "$out" 2>&1 | tee -a "$log_file"; then
if [ -f "$out" ] && [ -s "$out" ]; then
local size=$(stat -f%z "$out" 2>/dev/null || stat -c%s "$out" 2>/dev/null || echo "unknown")
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ SUCCESS: $file -> $out (size: ${size} bytes)" | tee -a "$log_file"
echo "OK:$file:$out:$size"
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ❌ FAILED: Output file is empty or missing: $out" | tee -a "$log_file"
echo "FAIL:$file:Output file empty or missing"
fi
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ❌ FAILED: ffmpeg error for $file" | tee -a "$log_file"
echo "FAIL:$file:ffmpeg error"
fi
EOF
chmod +x "$WRAPPER_SCRIPT"
# Process files using the wrapper script
cat "$MP4_FILES" | head -5 | xargs -I {} -P "$MAX_PROCS" -n 1 "$WRAPPER_SCRIPT" {} "$CONVERTED_DEST" "$LOG_DIR" > "$RESULTS" 2>&1
# Clean up wrapper script
rm -f "$WRAPPER_SCRIPT"
echo "Conversion jobs completed. Analyzing results..."
# Count results and show detailed summary
SUCCESS=0
FAILED=0
echo ""
echo "=== CONVERSION SUMMARY ==="
while IFS=: read -r status file rest; do
case "$status" in
"OK")
SUCCESS=$((SUCCESS + 1))
echo "✅ $file"
;;
"FAIL")
FAILED=$((FAILED + 1))
echo "❌ $file ($rest)"
;;
esac
done < "$RESULTS"
echo ""
echo "=== FINAL RESULTS ==="
echo "Successfully converted: $SUCCESS"
echo "Failed conversions: $FAILED"
echo "Total processed: $((SUCCESS + FAILED))"
# Show output directory contents
echo ""
echo "=== OUTPUT DIRECTORY CONTENTS ==="
if [ -d "$CONVERTED_DEST" ]; then
ls -la "$CONVERTED_DEST"/ || echo "Directory is empty"
else
echo "Output directory does not exist!"
fi
echo ""
echo "=== LOG FILES ==="
if [ -d "$LOG_DIR" ]; then
echo "Conversion logs available in: $LOG_DIR"
ls -la "$LOG_DIR"/ || echo "No log files found"
# Show last few lines of failed conversions
if [ "$FAILED" -gt 0 ]; then
echo ""
echo "=== SAMPLE ERROR LOGS ==="
for logfile in "$LOG_DIR"/*.log; do
if [ -f "$logfile" ] && grep -q "FAILED\|ERROR" "$logfile"; then
echo "--- Last 10 lines of $(basename "$logfile") ---"
tail -10 "$logfile"
echo ""
fi
done
fi
else
echo "Log directory does not exist!"
fi
# Cleanup temp files
rm -f "$MP4_FILES" "$RESULTS"
echo "All outputs are in: $CONVERTED_DEST"
echo "All logs are in: $LOG_DIR"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment