Skip to content

Instantly share code, notes, and snippets.

@martylouis
Last active March 10, 2025 17:18
Show Gist options
  • Select an option

  • Save martylouis/fa1691991bdcf5035da2792a111c352b to your computer and use it in GitHub Desktop.

Select an option

Save martylouis/fa1691991bdcf5035da2792a111c352b to your computer and use it in GitHub Desktop.
MP3 Archive Script for WordPress

MP3 Archive Script for WordPress

A WP-CLI script to archive MP3 files from your WordPress media library, create a ZIP file of the archive, and generate detailed logs.

Overview

This script helps WordPress administrators manage large collections of MP3 files by:

  1. Finding all MP3 files in the WordPress media library
  2. Creating a dated archive in a dedicated uploads directory
  3. Generating a ZIP file of all archived MP3s
  4. Creating a detailed CSV log of the operation
  5. Adding both the ZIP and log files to the media library for easy access
  6. Optionally deleting the original files after archiving

Requirements

  • WordPress installation with WP-CLI
  • Bash shell environment
  • zip command installed on the server
  • Sufficient disk space for the archive and ZIP file

Optional Dependencies

  • bc command for more precise file size formatting (will use integer math if not available)

Installation

  1. Download the script to your WordPress root directory:

    wget -O archive_mp3_files.sh https://gist.githubusercontent.com/martylouis/fa1691991bdcf5035da2792a111c352b/raw/archive_mp3_files.sh
  2. Make the script executable:

    chmod +x archive_mp3_files.sh

Usage

Run the script from your WordPress root directory (where wp-config.php is located):

# Archive MP3 files without deleting originals
./archive_mp3_files.sh

# Archive MP3 files and delete originals
./archive_mp3_files.sh --delete

Command Line Options

  • --delete: Delete original MP3 files after successful archiving
  • --help: Display usage information

What the Script Does

  1. Identifies MP3 files in the WordPress media library (MIME type: audio/mpeg)
  2. Creates an archive directory at wp-content/uploads/mp3-archive/YYYY-MM-DD/
  3. Copies all MP3 files to the archive location
  4. Creates a ZIP file at wp-content/uploads/mp3-archive/mp3-archive-YYYY-MM-DD.zip
  5. Generates a CSV log file at wp-content/uploads/mp3-archive/mp3-archive-log-YYYY-MM-DD.csv
  6. Adds both files to the WordPress media library for easy access
  7. Displays direct URLs to both the ZIP and log files
  8. Optionally deletes original files if the --delete flag is used

Output

The script provides detailed output including:

  • Number of MP3 files found and archived
  • Total size of archived files
  • Direct URLs to the ZIP and log files
  • Media library IDs for the ZIP and log files
  • Summary of the operation

Directory Structure

The script creates a dedicated archive directory in your uploads folder:

wp-content/uploads/mp3-archive/
├── YYYY-MM-DD/                     # Directory containing archived MP3 files
│   ├── file1.mp3
│   ├── file2.mp3
│   └── ...
├── mp3-archive-YYYY-MM-DD.zip      # ZIP file of the archive
└── mp3-archive-log-YYYY-MM-DD.csv  # Log file of the operation

Important Note: Unlike regular WordPress media uploads that go into year/month-based folders (like wp-content/uploads/2025/03/), this script places all archives in a dedicated mp3-archive folder for better organization and easier access.

Log File Format

The CSV log file contains the following columns:

  • ID: WordPress attachment ID
  • Title: MP3 file title
  • File Path: Original file path
  • File Size: Size in bytes
  • Archive Path: Path in the archive
  • Status: Operation status
  • Date Archived: Timestamp

Performance Considerations

  • The script uses --skip-themes --skip-plugins flags with WP-CLI commands for better performance
  • For large media libraries, the script may take some time to complete
  • Ensure you have sufficient disk space for both the archive and the ZIP file

Examples

Basic Usage

./archive_mp3_files.sh

Output:

Finding MP3 files to archive...
Finding MP3 files in media library...
Found 25 MP3 files to archive.
Processing: 1234 - Sample MP3 File
...
ZIP file created successfully: wp-content/uploads/mp3-archive/mp3-archive-2025-03-10.zip (45.2 MB)
ZIP file URL: https://example.com/wp-content/uploads/mp3-archive/mp3-archive-2025-03-10.zip
Log file URL: https://example.com/wp-content/uploads/mp3-archive/mp3-archive-log-2025-03-10.csv
...
Operation completed:
- 25 MP3 files archived to wp-content/uploads/mp3-archive/2025-03-10
- Total size of archived files: 45.2 MB
- 0 operations failed
- Original files were preserved (use --delete to remove after archiving)

Archive and Delete

./archive_mp3_files.sh --delete

Troubleshooting

Common Issues

  1. "No MP3 files found"

    • Check if you have MP3 files in your media library
    • Verify they have the correct MIME type (audio/mpeg)
  2. "ZIP command not found"

    • Install the zip utility: apt-get install zip (Debian/Ubuntu) or yum install zip (CentOS/RHEL)
  3. "Failed to add ZIP file to media library"

    • Check WordPress permissions
    • Ensure the uploads directory is writable
  4. Integer file sizes instead of decimal

    • This happens when the bc command is not installed
    • The script will still work, but file sizes will be rounded (e.g., "45 MB" instead of "45.2 MB")
    • Install bc: apt-get install bc (Debian/Ubuntu) or yum install bc (CentOS/RHEL)
  5. WP-CLI errors

    • Make sure you're running the script from the WordPress root directory
    • Verify WP-CLI is installed and working: wp --info

Where to Run the Script

The script must be run from the root directory of your WordPress installation (where the wp-config.php file is located). Running it from any other directory will result in errors.

Security Considerations

  • The script should be run by users with appropriate permissions
  • The --delete option permanently removes files, so use with caution
  • Consider backing up your WordPress installation before using the --delete option

License

This script is provided under the MIT License.

Author

Marty Thierry (hey@martylouis.com)

Changelog

  • 1.0.0 (2025-03-10): Initial release

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

#!/bin/bash
# Configuration
DATE_STAMP=$(date +%Y-%m-%d)
UPLOADS_DIR="wp-content/uploads"
ARCHIVE_BASE_DIR="$UPLOADS_DIR/mp3-archive"
ARCHIVE_DIR="$ARCHIVE_BASE_DIR/$DATE_STAMP"
ZIP_FILE="$ARCHIVE_BASE_DIR/mp3-archive-$DATE_STAMP.zip"
ZIP_RELATIVE_PATH="mp3-archive/mp3-archive-$DATE_STAMP.zip"
LOG_FILE="$ARCHIVE_BASE_DIR/mp3-archive-log-$DATE_STAMP.csv"
LOG_RELATIVE_PATH="mp3-archive/mp3-archive-log-$DATE_STAMP.csv"
DELETE_AFTER_ARCHIVE=false # Set to true if you want to delete after archiving
# WP-CLI common flags to skip themes and plugins
WP_CLI_ARGS="--skip-themes --skip-plugins"
# Function to convert bytes to human-readable format
format_bytes() {
local bytes=$1
local hr_size=""
# Check if bc is available
if command -v bc >/dev/null 2>&1; then
# Use bc for floating-point arithmetic
if [ $bytes -ge 1073741824 ]; then
hr_size=$(echo "scale=2; $bytes/1073741824" | bc)" GB"
elif [ $bytes -ge 1048576 ]; then
hr_size=$(echo "scale=2; $bytes/1048576" | bc)" MB"
elif [ $bytes -ge 1024 ]; then
hr_size=$(echo "scale=2; $bytes/1024" | bc)" KB"
else
hr_size="$bytes bytes"
fi
else
# Fallback method without bc
if [ $bytes -ge 1073741824 ]; then
hr_size="$((bytes / 1073741824)) GB"
elif [ $bytes -ge 1048576 ]; then
hr_size="$((bytes / 1048576)) MB"
elif [ $bytes -ge 1024 ]; then
hr_size="$((bytes / 1024)) KB"
else
hr_size="$bytes bytes"
fi
fi
echo "$hr_size"
}
# Process command line arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
--delete) DELETE_AFTER_ARCHIVE=true ;;
--help) echo "Usage: $0 [--delete]"; echo " --delete: Delete MP3 files after archiving"; exit 0 ;;
*) echo "Unknown parameter: $1"; echo "Usage: $0 [--delete]"; exit 1 ;;
esac
shift
done
echo "Finding MP3 files to archive..."
# Create archive directories if they don't exist
mkdir -p "$ARCHIVE_DIR"
mkdir -p "$ARCHIVE_BASE_DIR"
# Create log file with headers
echo "ID,Title,File Path,File Size,Archive Path,Status,Date Archived" > "$LOG_FILE"
# Get list of MP3 attachments
echo "Finding MP3 files in media library..."
MP3_IDS=$(wp post list $WP_CLI_ARGS --post_type=attachment --post_mime_type=audio/mpeg --format=ids)
# Check if any MP3 files were found
if [ -z "$MP3_IDS" ]; then
echo "No MP3 files found. Exiting."
exit 0
fi
# Count MP3 files
MP3_COUNT=$(echo "$MP3_IDS" | wc -w)
echo "Found $MP3_COUNT MP3 files to archive."
# Process each MP3 file
ARCHIVED_COUNT=0
FAILED_COUNT=0
TOTAL_SIZE_BYTES=0
for ID in $MP3_IDS; do
# Get attachment details
TITLE=$(wp post get $WP_CLI_ARGS $ID --field=post_title)
FILE_PATH=$(wp eval $WP_CLI_ARGS "echo get_attached_file($ID);")
ARCHIVE_DATE=$(date +"%Y-%m-%d %H:%M:%S")
echo "Processing: $ID - $TITLE"
# Check if file exists
if [ -f "$FILE_PATH" ]; then
# Get file size for logging
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
FILE_SIZE=$(stat -f%z "$FILE_PATH")
else
# Linux and others
FILE_SIZE=$(stat -c%s "$FILE_PATH")
fi
# Add to total size
TOTAL_SIZE_BYTES=$((TOTAL_SIZE_BYTES + FILE_SIZE))
# Get directory structure
FILENAME=$(basename "$FILE_PATH")
# Copy file to archive location (flat structure in the archive)
cp "$FILE_PATH" "$ARCHIVE_DIR/"
if [ $? -eq 0 ]; then
STATUS="Archived successfully"
ARCHIVED_COUNT=$((ARCHIVED_COUNT + 1))
# Store archive info in metadata
wp post meta update $WP_CLI_ARGS $ID _mp3_archive_location "$ARCHIVE_DIR/$FILENAME"
wp post meta update $WP_CLI_ARGS $ID _mp3_archive_date "$ARCHIVE_DATE"
# Delete the file if requested
if [ "$DELETE_AFTER_ARCHIVE" = true ]; then
rm "$FILE_PATH"
if [ $? -eq 0 ]; then
STATUS="$STATUS and deleted from original location"
else
STATUS="$STATUS but deletion failed"
fi
fi
else
STATUS="Archive failed"
FAILED_COUNT=$((FAILED_COUNT + 1))
fi
else
FILE_SIZE="N/A"
STATUS="Original file not found"
FAILED_COUNT=$((FAILED_COUNT + 1))
fi
# Log the operation
echo "$ID,\"$TITLE\",\"$FILE_PATH\",$FILE_SIZE,\"$ARCHIVE_DIR/$FILENAME\",\"$STATUS\",\"$ARCHIVE_DATE\"" >> "$LOG_FILE"
done
# Create ZIP file of the archive
if [ $ARCHIVED_COUNT -gt 0 ]; then
echo "Creating ZIP file of the archive..."
# Check if zip command is available
if command -v zip >/dev/null 2>&1; then
# Create ZIP file
(cd "$ARCHIVE_BASE_DIR" && zip -r "$(basename "$ZIP_FILE")" "$(basename "$ARCHIVE_DIR")")
if [ $? -eq 0 ]; then
# Get ZIP file size
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
ZIP_SIZE=$(stat -f%z "$ZIP_FILE")
else
# Linux and others
ZIP_SIZE=$(stat -c%s "$ZIP_FILE")
fi
# Convert to human-readable format
ZIP_SIZE_HR=$(format_bytes $ZIP_SIZE)
# Get site URL
SITE_URL=$(wp option get $WP_CLI_ARGS siteurl)
echo "ZIP file created successfully: $ZIP_FILE ($ZIP_SIZE_HR)"
echo "ZIP file URL: $SITE_URL/$ZIP_RELATIVE_PATH"
echo "Log file URL: $SITE_URL/$LOG_RELATIVE_PATH"
# Instead of importing to media library (which would move the files),
# just create the attachment entries pointing to the existing files
# Register the ZIP file in the media library without moving it
ZIP_TITLE="MP3 Archive $DATE_STAMP"
ZIP_DESCRIPTION="Archive of $ARCHIVED_COUNT MP3 files created on $DATE_STAMP"
# Create attachment for ZIP file
ZIP_ID=$(wp db query $WP_CLI_ARGS --skip-column-names "INSERT INTO wp_posts (post_author, post_date, post_date_gmt, post_title, post_status, comment_status, ping_status, post_name, post_type, post_mime_type, guid) VALUES (1, NOW(), NOW(), '$ZIP_TITLE', 'inherit', 'open', 'closed', 'mp3-archive-$DATE_STAMP', 'attachment', 'application/zip', '$SITE_URL/$ZIP_RELATIVE_PATH') RETURNING ID;")
if [ -n "$ZIP_ID" ]; then
# Update attachment metadata
wp post meta update $WP_CLI_ARGS $ZIP_ID _wp_attached_file "$ZIP_RELATIVE_PATH"
wp post update $WP_CLI_ARGS $ZIP_ID --post_content="$ZIP_DESCRIPTION" --post_excerpt="$ZIP_DESCRIPTION"
echo "ZIP file added to media library. ID: $ZIP_ID"
echo "Direct download link: $SITE_URL/$UPLOADS_DIR/$ZIP_RELATIVE_PATH"
else
echo "Failed to add ZIP file to media library, but it's still accessible at: $SITE_URL/$UPLOADS_DIR/$ZIP_RELATIVE_PATH"
fi
# Register the log file in the media library without moving it
LOG_TITLE="MP3 Archive Log $DATE_STAMP"
LOG_DESCRIPTION="Log of MP3 archive operation performed on $DATE_STAMP"
# Create attachment for log file
LOG_ID=$(wp db query $WP_CLI_ARGS --skip-column-names "INSERT INTO wp_posts (post_author, post_date, post_date_gmt, post_title, post_status, comment_status, ping_status, post_name, post_type, post_mime_type, guid) VALUES (1, NOW(), NOW(), '$LOG_TITLE', 'inherit', 'open', 'closed', 'mp3-archive-log-$DATE_STAMP', 'attachment', 'text/csv', '$SITE_URL/$LOG_RELATIVE_PATH') RETURNING ID;")
if [ -n "$LOG_ID" ]; then
# Update attachment metadata
wp post meta update $WP_CLI_ARGS $LOG_ID _wp_attached_file "$LOG_RELATIVE_PATH"
wp post update $WP_CLI_ARGS $LOG_ID --post_content="$LOG_DESCRIPTION" --post_excerpt="$LOG_DESCRIPTION"
echo "Log file added to media library. ID: $LOG_ID"
echo "Direct access to log: $SITE_URL/$UPLOADS_DIR/$LOG_RELATIVE_PATH"
else
echo "Failed to add log file to media library, but it's still accessible at: $SITE_URL/$UPLOADS_DIR/$LOG_RELATIVE_PATH"
fi
else
echo "Failed to create ZIP file."
fi
else
echo "ZIP command not found. Please install zip utility to create ZIP archives."
fi
fi
# Convert total size to human-readable format
TOTAL_SIZE_HR=$(format_bytes $TOTAL_SIZE_BYTES)
echo "Operation completed:"
echo "- $ARCHIVED_COUNT MP3 files archived to $ARCHIVE_DIR"
echo "- Total size of archived files: $TOTAL_SIZE_HR"
echo "- $FAILED_COUNT operations failed"
if [ "$DELETE_AFTER_ARCHIVE" = true ]; then
echo "- Original files were deleted after archiving"
else
echo "- Original files were preserved (use --delete to remove after archiving)"
fi
echo "Log file: $LOG_FILE"
echo "Log file URL: $SITE_URL/$UPLOADS_DIR/$LOG_RELATIVE_PATH"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment