Everything below reflects the current release (v1.1.0). From install to a fully accounted-for library is about ten minutes of your attention.
You need WordPress 6.0+ on PHP 7.4+, an administrator login, and the plugin zip plus licence key from your purchase email. One honest note before the first cleanup: the scan can only see reference formats it knows (they cover content, blocks, Elementor, ACF, settings and terms — the places references really live), which is why every destructive step is trash-first, re-verified at that moment, and restorable. Nothing in this plugin permanently deletes a file.
Go to Plugins → Add New → Upload Plugin, choose the zip from your purchase email, install and activate. It adds a Media Audit item to the admin menu. Activation changes nothing — the plugin only ever writes when you press a cleanup button.
Open Media Audit → Licence and paste the key from your purchase email into the licence box — it's a long code starting VPC1., so paste the whole thing into the textarea rather than typing it. Keys are domain-based — no account, no phoning home.
Press Run first scan on the Overview tab. The scanner inventories every image, then resolves usage against content, blocks, featured images, Elementor data, ACF fields, site settings and term images. Read-only, a few seconds on most sites.
The dial gives you 0–100 — Tidy, Room to clean, Cluttered or Hoarding. Below it, each finding carries a see the images link straight to the filtered library: “178 in use without alt text” is one click from the 178 images with thumbnails and edit links. The tiles up top are filters too.
The Library tab is the whole inventory — thumbnails, dimensions, weight including thumbnails, per-image “used by” reasons, and flags. Filters: Unused, Oversized, No alt, Unattached, Duplicates. The Duplicates tab shows each byte-identical set with its suggested keeper.
From Cleanup, trash everything unused in one go — every image is re-verified as still-unused at that moment — or work per-image from the Library, or trash just the unused duplicate copies. Batches are logged; Restore newest batch undoes one click's work. WordPress purges its own bin after 30 days by default.
In Settings, set the report name, logo URL, accent colour and footer note — then Branded report and Download PDF carry your branding. Turn on the email digest, pick weekly (Mondays 8am) or monthly (the 1st), add recipients, and Send digest now proves the wiring immediately.
The behaviours and safeguards you'll actually live with.
Something the scan can point at references the image: a featured-image assignment (owner post alive), a wp-image class or image/gallery block id, a [gallery] shortcode, an upload URL in content or meta (size variants normalised), an id or URL inside Elementor data, a twin-verified ACF field, the site icon or logo, a theme setting, a widget, or a term image. The reason is stored per image and shown in the Library.
Patterns are deliberately loose — a JSON "id" that happens to match an attachment counts as a use. That direction of error means the USED pile may be slightly padded and the UNUSED pile is conservative. Since unused is the list you act on, that's the right way round.
A bare number in a custom field only counts as an image reference when ACF's invisible twin row (_fieldname → "field_…") confirms it's a real ACF field value. Serialized id arrays (gallery fields) are handled the same way. A price, a quantity or a menu order never trashes anyone's hero image.
Attachment is upload bookkeeping; usage is reality. The audit keeps them separate and the Library filters on each. The combination — unattached AND unreferenced — is the strongest deletion signal, and those images sort to the top of the Unused view.
Files are grouped by byte size, then hashed within groups — cheap and exact. Each set's keeper is a used copy when one exists, the oldest otherwise. “Trash unused duplicate copies” never touches keepers or anything in use. Repointing references from copies to keepers is deliberately NOT automated — that's content surgery, and you're better placed to do it per case.
WordPress scales uploads over 2560px and serves the scaled copy — but keeps the camera original on disk. The audit reads the original's real dimensions and size (that's what storage and backups pay for) and judges against your thresholds from Settings.
Cleanup calls WordPress's own trash — post status changes, files stay on disk, the media grid hides them. Restore returns attachments to the library exactly as they were. WordPress itself purges trashed items after EMPTY_TRASH_DAYS (default 30); until then everything is one click from back.
Weekly (Mondays 08:00 site time) or monthly (the 1st, 08:00): the score, per-category changes since the last digest, the top findings and a link in. The schedule re-arms itself whenever an admin is around, and the Send-now button covers hosts where WP-Cron never fires.
The CSV is the full inventory — id, file, dimensions, bytes with thumbnails, used with reasons, alt, attached, oversized, duplicate group — one row per image, spreadsheet-ready. The report is the same evidence shaped for a human: score, findings, largest unreferenced images, under your branding, printable or as a true PDF.
A plugin storing references in a format the scan has never met would not be counted — that's physics, not modesty, and it's why cleanup is trash-first, re-verified and restorable rather than a delete button. If you run something exotic, trash in small batches and click through the site before emptying the bin.
One textarea, one Save button. Paste the full VPC1.… key from your purchase email. The key covers the number of sites on your plan; move a site by deactivating on one and activating on another. Unlicensed, the whole diagnosis keeps working — cleanup, export, report and digest pause with a notice, never a breakage.