Select one or more tags, then press “Search Plugins”

Find Plugin with any / all of the selected criteria
Search Plugin

Media Mage Wordpress Plugin - Rating, Reviews, Demo & Download

Media Mage Preview Wordpress Plugin - Rating, Reviews, Demo & Download
No ratings yet
Free
Follow for free plugins, new theme releases and theme news

Plugin Description

Media Mage scans your media library for two kinds of clutter:

  • Duplicate files – the same image uploaded more than once under different names. Matched on MD5 content hash, so a renamed copy is still caught.
  • Unused media – attachments that nothing on the site appears to reference.

A tool that deletes files is only as good as the search it runs first, because anything the search misses gets reported as unused and then deleted. Most of the work in this plugin is in that search, and in the guards that sit between the search result and the delete.

What counts as a reference

Media Mage checks all of the following before it will call a file unused:

  • Post content of any post, page or custom post type, including trashed posts
  • Post meta – ACF fields, Elementor data, page-builder payloads, plain custom fields, wherever the value contains the URL
  • Term meta, user meta and comment meta, again wherever the value contains the URL
  • Comment content
  • The options table – theme mods, customizer values, widget areas
  • Featured images (_thumbnail_id) and the attachment’s own post parent
  • Site logo and site icon
  • WooCommerce product galleries
  • Oxygen Builder base64-encoded layout data (_ct_builder_shortcodes, _ct_builder_json, ct_style_sheets, ct_components_classes)
  • Gutenberg block IDs and wp-image-N classes, so a block that carries an ID and no URL still counts

Each of those is checked against every URL the attachment can appear under: the full-size file, every generated size variant, the -scaled version and the preserved original. Inserting an image at Medium embeds the medium URL and never the full-size one, so checking only the full-size URL is how a live image ends up on an unused list.

URLs are also matched in their JSON-escaped form (/ instead of /), which is how Elementor, Divi and Gutenberg block attributes store them.

What it cannot see

Media Mage reads the database. It does not read your code. It will not find:

  • An image path hardcoded in a theme or plugin PHP file, including a child theme
  • URLs rewritten to an external CDN, so what is stored no longer matches what is served
  • An attachment referenced only by its numeric ID in a custom field. IDs are checked in featured images, WooCommerce galleries, the site logo and icon, and Gutenberg blocks and gallery shortcodes inside post content. Everywhere else the search is for the URL. The common case is an ACF image field set to return the Image ID rather than the URL – that value is a bare number and Media Mage cannot tell it from any other number.
  • Uploads shared or copied between sites on a multisite network. Each scan sees one site. If site A’s image is used on site B, site A reports it unused.
  • Anything held in a store the plugin does not know about

If your theme prints wp-content/uploads/2026/01/hero.jpg straight out of a template file, that image is reported as unused. Review the list before deleting. There is a wpmj_is_referenced filter for protecting files programmatically – see the FAQ.

One more thing worth knowing, because it works the other way: an attachment whose post_parent points at a post that still exists counts as referenced. Most images uploaded through the post editor have one, so on a long-lived site the unused list will be shorter than you expect. That is deliberate – it errs toward keeping files – but it is why a scan can come back nearly empty on a site you were certain was full of clutter.

How it avoids breaking your site

  • Deleting trashes by default. Files go to the WordPress trash. The database rows and the files on disk both survive, so a wrong call is recoverable from the Trash tab. Disk space comes back when the trash is emptied, which is a separate action.
  • Every file is re-checked at delete time. The scan is a snapshot. Scan at 09:00, put one of those images on the homepage at 11:00, click delete at 12:00, and the snapshot is wrong. Each file is checked again immediately before it is touched, and anything back in use is skipped and reported.
  • Deletes are limited to the current scan results. A delete request for an ID that is not in the results is refused, and expired results fail closed rather than waving the request through.
  • Resolving a duplicate re-points references first. Post content, post meta, options, theme mods, WooCommerce galleries and Oxygen data are rewritten to the keeper before the duplicate is removed, including every size variant and the JSON-escaped forms.
  • Serialized data is handled properly. Nested serialized values are rewritten with their length prefixes recomputed at every level. Values containing PHP objects are skipped rather than round-tripped, and any rewrite that will not read back is discarded.
  • Rewrites stay in scope. Cron, rewrite rules, transients and edit locks are excluded by name. A backup plugin’s run log that merely names a file is not evidence, and editing it is its own kind of damage.
  • A duplicate group is only a proposal, and the delete verifies it. Groups come from cached hashes, and a cache keyed on file timestamps can be wrong – restoring uploads with rsync or a backup tool changes content while leaving timestamps untouched. Both files are re-hashed immediately before a duplicate is removed, and anything no longer byte-identical is kept and reported instead. If either file cannot be read, that counts as unverified, and unverified is never treated as identical.
  • A duplicate whose references could not all be rewritten is kept, not deleted. Some values cannot be safely rewritten – a serialized PHP object, a structure with an internal back-reference. Those are left alone rather than corrupted, and because leaving them alone means a reference would survive pointing at a deleted file, the deletion is cancelled and the file and rows are named.

Other things it does

  • Trash view. Everything Media Mage trashed is listed on its own tab, with restore and a separate empty-the-trash action. It lists what this plugin trashed, not everything ever trashed on the site.
  • Ignore list. Mark a file as fine and it stops appearing in the unused list. Without this, the handful of files the plugin structurally cannot see references for sit at the top of every scan forever.
  • Where is this used? Each file can show the posts that reference it, with edit links and how the match was made. The queries behind the reference count already knew which posts matched, and that count is the whole basis for deciding which copy of a duplicate to keep, so it is worth seeing.
  • CSV export of the scan results, so there is a record of what was found before anything is deleted.
  • WP-CLI commands for scanning, listing, resolving, deleting, exporting, the ignore list and the trash, with dry runs. Resolving shares one implementation with the admin screen, so the two cannot drift apart. See the FAQ.
  • Two-phase scan with per-file progress, sized so each request finishes well inside a shared host’s PHP timeout
  • MD5 hashes cached against file modification time and size, so re-scans are fast
  • Reclaimable-bytes total, plus library total, ignored count and trashed count, so the numbers have a denominator
  • Attachments whose file is missing from disk are counted separately rather than silently hashed as identical
  • Two filters, wpmj_is_referenced and wpmj_replace_query, for sites that need the last word
  • Uninstall removes every row the plugin wrote, on single sites and across a multisite network

Free, and staying that way

No pro tier, no license key, no upsell. If it saved you an afternoon you can buy me a coffee. Never required.

Screenshots

  1. Duplicate groups, matched on file content rather than filename. The reference count is there because it is the number you weigh up when choosing which copy to keep.

    Duplicate groups, matched on file content rather than filename. The reference count is there because it is the number you weigh up when choosing which copy to keep.

  2. The unused list. Every file here was checked against twelve places first, and the notice is a standing reminder that a database scan cannot read your theme's PHP.

    The unused list. Every file here was checked against twelve places first, and the notice is a standing reminder that a database scan cannot read your theme’s PHP.

  3. Nothing is deleted without naming the files first. Cancel leaves the library untouched.

    Nothing is deleted without naming the files first. Cancel leaves the library untouched.

  4. Removals go to the trash and stay on disk. Restore puts a file back; emptying the trash is the separate step that actually reclaims the space.

    Removals go to the trash and stay on disk. Restore puts a file back; emptying the trash is the separate step that actually reclaims the space.

  5. A scan in progress. The log names every file as it goes, so a run that misses something is visible rather than silent.

    A scan in progress. The log names every file as it goes, so a run that misses something is visible rather than silent.


Reviews & Comments