Find Duplicate Images by Hash
Scan the WordPress Media Library and find duplicate images by file hash (MD5). Includes stats, orphan check, and delete option with a beautiful dashboard.
by Shah Jalal · github.com/shahjalal132/detect-duplicate-images
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/shahjalal132/detect-duplicate-images/archive/refs/heads/main.zipReadme
Find Duplicate Images by Hash
A high-performance WordPress plugin to scan the Media Library and find duplicate images by file hash (MD5). Optimized for large media libraries (1M+ images) with batch processing, caching, and beautiful UI.
Features
- 🔍 Smart Scanning - MD5 hash-based duplicate detection
- ⚡ High Performance - Batch processing for millions of images
- 💾 Intelligent Caching - Results cached for 24 hours
- ⏸️ Stop & Resume - Pause scan anytime and resume later
- 📊 Beautiful Statistics Dashboard
- 🏷️ Orphan Detection - Identify unused vs attached images
- 🗑️ Safe Bulk Delete - Only delete orphaned duplicates
- 📄 Pagination - Handle thousands of duplicate sets
- 📈 Real-time Progress - Visual progress bar during scanning
- 💾 Partial Results - View duplicates found even if scan is stopped
- ✨ Modern UI - Clean, intuitive interface
Performance Optimizations
For Large Media Libraries (1M+ images)
-
Batch Processing
- Processes 100 images per batch via AJAX
- Prevents PHP timeout errors
- Real-time progress updates
-
Smart Caching
- MD5 hashes stored in postmeta (
_fdi_md5_hash) - Scan results cached for 24 hours
- Only recalculates when needed
- MD5 hashes stored in postmeta (
-
Optimized Database Queries
- Efficient batched queries
- Indexed lookups
- Minimal memory usage
-
Manual Scan Trigger
- No automatic scanning on page load
- User-initiated scans only
- Background processing
File Structure
ditect-duplicate-images/
├── ditect-duplicate-images.php # Main plugin file
├── src/
│ ├── helpers/
│ │ └── functions.php # Helper functions (caching, batch processing)
│ ├── templates/
│ │ ├── main-page.php # Main admin page with scan controls
│ │ ├── stats-dashboard.php # Statistics dashboard
│ │ ├── pagination.php # Pagination controls
│ │ └── duplicate-table.php # Duplicate images table
│ ├── css/
│ │ └── admin-styles.css # Admin styles (scan UI, progress bar)
│ └── js/
│ └── admin-scripts.js # AJAX batch processing, progress updates
└── README.md
Architecture
Main Plugin File (ditect-duplicate-images.php)
- Plugin initialization and WordPress hooks
- Asset enqueuing (CSS/JS with localized data)
- Admin page registration
- AJAX handlers for batch processing
- Controller logic for rendering and deletion
Helper Functions (src/helpers/functions.php)
Scanning Functions:
fdi_get_attachments_batch()- Get batch of attachmentsfdi_process_batch()- Process batch and calculate hashesfdi_get_attachment_hash()- Get/cache MD5 hashfdi_merge_batch_results()- Merge batch results
Caching Functions:
fdi_get_cached_duplicates()- Retrieve cached resultsfdi_set_cached_duplicates()- Store results in cachefdi_clear_cache()- Clear all cached datafdi_get_scan_progress()- Get current scan progressfdi_set_scan_progress()- Update scan progress
Utility Functions:
fdi_is_image_attached()- Check if image is usedfdi_calculate_stats()- Calculate statisticsfdi_get_pagination_data()- Calculate paginationfdi_get_last_scan_time()- Get last scan timestamp
Templates (src/templates/)
- main-page.php - Scan controls, progress bar, results
- stats-dashboard.php - Statistics boxes
- pagination.php - Pagination controls (reusable)
- duplicate-table.php - Table with delete actions
Styles (src/css/admin-styles.css)
- Scan card and button styles
- Progress bar animations
- Stats dashboard layout
- Table and image previews
- Pagination controls
JavaScript (src/js/admin-scripts.js)
AJAX Operations:
- Start scan process
- Process batches sequentially
- Update progress bar in real-time
- Clear cache
User Interactions:
- Pagination navigation
- Delete confirmations
- Button state management
Installation
- Upload the plugin folder to
/wp-content/plugins/ - Activate the plugin through the 'Plugins' menu in WordPress
- Navigate to Tools > Find Duplicate Images
Usage
First-Time Use
- Go to Tools > Find Duplicate Images
- Click "▶️ Start Scan" button
- Wait for the scan to complete (progress bar shows status)
- Review duplicate sets and delete orphaned images
For Large Libraries
- 100k images: ~1-2 minutes
- 500k images: ~5-10 minutes
- 1M+ images: ~10-20 minutes
The scan runs in the background with real-time progress updates.
After Scanning
- Results are cached for 24 hours
- View duplicates instantly on subsequent visits
- Click "🔄 Re-scan Library" to update results
- Click "Clear Cache" to force a new scan
Stop & Resume Feature
- Click "⏸️ Stop Scan" at any time during scanning
- Partial results are automatically saved
- View duplicates found so far immediately
- Click "▶️ Resume Scan" to continue from where you stopped
- Already scanned images are skipped (uses cached MD5 hashes)
- Perfect for very large libraries or if you need to free up resources
Deleting Duplicates
- Review the duplicate sets
- Check which images are "Orphans" (not attached)
- Click "Delete X Orphan(s)" button
- Confirm deletion
- Cache automatically clears and page reloads
Configuration
Batch Size
Edit FDI_BATCH_SIZE constant in main plugin file (default: 100)
define( 'FDI_BATCH_SIZE', 100 ); // Process 100 images per batch
- Lower = slower but more stable
- Higher = faster but may timeout on slow servers
Cache Duration
Edit cache expiration in ajax_process_batch() method:
fdi_set_cached_duplicates( $progress['duplicates'], 3600 * 24 ); // 24 hours
Items Per Page
Edit PER_PAGE constant in plugin class (default: 20)
const PER_PAGE = 20;
Database Storage
Transients (Temporary Cache)
fdi_duplicate_images- Cached duplicate results (24 hours)fdi_scan_progress- Current scan progress (10 minutes)
Options (Persistent)
fdi_last_scan_time- Timestamp of last completed scan
Post Meta (Per Image)
_fdi_md5_hash- Cached MD5 hash for each attachment
Performance Tips
- Run scans during off-peak hours for very large libraries
- Increase PHP memory limit if processing 1M+ images:
define('WP_MEMORY_LIMIT', '512M'); - Increase PHP max execution time in
.htaccess:php_value max_execution_time 300 - Use object caching (Redis/Memcached) for faster transient access
Troubleshooting
Scan Timeout
- Reduce
FDI_BATCH_SIZEto 50 or lower - Increase PHP
max_execution_time - Check server resources
Memory Errors
- Increase PHP
memory_limit - Reduce batch size
- Clear other WordPress transients
Slow Performance
- Enable object caching
- Optimize database tables
- Check for conflicting plugins
Security
- ✅ Permission checks (
current_user_can) - ✅ Nonce verification for all forms
- ✅ AJAX nonce verification
- ✅ Data sanitization and validation
- ✅ Double-check orphan status before deletion
- ✅ Safe deletion (only orphaned images)
Browser Compatibility
- Chrome/Edge: ✅ Full support
- Firefox: ✅ Full support
- Safari: ✅ Full support
- IE11: ⚠️ Not supported
Version History
-
1.5 - Stop & Resume functionality
- Added stop scan button
- Partial results display
- Resume from last position
- Smart hash caching (skips already scanned images)
- Improved progress tracking
- Better UX for interrupted scans
-
1.4 - Performance optimization for large libraries
- Added batch processing with AJAX
- Implemented caching system
- MD5 hash storage in postmeta
- Manual scan trigger with progress bar
- Background processing
-
1.3 - Refactored architecture with separated concerns
-
1.2 - Added pagination support
-
1.1 - Added single delete button for orphans only
-
1.0 - Initial release
Requirements
- WordPress 5.0+
- PHP 7.0+
- MySQL 5.6+
Author
Shah Jalal
License
GPL v2 or later
Support
For issues with 1M+ images or performance optimization, please ensure:
- Server meets minimum requirements
- PHP memory limit is adequate (512MB+ recommended)
- Object caching is enabled for best performance