{"id":318770,"date":"2026-05-29T21:11:48","date_gmt":"2026-05-29T21:11:48","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/snopix\/"},"modified":"2026-06-04T17:04:25","modified_gmt":"2026-06-04T17:04:25","slug":"snopix","status":"publish","type":"plugin","link":"https:\/\/roh.wordpress.org\/plugins\/snopix\/","author":20938031,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.1.4","stable_tag":"0.1.4","tested":"7.0","requires":"6.0","requires_php":"8.0","requires_plugins":null,"header_name":"Snopix","header_author":"SH4LIN","header_description":"Image similarity search for WordPress media library.","assets_banners_color":"f2f5f9","last_updated":"2026-06-04 17:04:25","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/SH4LIN\/snopix","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":134,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.1.0":{"tag":"0.1.0","author":"sh4lin","date":"2026-05-29 21:11:37"},"0.1.1":{"tag":"0.1.1","author":"sh4lin","date":"2026-05-29 22:05:40"},"0.1.2":{"tag":"0.1.2","author":"sh4lin","date":"2026-06-02 07:32:33"},"0.1.3":{"tag":"0.1.3","author":"sh4lin","date":"2026-06-03 11:12:47"},"0.1.4":{"tag":"0.1.4","author":"sh4lin","date":"2026-06-04 17:04:25"}},"upgrade_notice":{"0.1.4":"<p>Changes default search visibility to logged-in users only and disables drop-on-uninstall by default. Review your Settings tab after upgrading.<\/p>","0.1.3":"<p>Hardens bulk indexing resilience, speeds up reindex progress, and fixes uninstall cleanup to remove all plugin data.<\/p>","0.1.2":"<p>Restores PHP 8.0 compatibility and hardens background indexing, duplicate scanning, and search rate limiting.<\/p>","0.1.1":"<p>Stability and compatibility fixes. Removes the non-working uninstall-confirmation prompt; your keep \/ drop-on-uninstall setting is unaffected.<\/p>","0.1.0":"<p>First public preview. Expect breaking changes between 0.x releases as thresholds and the index schema are still being tuned.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3554194,"resolution":"128x128","location":"assets","locale":"","width":384,"height":384},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3554194,"resolution":"256x256","location":"assets","locale":"","width":768,"height":768}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3554220,"resolution":"1544x500","location":"assets","locale":"","width":4632,"height":1500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3554220,"resolution":"772x250","location":"assets","locale":"","width":2316,"height":750}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.1.0","0.1.1","0.1.2","0.1.3","0.1.4"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3557596,"resolution":"1","location":"assets","locale":"","width":2398,"height":1320},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3557596,"resolution":"2","location":"assets","locale":"","width":2393,"height":1314},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3557596,"resolution":"3","location":"assets","locale":"","width":2392,"height":1312},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3557596,"resolution":"4","location":"assets","locale":"","width":2394,"height":1314}},"screenshots":{"1":"Dashboard with stat counters, indexed-image table, and the reverse-image search dropzone.","2":"Frontend search widget (card variant) showing the drop zone and result grid on a public page.","3":"Duplicate groups with per-group keep selection and bulk delete.","4":"Tools panel for reindexing, clearing the index, deleting orphan rows, and flushing caches.","5":"Settings for match thresholds, search rate limiting, batch size, and the keep \/ drop-on-uninstall choice."}},"plugin_section":[],"plugin_tags":[29823,31038,233,265013,265014],"plugin_category":[50],"plugin_contributors":[222528,223420,127129],"plugin_business_model":[],"class_list":["post-318770","plugin","type-plugin","status-publish","hentry","plugin_tags-duplicates","plugin_tags-image-search","plugin_tags-media-library","plugin_tags-reverse-image-search","plugin_tags-similarity-search","plugin_category-media","plugin_contributors-akrocks","plugin_contributors-sh4lin","plugin_contributors-vishalkakadiya","plugin_committers-sh4lin"],"banners":{"banner":"https:\/\/ps.w.org\/snopix\/assets\/banner-772x250.png?rev=3554220","banner_2x":"https:\/\/ps.w.org\/snopix\/assets\/banner-1544x500.png?rev=3554220","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/snopix\/assets\/icon-128x128.png?rev=3554194","icon_2x":"https:\/\/ps.w.org\/snopix\/assets\/icon-256x256.png?rev=3554194","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/snopix\/assets\/screenshot-1.png?rev=3557596","caption":"Dashboard with stat counters, indexed-image table, and the reverse-image search dropzone."},{"src":"https:\/\/ps.w.org\/snopix\/assets\/screenshot-2.png?rev=3557596","caption":"Frontend search widget (card variant) showing the drop zone and result grid on a public page."},{"src":"https:\/\/ps.w.org\/snopix\/assets\/screenshot-3.png?rev=3557596","caption":"Duplicate groups with per-group keep selection and bulk delete."},{"src":"https:\/\/ps.w.org\/snopix\/assets\/screenshot-4.png?rev=3557596","caption":"Tools panel for reindexing, clearing the index, deleting orphan rows, and flushing caches."}],"raw_content":"<!--section=description-->\n<p>Snopix adds reverse-image search to your WordPress site. Visitors drop an\nimage onto the search widget and the plugin returns the most visually similar\nimages already in your media library - ranked by a composite score that looks\nat overall structure, colour palette, and edge patterns all at once.<\/p>\n\n<p>The same fingerprints power a <strong>Duplicates<\/strong> tab in the admin: it clusters\nnear-identical attachments so you can keep one copy and bulk-delete the rest.<\/p>\n\n<h4>Frontend search widget<\/h4>\n\n<p>Place a search widget on any page using the <code>[snopix_search]<\/code> shortcode.<\/p>\n\n<p><strong>Default card layout<\/strong> - framed widget, good for a standalone search page:\n    [snopix_search]<\/p>\n\n<p><strong>Inline layout<\/strong> - borderless, flows naturally with your page content:\n    [snopix_search variant=\"inline\"]<\/p>\n\n<p><strong>Narrow layout<\/strong> - compact single column, fits a sidebar or tight layout:\n    [snopix_search variant=\"narrow\" max_results=\"6\"]<\/p>\n\n<p><strong>Custom title and more results:<\/strong>\n    [snopix_search title=\"Find similar products\" max_results=\"24\"]<\/p>\n\n<p><strong>Card with a custom prompt and result cap:<\/strong>\n    [snopix_search variant=\"card\" title=\"Reverse Image Search\" max_results=\"16\"]<\/p>\n\n<p>Optional attributes:<\/p>\n\n<ul>\n<li><code>variant<\/code> - <code>card<\/code> (default), <code>inline<\/code>, or <code>narrow<\/code>.<\/li>\n<li><code>title<\/code> - header label shown above the drop zone. Default: \"Search by image\". Ignored by the <code>inline<\/code> variant.<\/li>\n<li><code>max_results<\/code> - number of result images to show. Accepts 1-48. Default: 12.<\/li>\n<\/ul>\n\n<p>The block editor also exposes these options via a <strong>Snopix Search<\/strong> panel on\nthe core <strong>Shortcode<\/strong> block, so you can configure the widget without typing\nthe shortcode manually.<\/p>\n\n<p>By default the search endpoint is open to all visitors. Restrict it to\nlogged-in users only from the <strong>Settings<\/strong> tab in <strong>Media \u2192 Snopix<\/strong>.<\/p>\n\n<h4>Admin search<\/h4>\n\n<p>The dashboard includes a drag-and-drop search panel that lets admins test\nreverse-image search directly from the media library page without publishing a\nshortcode anywhere.<\/p>\n\n<h4>Duplicate detection<\/h4>\n\n<p>The <strong>Duplicates<\/strong> tab scans your indexed images against each other and groups\nnear-identical files. Each group shows the images side by side with a \"keep\"\nselector. Delete all duplicates in a group with one click, or use the global\n<strong>Delete all duplicates<\/strong> button to clean everything at once.<\/p>\n\n<h4>Background indexing<\/h4>\n\n<p>New uploads are fingerprinted automatically on save. Existing images are\nindexed in the background via WP-Cron in batches, so large libraries\n(10 000+ images) can be indexed without hitting PHP time limits.<\/p>\n\n<h4>Features<\/h4>\n\n<ul>\n<li>Frontend reverse-image search via <code>[snopix_search]<\/code> shortcode (card, inline, and narrow variants).<\/li>\n<li>Block-editor shortcode panel for configuring the widget visually.<\/li>\n<li>Admin dashboard search dropzone for quick manual testing.<\/li>\n<li>Duplicate detection with per-group keep selection and bulk delete.<\/li>\n<li>Automatic indexing of new uploads; background bulk indexing for existing images.<\/li>\n<li>REST API at <code>\/wp-json\/snopix\/v1\/<\/code> with rate limiting on the public search endpoint.<\/li>\n<li>Settings: search visibility, rate limit, match and duplicate thresholds, indexer batch size, probe downscale ceiling.<\/li>\n<li>Tools panel: reindex everything, clear the index, delete orphan rows, flush caches.<\/li>\n<li>Clean uninstall with option to keep or drop all data.<\/li>\n<\/ul>\n\n<h4>Supported image formats<\/h4>\n\n<p>JPEG, PNG, GIF, WebP, BMP.<\/p>\n\n<h4>Where data lives<\/h4>\n\n<p>Snopix creates one custom table (<code>{prefix}snopix_index<\/code>) with one row per\nindexed image. Uninstalling the plugin drops the table unless you turn off\n<strong>Drop data on uninstall<\/strong> in the Settings tab.<\/p>\n\n<h3>Accuracy notice<\/h3>\n\n<p>Snopix is functional, but search ranking, duplicate detection and the\nthreshold tuning may be less accurate than expected. You may see occasional\nfalse positives or miss visually-similar images depending on your media\nlibrary. Please report anything that looks off.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>snopix<\/code> folder to <code>\/wp-content\/plugins\/<\/code> (or install the zip via <strong>Plugins \u2192 Add New \u2192 Upload Plugin<\/strong>).<\/li>\n<li>Activate the plugin in <strong>Plugins<\/strong>.<\/li>\n<li>Go to <strong>Media \u2192 Snopix<\/strong> and click <strong>Index Remaining<\/strong> to fingerprint images already in your library. This runs in the background and may take a few minutes for large libraries.<\/li>\n<li>Once indexing is complete, drop any image onto the <strong>Search by Image<\/strong> panel on the dashboard to test.<\/li>\n<li>Add <code>[snopix_search]<\/code> to any page or post to give visitors the same search widget.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"which%20image%20formats%20are%20supported%3F\"><h3>Which image formats are supported?<\/h3><\/dt>\n<dd><p>JPEG, PNG, GIF, WebP, and BMP. Other types (SVG, HEIC, AVIF, TIFF) are\nrejected at both the upload and search endpoints.<\/p><\/dd>\n<dt id=\"what%20does%20each%20shortcode%20variant%20look%20like%3F\"><h3>What does each shortcode variant look like?<\/h3><\/dt>\n<dd><ul>\n<li><code>card<\/code> (default) - a self-contained framed widget with a drop zone, progress bar, and result grid. Best as a standalone element on a dedicated search page.<\/li>\n<li><code>inline<\/code> - no card border or header label; the drop zone and results sit flush with your page content. Good when the search feels like part of a larger UI.<\/li>\n<li><code>narrow<\/code> - compact single-column layout designed for sidebars or narrow content areas.<\/li>\n<\/ul><\/dd>\n<dt id=\"how%20do%20i%20add%20the%20search%20widget%20to%20a%20page%3F\"><h3>How do I add the search widget to a page?<\/h3><\/dt>\n<dd><p>Add <code>[snopix_search]<\/code> to the body of any post or page. In the block editor,\ninsert a Shortcode block and either paste the tag or use the <strong>Snopix Search<\/strong>\npanel in the block sidebar to configure it visually.<\/p>\n\n<p>For a sidebar, use <code>[snopix_search variant=\"narrow\" max_results=\"6\"]<\/code>. For a\nfull-width search page, use <code>[snopix_search max_results=\"24\"]<\/code>.<\/p><\/dd>\n<dt id=\"can%20i%20restrict%20search%20to%20logged-in%20users%3F\"><h3>Can I restrict search to logged-in users?<\/h3><\/dt>\n<dd><p>Yes. Go to <strong>Media \u2192 Snopix \u2192 Settings<\/strong> and set <strong>Search visibility<\/strong> to\n<strong>Logged-in users only<\/strong>. The REST endpoint and the frontend widget both\nenforce the same rule.<\/p><\/dd>\n<dt id=\"how%20big%20can%20my%20media%20library%20be%3F\"><h3>How big can my media library be?<\/h3><\/dt>\n<dd><p>The fingerprint table is compact (one row per image). The bulk indexer runs in\nchained WP-Cron batches, so it will not time out regardless of library size,\nthough indexing 10 000+ images takes several minutes.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20images%20stored%20on%20s3%20or%20a%20cdn%3F\"><h3>Does it work with images stored on S3 or a CDN?<\/h3><\/dt>\n<dd><p>The indexer reads raw bytes via PHP-GD. If your offload plugin keeps a local\ncopy until indexing is done, you are fine. If files are removed from the local\nfilesystem before the indexer runs, those images are skipped.<\/p><\/dd>\n<dt id=\"how%20accurate%20is%20the%20search%3F\"><h3>How accurate is the search?<\/h3><\/dt>\n<dd><p>The composite score combines perceptual hash, colour histogram, and edge\nhistogram (weights 0.40 \/ 0.35 \/ 0.25). Format conversions, resizes, and JPEG\nrecompression are typically recovered with &gt;0.95 similarity. Heavy blur,\nextreme downscale, or noise corruption may score near the threshold and\noccasionally not rank first. Tuning is ongoing.<\/p><\/dd>\n<dt id=\"how%20is%20duplicate%20detection%20different%20from%20search%3F\"><h3>How is duplicate detection different from search?<\/h3><\/dt>\n<dd><p>Search matches a probe image (something you upload) against the indexed\nlibrary. Duplicate detection clusters the indexed images against each other\nto find pairs or groups that are already near-identical, without needing an\nexternal probe.<\/p><\/dd>\n<dt id=\"does%20it%20work%20on%20wordpress%20multisite%3F\"><h3>Does it work on WordPress multisite?<\/h3><\/dt>\n<dd><p>Snopix is built for single-site installs. It creates one\n    {prefix}snopix_index table per site and is not network-activation aware.\nActivate it per-site rather than network-wide.<\/p><\/dd>\n<dt id=\"how%20do%20i%20uninstall%20cleanly%3F\"><h3>How do I uninstall cleanly?<\/h3><\/dt>\n<dd><p>Deactivate and delete the plugin. By default your index is kept on uninstall.\nTo remove all plugin data - index table, options, transients, scheduled cron\nevents, and per-user meta - enable <strong>Drop data on uninstall<\/strong> in the Settings\ntab before deleting the plugin.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.1.4 - 2026-06-04<\/h4>\n\n<ul>\n<li>Changed: search visibility default changed from \"Anyone\" to \"Logged-in users only\" for stricter access out of the box.<\/li>\n<li>Changed: \"Drop data on uninstall\" now defaults to disabled - your index is preserved on plugin removal unless you opt in.<\/li>\n<li>Changed: slider values in Settings are now click-to-edit - click any numeric label to type a value directly.<\/li>\n<li>Changed: indexing panel shows \"Last run completed\" once a reindex job finishes, rather than staying silent.<\/li>\n<li>Changed: admin UI terminology standardised from \"attachments\" to \"images\" throughout.<\/li>\n<\/ul>\n\n<h4>0.1.3 - 2026-06-03<\/h4>\n\n<ul>\n<li>Fixed: bulk indexer no longer aborts the entire queue when a later batch encounters only unreadable images; the stall guard now fires only on the first batch, so isolated file failures are skipped rather than stranding all remaining attachments.<\/li>\n<li>Changed: inter-batch delay reduced from 60 s to 15 s, keeping the reindex progress UI responsive during a full library scan.<\/li>\n<li>Fixed: third-party admin notices are now suppressed on the Snopix dashboard to prevent them from breaking the full-bleed layout.<\/li>\n<li>Changed: admin script now declares <code>wp-i18n<\/code> as a dependency; build artefacts reorganised to <code>assets\/<\/code> subdirectories.<\/li>\n<li>Fixed: uninstall cleanup now removes all plugin data - index table, all options, transients, scheduled cron events, and per-user meta - when the \"Remove all plugin data on uninstall\" setting is enabled.<\/li>\n<\/ul>\n\n<h4>0.1.2 - 2026-06-02<\/h4>\n\n<ul>\n<li>Fixed: removed <code>readonly<\/code> properties that broke activation on PHP 8.0 (the declared minimum version).<\/li>\n<li>Changed: per-IP search rate limiting now uses an atomic counter when a persistent object cache is present, and fails closed when the client IP cannot be resolved.<\/li>\n<li>Fixed: bulk indexing no longer stalls when a run of unsupported image types (SVG\/AVIF\/TIFF\/HEIC) is queued - they are skipped at enqueue time.<\/li>\n<li>Fixed: the daily duplicate scan no longer restarts an in-progress scan, and a failed scan tick recovers instead of appearing to run forever.<\/li>\n<li>Fixed: deleting a non-existent index row now returns 404; plus minor admin-UI memory and dead-code cleanups.<\/li>\n<li>Changed: the dashboard \"Indexed\" count is now shown in green, with status colours centralised as reusable design tokens.<\/li>\n<\/ul>\n\n<h4>0.1.1 - 2026-05-30<\/h4>\n\n<ul>\n<li>Fixed: certain extreme aspect-ratio images could trigger a fatal error during indexing; working dimensions are now clamped.<\/li>\n<li>Changed: index vector columns now use LONGTEXT instead of JSON for compatibility with older MySQL \/ MariaDB.<\/li>\n<li>Fixed: capitalised indexed-image status labels and corrected spacing on the duplicate \"Keep\" badge.<\/li>\n<li>Removed: the non-functional Plugins-screen delete-confirmation modal and its \"require confirmation\" setting. The keep \/ drop-on-uninstall setting is unchanged.<\/li>\n<\/ul>\n\n<h4>0.1.0 - 2026-05-30<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<li>Perceptual hash + colour histogram + edge histogram fingerprinting.<\/li>\n<li>Reverse-image search via admin dropzone and <code>[snopix_search]<\/code> shortcode.<\/li>\n<li>Block-editor panel for inserting and configuring the search shortcode.<\/li>\n<li>Duplicate detection with per-group keep selection.<\/li>\n<li>WP-Cron bulk indexing, rate-limited public search endpoint.<\/li>\n<li>Configurable thresholds, rate limit, and batch size, plus a keep or drop-on-uninstall choice.<\/li>\n<li>WordPress.org compatibility: JPEG, PNG, GIF, WebP, BMP.<\/li>\n<\/ul>","raw_excerpt":"Reverse image search and duplicate detection for the WordPress media library, powered by perceptual hashing.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/318770","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=318770"}],"author":[{"embeddable":true,"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/sh4lin"}],"wp:attachment":[{"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=318770"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=318770"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=318770"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=318770"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=318770"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/roh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=318770"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}