{"id":342736,"date":"2026-07-21T12:27:18","date_gmt":"2026-07-21T12:27:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/hxmc-smart-media-cleaner\/"},"modified":"2026-07-21T12:27:00","modified_gmt":"2026-07-21T12:27:00","slug":"hxmc-smart-media-cleaner","status":"publish","type":"plugin","link":"https:\/\/cy.wordpress.org\/plugins\/hxmc-smart-media-cleaner\/","author":23511001,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.3.11","stable_tag":"0.3.11","tested":"7.0.2","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"HXMC \u2014 Smart Media Cleaner","header_author":"youheiokubo","header_description":"Code-first media library cleanup. Detect unused images, rename non-ASCII filenames safely, convert to WebP with built-in compression. No external services.","assets_banners_color":"fdfcfd","last_updated":"2026-07-21 12:27:00","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/okuboyouhei\/hxmc-smart-media-cleaner","header_author_uri":"https:\/\/profiles.wordpress.org\/youheiokubo\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":48,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","faq","changelog"],"tags":{"0.3.11":{"tag":"0.3.11","author":"youheiokubo","date":"2026-07-21 12:27:00"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3617124,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3617124,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3617124,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3617124,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.3.11"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[3786,17209,84,3990,15376],"plugin_category":[50],"plugin_contributors":[266517],"plugin_business_model":[],"class_list":["post-342736","plugin","type-plugin","status-publish","hentry","plugin_tags-cleanup","plugin_tags-compression","plugin_tags-media","plugin_tags-rename","plugin_tags-webp","plugin_category-media","plugin_contributors-youheiokubo","plugin_committers-youheiokubo"],"banners":{"banner":"https:\/\/ps.w.org\/hxmc-smart-media-cleaner\/assets\/banner-772x250.png?rev=3617124","banner_2x":"https:\/\/ps.w.org\/hxmc-smart-media-cleaner\/assets\/banner-1544x500.png?rev=3617124","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/hxmc-smart-media-cleaner\/assets\/icon-128x128.png?rev=3617124","icon_2x":"https:\/\/ps.w.org\/hxmc-smart-media-cleaner\/assets\/icon-256x256.png?rev=3617124","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>HXMC keeps your media library tidy with three focused tools \u2014 and an honest scope for each.<\/p>\n\n<p><strong>1. Usage scan (read-only)<\/strong>\nDetects references to each image in featured images, post content (all intermediate sizes covered), custom fields, and widgets\/options. Images with no detected reference are flagged as \"No reference found\" \u2014 deliberately <em>not<\/em> \"unused\", because the scanner cannot see theme\/CSS hardcoded URLs, serialized page-builder data, or external sites. This version never deletes anything. The list is there to inform your own decision.<\/p>\n\n<p><strong>2. Safe rename for non-ASCII filenames<\/strong>\nJapanese (or any non-ASCII) filenames cause percent-encoded URL mess and occasional server-side trouble. HXMC renames the original file and every intermediate size on disk, rewrites URLs in post content and custom fields, and registers a 302 fallback redirect for the old URL \u2014 so nothing breaks even where a reference could not be rewritten. Redirects fire only on 404, costing nothing on normal requests. GUIDs are left untouched, per WordPress convention.<\/p>\n\n<p><strong>3. WebP conversion with built-in compression<\/strong>\nGenerates <code>.webp<\/code> twins for the original and all sizes using GD or Imagick \u2014 entirely on your server, no external API. Compression is simply the WebP quality setting (default 82): one knob, not two features. Originals are kept on disk as insurance, references are rewritten to <code>.webp<\/code>, and old URLs get the same 302 fallback. Already-WebP files, SVGs, and animated GIFs are honestly refused rather than silently mangled.<\/p>\n\n<p>For the ~2% of browsers without WebP support, HXMC writes Accept-header fallback rules to <code>uploads\/.htaccess<\/code> (Apache\/LiteSpeed): requests for <code>.webp<\/code> from non-supporting browsers are transparently served the kept original, with <code>Vary: Accept<\/code> so caches stay correct. On Nginx, where plugins cannot write server config, the admin page shows an equivalent copy-paste snippet instead.<\/p>\n\n<p><strong>4. In-place compression (overwrite mode)<\/strong>\nRe-encodes JPEG (quality knob, shared with WebP \u2014 one quality concept) and losslessly recompresses PNG, overwriting the same filename so URLs never change and no database rewrite is needed. Files are overwritten <em>only when the result is smaller<\/em>; otherwise the original bytes are kept. This is lossy for JPEG and irreversible \u2014 the confirm dialog says so plainly, and no backups are made (doubling disk usage would contradict the point of a cleanup plugin). JPEG metadata (EXIF) is stripped as part of the diet when Imagick is available.<\/p>\n\n<p><strong>5. Media replace (same filename, fresh cache)<\/strong>\nUpload a new file over an existing attachment: the filename \u2014 and therefore every URL \u2014 stays identical, thumbnails are regenerated (old size files are cleaned up, stale size references in content are healed), and references get a <code>?v={timestamp}<\/code> cache-busting parameter so browsers and CDNs fetch the new bytes immediately. WebP twins and compression state are reset, with content references rewritten back and 302 entries covering stragglers. Same MIME type only \u2014 keeping a <code>.jpg<\/code> name on PNG bytes would be a lie.<\/p>\n\n<p><strong>Design principles (HX Series)<\/strong>\n* Admin-only. Zero JavaScript, zero markup, zero cookies added to your visitors' pages. The only front-end footprint is a 404-time redirect lookup.\n* No external services. Image processing is local GD\/Imagick.\n* 302 only \u2014 never 301 \u2014 so redirects are never cached into permanence.\n* Fires <code>hxmc_after_rename<\/code> and <code>hxmc_after_convert<\/code> actions for integration (e.g. HXMD \u2014 Markdown Log Manager).<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"why%20doesn%27t%20it%20delete%20unused%20images%3F\"><h3>Why doesn't it delete unused images?<\/h3><\/dt>\n<dd><p>Because \"no reference found\" is not proof of non-use. Deletion may come later behind explicit safeguards; v0.1 is read-only by design.<\/p><\/dd>\n<dt id=\"does%20it%20modify%20my%20original%20images%3F\"><h3>Does it modify my original images?<\/h3><\/dt>\n<dd><p>No. Rename moves files (content intact); WebP conversion creates new files alongside the originals.<\/p><\/dd>\n<dt id=\"is%20compression%20reversible%3F\"><h3>Is compression reversible?<\/h3><\/dt>\n<dd><p>No. JPEG recompression is lossy and originals are overwritten (only when smaller). If you need the pristine originals, keep your own backups before compressing. WebP conversion, by contrast, always keeps originals.<\/p><\/dd>\n<dt id=\"what%20happens%20in%20browsers%20without%20webp%20support%3F\"><h3>What happens in browsers without WebP support?<\/h3><\/dt>\n<dd><p>On Apache\/LiteSpeed, nothing \u2014 the fallback rules serve the kept original automatically. On Nginx, add the snippet shown on the admin page to your server config. Without it, non-supporting browsers (IE11, Safari 13 and older) would fail to load converted images.<\/p><\/dd>\n<dt id=\"what%20about%20serialized%20data%20%28page%20builders%29%3F\"><h3>What about serialized data (page builders)?<\/h3><\/dt>\n<dd><p>String replacement inside serialized values corrupts them, so HXMC skips serialized rows on purpose. The 302 fallback redirect covers those references instead.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.3.11<\/h4>\n\n<ul>\n<li>Security hardening: every file operation (compress, rename, convert, replace) now validates that paths derived from attachment metadata resolve inside the uploads directory (validate_file + realpath containment), refusing tampered _wp_attached_file or size entries.<\/li>\n<\/ul>\n\n<h4>0.3.10<\/h4>\n\n<ul>\n<li>Removed bundled translation files and load_plugin_textdomain() per WordPress.org review; translations will be provided via translate.wordpress.org.<\/li>\n<\/ul>\n\n<h4>0.3.9<\/h4>\n\n<ul>\n<li>Fix: WebP twins are now served for metadata-driven output too (featured images, theme templates, render-time srcset). Previously only URLs stored in content were rewritten, so themes calling wp_get_attachment_image() kept serving originals.<\/li>\n<\/ul>\n\n<h4>0.3.8<\/h4>\n\n<ul>\n<li>Packaging: distribution ZIP now excludes repository-only files (CLAUDE.md); ai-reference.md and llms.txt carry synced version headers.<\/li>\n<\/ul>\n\n<h4>0.3.7<\/h4>\n\n<ul>\n<li>Bundled ai-reference.md and llms.txt (AI-facing documentation, series convention).<\/li>\n<\/ul>\n\n<h4>0.3.6<\/h4>\n\n<ul>\n<li>Plugin Check: annotated the read-only use of core's big_image_size_threshold filter.<\/li>\n<\/ul>\n\n<h4>0.3.5<\/h4>\n\n<ul>\n<li>Admin screen restyled with the HXMC deep-purple identity (#4608AD) via --hxmc-* design tokens.<\/li>\n<\/ul>\n\n<h4>0.3.4<\/h4>\n\n<ul>\n<li>Replace now refuses images over the big-image threshold (default 2560px) instead of letting WordPress silently rename them to -scaled files, which would break the kept URLs.<\/li>\n<\/ul>\n\n<h4>0.3.3<\/h4>\n\n<ul>\n<li>Plugin Check: tmp_name handling restructured (sanitize + unslash + is_uploaded_file as the authoritative validation).<\/li>\n<\/ul>\n\n<h4>0.3.2<\/h4>\n\n<ul>\n<li>After a replace, WordPress-generated URLs (media edit screen, library grid\/modal, render-time srcset) also carry ?v=, so every admin view shows the new image without a hard refresh.<\/li>\n<\/ul>\n\n<h4>0.3.1<\/h4>\n\n<ul>\n<li>Fix: admin list thumbnails now carry the replace version (?v=), so a second replace with identical dimensions is visibly fresh instead of showing the browser-cached old thumbnail. Version counter can no longer collide within the same second.<\/li>\n<\/ul>\n\n<h4>0.3.0<\/h4>\n\n<ul>\n<li>Media replace: overwrite an attachment with a new file of the same type, keeping the filename. Regenerates sizes, resets WebP\/compression state, heals stale size URLs, appends ?v= cache busting. New <code>hxmc_after_replace<\/code> hook.<\/li>\n<\/ul>\n\n<h4>0.2.0<\/h4>\n\n<ul>\n<li>In-place compression: JPEG quality re-encode + PNG lossless recompress, overwrite only when smaller, EXIF stripped via Imagick. New <code>hxmc_after_compress<\/code> hook.<\/li>\n<\/ul>\n\n<h4>0.1.2<\/h4>\n\n<ul>\n<li>Each row now links to the attachment edit screen, so acting on \"No reference found\" images (including deletion, by you, with WordPress core) is one click away.<\/li>\n<\/ul>\n\n<h4>0.1.1<\/h4>\n\n<ul>\n<li>WebP fallback for non-supporting browsers: Accept-header rules in uploads\/.htaccess (Apache\/LiteSpeed) + Vary: Accept; Nginx snippet on the admin page.<\/li>\n<\/ul>\n\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>Initial release: usage scan, non-ASCII rename with URL rewrite + 302 fallback, WebP conversion (GD\/Imagick), HXMD-ready hooks.<\/li>\n<\/ul>","raw_excerpt":"Code-first media library cleanup: find unreferenced images, rename non-ASCII filenames safely, convert to WebP. No external services.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/342736","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=342736"}],"author":[{"embeddable":true,"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/youheiokubo"}],"wp:attachment":[{"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=342736"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=342736"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=342736"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=342736"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=342736"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/cy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=342736"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}