How to restore/regenerate missing attachment metadata in WordPress

Recover missing attachment metadata

Recently, one of my clients encountered a significant issue when they lost their entire wp_postmeta table, and unfortunately, there was no backup available. Our only viable option was to recreate the table and manually add the post meta. However, this posed a new problem. The website has thousands of media files, which became unusable as featured images or anywhere on the site due to their missing metadata. Essentially, the files were present, but WordPress couldn’t utilize them in posts without their associated metadata, a vital piece stored within the wp_postmeta table.

At first, I considered using a popular plugin made for regenerating thumbnails, assuming it would perform the necessary checks and recreate the missing metadata from the files. However, the plugin failed to regenerate either the metadata or the thumbnails. So, I decided to write a custom code to solve this issue.

The initial part of the code involved hooking a function into wp_head and checking for a specific query parameter. This approach allowed me to trigger the function by simply adding /?regenerate_attach_meta=1 to the website’s URL. I chose this method because it was a one-time solution, and I planned to remove the code after the metadata was restored.

Here’s the first part of the code:

add_action('wp_head', 'ak_regenerate_attach_meta');
function ak_regenerate_attach_meta()
{
    // Check for the presence of the query parameter `regenerate_attach_meta` in the URL
    if (!isset($_GET['regenerate_attach_meta'])) {
        return;
    }
    
    // Implement our code when the `regenerate_attach_meta` query parameter is detected
}

The reason I opted for the wp_head hook was to facilitate debugging using functions like var_dump and print_r with proper styling in place. However, the code can function effectively with other hooks that execute earlier in the process as well.

Moving forward, the next step involved running a meta query to retrieve all the attachments lacking their metadata:

$attachments = get_posts(
    array(
        'post_type' => 'attachment',
        'posts_per_page' => 100,
        'fields' => 'ids',
        'meta_query' => array(
            array(
                'key' => '_wp_attachment_metadata',
                'compare' => 'NOT EXISTS'
            ),
        ),
    )
);

It’s worth noting that I set posts_per_page to 100 to avoid potential timeout errors during script execution. You can adjust this number based on the execution timeout settings on your specific website.

Next step is to make sure certain essential functions are loaded, such as wp_crop_image and wp_read_video_metadata, which are usually only available in the wp-admin section. Here’s how to ensure they’re accessible on the front end:

// Ensure `wp_crop_image` is loaded
if (!function_exists('wp_crop_image')) {
    include(ABSPATH . 'wp-admin/includes/image.php');
}

// Ensure `wp_read_video_metadata` is loaded
if (!function_exists('wp_read_video_metadata')) {
    include(ABSPATH . 'wp-admin/includes/media.php');
}

Right after that, we loop through the query results and update the attachment metadata using the necessary functions:

// Loop through all attachments with missing metadata
$i = 0;
foreach ($attachments as $attach_id) {
    // Check if the attachment has a file meta attached to it
    $file = get_attached_file($attach_id);

    // If no file is found, generate one and attach it
    if (empty($file)) {
        $attach_obj = get_post($attach_id);
        // Code for generating file path here...

        // Update attached file
        update_attached_file($attach_id, $filename);
        $file = get_attached_file($attach_id);
    }

    // Generate and update the metadata for the current attachment
    if ($file) {
        $attach_data = wp_generate_attachment_metadata($attach_id, $file);
        wp_update_attachment_metadata($attach_id, $attach_data);
    }

    // Track the count
    $i++;
}

// Display the count of restored attachments
die($i . ' attachment(s) metadata restored');

And that’s it! If you put everything together and open your website with ?regenerate_attach_meta=1 added to the URL, the missing metadata should be recreated for the attachments retrieved by our query. Be sure to run this as many times as needed until all the missing data is fully restored.

To sum it up, here’s the complete snippet:


/**
 * Restore/regenerate missing attachment metadata in WordPress.
 * Add this to your child theme's functions.php and open you website
 * with ?regenerate_attach_meta; ex: example.com/?regenerate_attach_meta=1
 */

add_action('wp_head', 'ak_regenerate_attach_meta');
function ak_regenerate_attach_meta()
{
    // Check whether the query parameter `regenerate_attach_meta` exists in the URL
    if (!isset($_GET['regenerate_attach_meta'])) {
        return;
    }

    // Run our code if the query parameter `regenerate_attach_meta` was found


    $attachments = get_posts(
        array(
            'post_type' => 'attachment',
            'posts_per_page' => 100,
            'fields' => 'ids',
            'meta_query' => array(
                array(
                    'key' => '_wp_attachment_metadata',
                    'compare' => 'NOT EXISTS'
                ),
            ),
        ),
    );

    if (!empty($attachments)) {

        // Ensure `wp_crop_image` is loaded
        if (!function_exists('wp_crop_image')) {
            include(ABSPATH . 'wp-admin/includes/image.php');
        }
        // Ensure `wp_read_video_metadata` is loaded
        if (!function_exists('wp_read_video_metadata')) {
            include(ABSPATH . 'wp-admin/includes/media.php');
        }

        // Loop through all the attachments with missing metadata
        $i = 0;
        foreach ($attachments as $attach_id) {
            // Check if the attachment has a file meta attached to it
            $file = get_attached_file($attach_id);
            // If no files was found, generate one and attach it
            if (empty($file)) {
                $attach_obj = get_post($attach_id);
                $filename = str_replace(site_url('/wp-content/uploads/', 'https'), '', $attach_obj->guid);
                $filename = str_replace(site_url('/wp-content/uploads/', 'http'), '', $filename);
                $filename = str_replace(site_url('/', 'https'), '', $filename);
                $filename = str_replace(site_url('/', 'http'), '', $filename);

                update_attached_file($attach_id, $filename);
                $file = get_attached_file($attach_id);
            }

            // Now generate the metadata and update it for the current attachment
            if ($file) {
                $attach_data = wp_generate_attachment_metadata($attach_id, $file);
                wp_update_attachment_metadata($attach_id, $attach_data);
            }

            // Save count
            $i++;
        }

        die($i . ' attachment(s) metadata restored');
    }

    // Show a custom message if all attachments have their metadata in the `wp_postmeta` table
    die('No missing metadata');
}

With this snippet, you won’t need to use any plugins to recover your attachment metadata in WordPress.