Web Development & WordPress

Remove duplicate images with same name and url but different ids from wordpress media library

Add the code below to your theme’s functions.php or use Code Snippet Plugins. Then Go to Tools > Duplicate Media in your WordPress admin, Click “Find Duplicates” to see what would be affected. Backup your Site and Media first, then click “Remove Duplicates”

<?php

// Hide duplicate media files - keep only the first one (lowest ID)
function hide_duplicate_media_files($query) {
    global $pagenow, $wpdb;
    
    // Only apply on media library page
    if (!is_admin() || $pagenow !== 'upload.php') {
        return;
    }
    
    // Only for attachment queries
    if (!$query->is_main_query() || $query->get('post_type') !== 'attachment') {
        return;
    }
    
    // Get duplicate IDs to hide (keep the one with lowest ID for each filename)
    $duplicates_to_hide = $wpdb->get_col("
        SELECT p1.ID
        FROM {$wpdb->posts} p1
        INNER JOIN (
            SELECT post_title, MIN(ID) as keep_id
            FROM {$wpdb->posts} 
            WHERE post_type = 'attachment'
            GROUP BY post_title
            HAVING COUNT(*) > 1
        ) p2 ON p1.post_title = p2.post_title
        WHERE p1.post_type = 'attachment' 
        AND p1.ID != p2.keep_id
    ");
    
    if (!empty($duplicates_to_hide)) {
        $existing_excluded = $query->get('post__not_in') ?: array();
        $query->set('post__not_in', array_merge($existing_excluded, $duplicates_to_hide));
    }
}
add_action('parse_query', 'hide_duplicate_media_files');

// Also handle AJAX requests for media modal/grid view
function hide_duplicates_in_ajax($query_args) {
    global $wpdb;
    
    if (is_admin() && defined('DOING_AJAX') && DOING_AJAX) {
        // Get duplicate IDs to hide
        $duplicates_to_hide = $wpdb->get_col("
            SELECT p1.ID
            FROM {$wpdb->posts} p1
            INNER JOIN (
                SELECT post_title, MIN(ID) as keep_id
                FROM {$wpdb->posts} 
                WHERE post_type = 'attachment'
                GROUP BY post_title
                HAVING COUNT(*) > 1
            ) p2 ON p1.post_title = p2.post_title
            WHERE p1.post_type = 'attachment' 
            AND p1.ID != p2.keep_id
        ");
        
        if (!empty($duplicates_to_hide)) {
            $existing_excluded = isset($query_args['post__not_in']) ? $query_args['post__not_in'] : array();
            $query_args['post__not_in'] = array_merge($existing_excluded, $duplicates_to_hide);
        }
    }
    
    return $query_args;
}
add_filter('ajax_query_attachments_args', 'hide_duplicates_in_ajax');

// Quick test - add this temporarily to see what duplicates exist
function show_duplicate_info() {
    global $wpdb, $pagenow;
    
    if (is_admin() && $pagenow === 'upload.php') {
        $duplicates = $wpdb->get_results("
            SELECT post_title, GROUP_CONCAT(ID ORDER BY ID) as all_ids, COUNT(*) as count
            FROM {$wpdb->posts} 
            WHERE post_type = 'attachment'
            GROUP BY post_title
            HAVING COUNT(*) > 1
            ORDER BY count DESC
            LIMIT 10
        ");
        
        if (!empty($duplicates)) {
            echo '<div class="notice notice-info"><p><strong>Duplicate files found:</strong><br>';
            foreach ($duplicates as $dup) {
                echo 'File: ' . esc_html($dup->post_title) . ' - IDs: ' . $dup->all_ids . ' (' . $dup->count . ' copies)<br>';
            }
            echo '</p></div>';
        }
    }
}
add_action('admin_notices', 'show_duplicate_info');

 
 ?>

Thanks to claude ai for this

Leave a comment