<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>wp-cli media library Archives - WP-CLI Mastery</title>
	<atom:link href="https://wpclimastery.com/blog/tag/wp-cli-media-library/feed/" rel="self" type="application/rss+xml" />
	<link>https://wpclimastery.com/blog/tag/wp-cli-media-library/</link>
	<description>Automate WordPress Like a DevOps Pro.</description>
	<lastBuildDate>Mon, 24 Nov 2025 11:16:24 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://wpclimastery.com/wp-content/uploads/2025/11/cropped-favicon-32x32.webp</url>
	<title>wp-cli media library Archives - WP-CLI Mastery</title>
	<link>https://wpclimastery.com/blog/tag/wp-cli-media-library/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WordPress Media Library Automation with WP-CLI: Complete Guide</title>
		<link>https://wpclimastery.com/blog/wordpress-media-library-automation-with-wp-cli-complete-guide/</link>
					<comments>https://wpclimastery.com/blog/wordpress-media-library-automation-with-wp-cli-complete-guide/#respond</comments>
		
		<dc:creator><![CDATA[Krasen]]></dc:creator>
		<pubDate>Mon, 24 Nov 2025 11:16:24 +0000</pubDate>
				<category><![CDATA[Content Management Automation]]></category>
		<category><![CDATA[bulk image upload]]></category>
		<category><![CDATA[wordpress media automation]]></category>
		<category><![CDATA[wordpress media management]]></category>
		<category><![CDATA[wp-cli media library]]></category>
		<category><![CDATA[wpcli media commands]]></category>
		<guid isPermaLink="false">https://wpclimastery.com/?p=218</guid>

					<description><![CDATA[<p>Managing thousands of media files in WordPress becomes tedious through the dashboard. WP-CLI transforms media library management from a manual chore into an automated, scriptable process, saving hours of work...</p>
<p>The post <a href="https://wpclimastery.com/blog/wordpress-media-library-automation-with-wp-cli-complete-guide/">WordPress Media Library Automation with WP-CLI: Complete Guide</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Managing thousands of media files in WordPress becomes tedious through the dashboard. WP-CLI transforms media library management from a manual chore into an automated, scriptable process, saving hours of work and ensuring consistency across large image collections.</p>



<h2 class="wp-block-heading" id="understanding-wordpress-media-management">Understanding WordPress Media Management</h2>



<p>WordPress stores media files in the uploads directory while maintaining database records for each attachment. These records include metadata like file paths, dimensions, captions, alt text, and custom fields. Effective media automation requires managing both filesystem operations and database records simultaneously.</p>



<p>The media library grows rapidly on content-heavy sites. Manual management becomes impractical when dealing with bulk operations like regenerating thumbnails, updating metadata, or migrating files. WP-CLI provides the tools to automate these tasks efficiently.</p>



<h2 class="wp-block-heading" id="bulk-image-import-and-upload">Bulk Image Import and Upload</h2>



<p>Traditional WordPress uploads through the dashboard are slow for large batches. WP-CLI enables efficient bulk imports from local directories or remote URLs.</p>



<pre class="wp-block-code"><code><em># Import single image</em>
wp media import /path/to/image.jpg --post_id=123 --title="Image Title" --alt="Alt text"

<em># Import all images from a directory</em>
wp media import /path/to/images/*.jpg --porcelain

<em># Import images and assign to specific post</em>
for file in /path/to/images/*.jpg; do
    wp media import "$file" --post_id=456 --featured_image
done

<em># Import from URL</em>
wp media import https://example.com/image.jpg --title="Remote Image"

<em># Bulk import with metadata from CSV</em>
while IFS=',' read -r filepath title alt caption; do
    wp media import "$filepath" --title="$title" --alt="$alt" --caption="$caption"
done &lt; images.csv
</code></pre>



<p>Create a comprehensive bulk import script:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># bulk-media-import.sh - Import media with metadata</em>

set -euo pipefail

WP_PATH="/var/www/html"
IMPORT_DIR="/path/to/import"
LOG_FILE="/var/log/media-import-$(date +%Y%m%d).log"

log() {
    echo "&#91;$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

<em># Check if import directory exists</em>
if &#91; ! -d "$IMPORT_DIR" ]; then
    log "ERROR: Import directory does not exist: $IMPORT_DIR"
    exit 1
fi

log "Starting bulk media import from $IMPORT_DIR"

<em># Count files to process</em>
TOTAL_FILES=$(find "$IMPORT_DIR" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" \) | wc -l)
log "Found $TOTAL_FILES files to import"

IMPORTED=0
FAILED=0

<em># Process each image</em>
find "$IMPORT_DIR" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" \) | while read -r file; do
    FILENAME=$(basename "$file")
    log "Processing: $FILENAME"

    <em># Extract metadata from filename (example: product-123-description.jpg)</em>
    if &#91;&#91; "$FILENAME" =~ ^(&#91;^-]+)-(&#91;0-9]+)-(.+)\..+$ ]]; then
        TYPE="${BASH_REMATCH&#91;1]}"
        POST_ID="${BASH_REMATCH&#91;2]}"
        DESCRIPTION="${BASH_REMATCH&#91;3]}"

        <em># Import with metadata</em>
        if ATTACHMENT_ID=$(wp --path="$WP_PATH" media import "$file" \
            --post_id="$POST_ID" \
            --title="$DESCRIPTION" \
            --alt="$DESCRIPTION" \
            --porcelain 2&gt;&amp;1); then

            log "SUCCESS: Imported $FILENAME (ID: $ATTACHMENT_ID)"
            ((IMPORTED++))

            <em># Move processed file to archive</em>
            mkdir -p "$IMPORT_DIR/processed"
            mv "$file" "$IMPORT_DIR/processed/"
        else
            log "ERROR: Failed to import $FILENAME: $ATTACHMENT_ID"
            ((FAILED++))
        fi
    else
        <em># Import without post association</em>
        if ATTACHMENT_ID=$(wp --path="$WP_PATH" media import "$file" --porcelain 2&gt;&amp;1); then
            log "SUCCESS: Imported $FILENAME (ID: $ATTACHMENT_ID)"
            ((IMPORTED++))
            mv "$file" "$IMPORT_DIR/processed/"
        else
            log "ERROR: Failed to import $FILENAME"
            ((FAILED++))
        fi
    fi
done

log "Import completed. Imported: $IMPORTED, Failed: $FAILED"
</code></pre>



<h2 class="wp-block-heading" id="thumbnail-regeneration">Thumbnail Regeneration</h2>



<p>Changing themes or image sizes requires regenerating thumbnails for existing images. WP-CLI makes this process efficient.</p>



<pre class="wp-block-code"><code><em># Regenerate all thumbnails</em>
wp media regenerate --yes

<em># Regenerate only missing thumbnails</em>
wp media regenerate --only-missing --yes

<em># Regenerate for specific image sizes</em>
wp media regenerate --image_size=thumbnail,medium --yes

<em># Regenerate images from specific year</em>
wp media regenerate --yes $(wp post list --post_type=attachment --year=2024 --format=ids)

<em># Regenerate for specific attachment IDs</em>
wp media regenerate 123 456 789 --yes

<em># Skip thumbnail generation (only register sizes)</em>
wp media regenerate --skip-delete --yes
</code></pre>



<p>Automated thumbnail regeneration with progress tracking:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># regenerate-thumbnails.sh - Regenerate with progress tracking</em>

WP_PATH="/var/www/html"

<em># Get total attachment count</em>
TOTAL=$(wp --path="$WP_PATH" post list --post_type=attachment --post_mime_type=image --format=count)
echo "Total images to process: $TOTAL"

<em># Process in batches</em>
BATCH_SIZE=50
PROCESSED=0

while &#91; $PROCESSED -lt $TOTAL ]; do
    echo "Processing batch: $PROCESSED to $((PROCESSED + BATCH_SIZE))"

    <em># Get batch of attachment IDs</em>
    IDS=$(wp --path="$WP_PATH" post list \
        --post_type=attachment \
        --post_mime_type=image \
        --posts_per_page=$BATCH_SIZE \
        --offset=$PROCESSED \
        --format=ids)

    if &#91; -n "$IDS" ]; then
        wp --path="$WP_PATH" media regenerate $IDS --yes
    fi

    PROCESSED=$((PROCESSED + BATCH_SIZE))
    echo "Progress: $PROCESSED / $TOTAL ($(( PROCESSED * 100 / TOTAL ))%)"

    <em># Sleep to avoid overloading server</em>
    sleep 2
done

echo "Thumbnail regeneration completed"
</code></pre>



<h2 class="wp-block-heading" id="image-optimization">Image Optimization</h2>



<p>Optimize images to reduce file sizes and improve site performance.</p>



<pre class="wp-block-code"><code><em># Install optimization plugin</em>
wp plugin install ewww-image-optimizer --activate

<em># Configure optimization settings</em>
wp option update ewww_image_optimizer_auto true
wp option update ewww_image_optimizer_jpg_level 30
wp option update ewww_image_optimizer_png_level 10

<em># Optimize all existing images</em>
wp ewwwio optimize all

<em># Optimize specific images</em>
wp media regenerate --yes --image_size=full
</code></pre>



<p>Alternative optimization with ImageMagick:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># optimize-images.sh - Optimize images using ImageMagick</em>

WP_PATH="/var/www/html"
UPLOADS_DIR="$WP_PATH/wp-content/uploads"
QUALITY=85

echo "Optimizing images in $UPLOADS_DIR"

<em># Find and optimize JPG files</em>
find "$UPLOADS_DIR" -type f -iname "*.jpg" -o -iname "*.jpeg" | while read -r file; do
    echo "Optimizing: $file"

    <em># Create backup</em>
    cp "$file" "$file.backup"

    <em># Optimize with ImageMagick</em>
    convert "$file" -strip -interlace Plane -quality $QUALITY "$file"

    <em># Check if optimization saved space</em>
    ORIGINAL_SIZE=$(stat -f%z "$file.backup" 2&gt;/dev/null || stat -c%s "$file.backup")
    NEW_SIZE=$(stat -f%z "$file" 2&gt;/dev/null || stat -c%s "$file")

    if &#91; "$NEW_SIZE" -lt "$ORIGINAL_SIZE" ]; then
        SAVED=$((ORIGINAL_SIZE - NEW_SIZE))
        echo "Saved: $SAVED bytes"
        rm "$file.backup"
    else
        <em># Restore if no improvement</em>
        mv "$file.backup" "$file"
    fi
done

<em># Optimize PNG files</em>
find "$UPLOADS_DIR" -type f -iname "*.png" | while read -r file; do
    echo "Optimizing: $file"
    optipng -o2 "$file"
done

echo "Image optimization completed"
</code></pre>



<h2 class="wp-block-heading" id="metadata-management">Metadata Management</h2>



<p>Update alt text, captions, titles, and descriptions programmatically.</p>



<pre class="wp-block-code"><code><em># Update alt text for specific attachment</em>
wp post meta update 123 _wp_attachment_image_alt "New alt text"

<em># Update attachment title</em>
wp post update 123 --post_title="New Title"

<em># Update caption</em>
wp post update 123 --post_excerpt="New caption"

<em># Update description</em>
wp post update 123 --post_content="New description"

<em># Bulk update alt text based on filename</em>
for id in $(wp post list --post_type=attachment --format=ids); do
    FILENAME=$(wp post get $id --field=post_title)
    <em># Generate alt text from filename</em>
    ALT_TEXT=$(echo "$FILENAME" | sed 's/-/ /g' | sed 's/\.&#91;^.]*$//')
    wp post meta update $id _wp_attachment_image_alt "$ALT_TEXT"
    echo "Updated alt text for ID $id: $ALT_TEXT"
done
</code></pre>



<p>Comprehensive metadata update script:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># update-media-metadata.sh - Bulk metadata updates</em>

WP_PATH="/var/www/html"
CSV_FILE="media-metadata.csv"

<em># CSV format: attachment_id,title,alt,caption,description</em>

while IFS=',' read -r id title alt caption description; do
    <em># Skip header row</em>
    if &#91; "$id" == "attachment_id" ]; then
        continue
    fi

    echo "Updating attachment ID: $id"

    <em># Update title</em>
    if &#91; -n "$title" ]; then
        wp --path="$WP_PATH" post update "$id" --post_title="$title"
    fi

    <em># Update alt text</em>
    if &#91; -n "$alt" ]; then
        wp --path="$WP_PATH" post meta update "$id" _wp_attachment_image_alt "$alt"
    fi

    <em># Update caption</em>
    if &#91; -n "$caption" ]; then
        wp --path="$WP_PATH" post update "$id" --post_excerpt="$caption"
    fi

    <em># Update description</em>
    if &#91; -n "$description" ]; then
        wp --path="$WP_PATH" post update "$id" --post_content="$description"
    fi

    echo "Metadata updated for attachment $id"
done &lt; "$CSV_FILE"

echo "Bulk metadata update completed"
</code></pre>



<h2 class="wp-block-heading" id="media-library-cleanup">Media Library Cleanup</h2>



<p>Remove unused, broken, or duplicate media files.</p>



<pre class="wp-block-code"><code><em># Find unattached media (not assigned to any post)</em>
wp post list --post_type=attachment --post_parent=0 --format=ids

<em># Delete unattached media</em>
wp post delete $(wp post list --post_type=attachment --post_parent=0 --format=ids) --force

<em># Find and remove broken attachments (missing files)</em>
for id in $(wp post list --post_type=attachment --format=ids); do
    FILE=$(wp post meta get $id _wp_attached_file)
    FULL_PATH="/var/www/html/wp-content/uploads/$FILE"

    if &#91; ! -f "$FULL_PATH" ]; then
        echo "Missing file for attachment $id: $FILE"
        wp post delete $id --force
    fi
done

<em># Find duplicate images by hash</em>
find /var/www/html/wp-content/uploads -type f -exec md5sum {} + | \
    sort | \
    uniq -w32 -D
</code></pre>



<p>Automated cleanup script:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># media-cleanup.sh - Clean up media library</em>

set -euo pipefail

WP_PATH="/var/www/html"
DRY_RUN=false

if &#91; "${1:-}" == "--dry-run" ]; then
    DRY_RUN=true
    echo "Running in dry-run mode (no changes will be made)"
fi

echo "=== Media Library Cleanup ==="

<em># Find orphaned attachments (older than 30 days, never used)</em>
echo "Finding orphaned attachments..."
ORPHANED_IDS=$(wp --path="$WP_PATH" post list \
    --post_type=attachment \
    --post_parent=0 \
    --post_status=inherit \
    --format=ids \
    --date_query='&#91;{"before":"30 days ago"}]')

if &#91; -n "$ORPHANED_IDS" ]; then
    ORPHANED_COUNT=$(echo "$ORPHANED_IDS" | wc -w)
    echo "Found $ORPHANED_COUNT orphaned attachments"

    if &#91; "$DRY_RUN" == false ]; then
        echo "Deleting orphaned attachments..."
        wp --path="$WP_PATH" post delete $ORPHANED_IDS --force
    else
        echo "Would delete: $ORPHANED_IDS"
    fi
else
    echo "No orphaned attachments found"
fi

<em># Check for broken file links</em>
echo "Checking for broken file links..."
UPLOADS_DIR="$WP_PATH/wp-content/uploads"
BROKEN_COUNT=0

for id in $(wp --path="$WP_PATH" post list --post_type=attachment --format=ids); do
    FILE=$(wp --path="$WP_PATH" post meta get $id _wp_attached_file)
    FULL_PATH="$UPLOADS_DIR/$FILE"

    if &#91; ! -f "$FULL_PATH" ]; then
        echo "Broken attachment $id: $FILE"
        ((BROKEN_COUNT++))

        if &#91; "$DRY_RUN" == false ]; then
            wp --path="$WP_PATH" post delete $id --force
        fi
    fi
done

echo "Found $BROKEN_COUNT broken attachments"

<em># Remove unused image sizes</em>
echo "Cleaning up unused image sizes..."
find "$UPLOADS_DIR" -type f -regextype posix-extended \
    -regex '.*-&#91;0-9]+x&#91;0-9]+\.(jpg|jpeg|png|gif)$' \
    -mtime +90 | head -10

echo "Media cleanup completed"
</code></pre>



<h2 class="wp-block-heading" id="featured-image-management">Featured Image Management</h2>



<p>Automate featured image assignment across posts.</p>



<pre class="wp-block-code"><code><em># Set featured image for specific post</em>
wp post meta set 123 _thumbnail_id 456

<em># Assign first attached image as featured image</em>
for post_id in $(wp post list --post_type=post --format=ids); do
    <em># Get first attachment</em>
    ATTACHMENT_ID=$(wp post list --post_type=attachment --post_parent=$post_id --posts_per_page=1 --format=ids)

    if &#91; -n "$ATTACHMENT_ID" ]; then
        wp post meta set $post_id _thumbnail_id $ATTACHMENT_ID
        echo "Set featured image for post $post_id"
    fi
done

<em># Download and set featured image from URL</em>
URL="https://example.com/image.jpg"
POST_ID=123

ATTACHMENT_ID=$(wp media import "$URL" --post_id=$POST_ID --porcelain)
wp post meta set $POST_ID _thumbnail_id $ATTACHMENT_ID
</code></pre>



<h2 class="wp-block-heading" id="file-organization-and-migration">File Organization and Migration</h2>



<p>Reorganize uploads directory or migrate media between sites.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># organize-uploads.sh - Organize uploads by year/month</em>

WP_PATH="/var/www/html"
UPLOADS_DIR="$WP_PATH/wp-content/uploads"

<em># Enable year/month organization</em>
wp --path="$WP_PATH" option update uploads_use_yearmonth_folders 1

<em># Migrate existing files to year/month structure</em>
for id in $(wp --path="$WP_PATH" post list --post_type=attachment --format=ids); do
    <em># Get attachment date</em>
    DATE=$(wp --path="$WP_PATH" post get $id --field=post_date)
    YEAR=$(date -d "$DATE" +%Y 2&gt;/dev/null || echo "")
    MONTH=$(date -d "$DATE" +%m 2&gt;/dev/null || echo "")

    if &#91; -n "$YEAR" ] &amp;&amp; &#91; -n "$MONTH" ]; then
        <em># Get current file path</em>
        FILE=$(wp --path="$WP_PATH" post meta get $id _wp_attached_file)

        <em># Skip if already organized</em>
        if &#91;&#91; "$FILE" =~ ^&#91;0-9]{4}/&#91;0-9]{2}/ ]]; then
            continue
        fi

        <em># Create target directory</em>
        TARGET_DIR="$UPLOADS_DIR/$YEAR/$MONTH"
        mkdir -p "$TARGET_DIR"

        <em># Move file</em>
        BASENAME=$(basename "$FILE")
        if &#91; -f "$UPLOADS_DIR/$FILE" ]; then
            mv "$UPLOADS_DIR/$FILE" "$TARGET_DIR/$BASENAME"

            <em># Update database</em>
            NEW_PATH="$YEAR/$MONTH/$BASENAME"
            wp --path="$WP_PATH" post meta update $id _wp_attached_file "$NEW_PATH"

            echo "Moved: $FILE -&gt; $NEW_PATH"
        fi
    fi
done

echo "Upload organization completed"
</code></pre>



<h2 class="wp-block-heading" id="media-export-and-backup">Media Export and Backup</h2>



<p>Create backups of media library with metadata.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># backup-media.sh - Backup media library</em>

WP_PATH="/var/www/html"
BACKUP_DIR="/backups/media"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_PATH="$BACKUP_DIR/media-backup-$TIMESTAMP"

mkdir -p "$BACKUP_PATH"

echo "Starting media backup..."

<em># Export attachment metadata</em>
wp --path="$WP_PATH" post list \
    --post_type=attachment \
    --format=json &gt; "$BACKUP_PATH/attachments-metadata.json"

<em># Backup uploads directory</em>
tar -czf "$BACKUP_PATH/uploads.tar.gz" \
    -C "$WP_PATH/wp-content" \
    uploads/

<em># Get backup size</em>
BACKUP_SIZE=$(du -sh "$BACKUP_PATH" | cut -f1)
echo "Backup completed: $BACKUP_SIZE at $BACKUP_PATH"

<em># Clean old backups (keep last 7)</em>
ls -t "$BACKUP_DIR" | tail -n +8 | xargs -I {} rm -rf "$BACKUP_DIR/{}"
</code></pre>



<h2 class="wp-block-heading" id="related-links">Related Links</h2>



<ul class="wp-block-list">
<li><a href="https://developer.wordpress.org/cli/commands/media/">WP-CLI Media Commands</a></li>



<li><a href="https://wordpress.org/support/article/media-library-screen/">WordPress Media Library Guide</a></li>



<li><a href="https://developer.wordpress.org/advanced-administration/performance/optimization/">Image Optimization Best Practices</a></li>



<li><a href="https://developer.wordpress.org/reference/functions/wp_generate_attachment_metadata/">WordPress Attachment Metadata</a></li>



<li><a href="https://docs.ewww.io/">EWWW Image Optimizer Documentation</a></li>
</ul>
<p>The post <a href="https://wpclimastery.com/blog/wordpress-media-library-automation-with-wp-cli-complete-guide/">WordPress Media Library Automation with WP-CLI: Complete Guide</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wpclimastery.com/blog/wordpress-media-library-automation-with-wp-cli-complete-guide/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
