Delayed Image Import changelog
Stop image servers throttling your imports - download them later
Latest version2.34.2
Released14 October 2026
Last 90 days17 releases
Last 90 days33 changes
Tested up toWordPress 7.1
Product page
What Delayed Image Import does, what it costs and who it is for.
Knowledge base
Setup guides, how-tos and troubleshooting for Delayed Image Import.
Discord support
Ask a question in the Delayed Image Import channel and get an answer from Dave.
Downloads & licence
Grab the latest build or check your licence and renewal status.
2.34.214 October 2026
- Fixed the PHP notice "Translation loading for the delayed-image-import domain was triggered too early" in debug.log on WooCommerce stores.
2.34.114 October 2026
- Maintenance release: the plugin package build now checks for the bundled MCP Adapter files the shared settings module loads, with no change to how the plugin works.
2.34.01 October 2026
- Checking whether an image is already in the Media Library is now a single indexed lookup, using a new
dii_source_urlstable built from existing images in the background after the update. - New "Find duplicate downloads" button on the Loaded Images tab, listing source URLs stored on more than one attachment, most copies first.
- New
wp dii index-sourcescommand to build the index immediately (--rebuildto re-derive it), andwp dii duplicatesto list duplicates from the command line.
2.33.030 September 2026
- Faster image downloads on large stores: checking whether an image is already in the Media Library no longer reads the whole post meta table.
2.32.025 September 2026
- Fixed the Delayed Image Import page, and Media > Delayed Images, timing out with a 502 gateway error on stores with many loaded images.
- The Status, Delayed, Loaded and Failed tabs now load after the page opens, so one busy tab no longer holds up the others.
- Faster queries behind the status counts, the image lists and the background download queue.
- The Loaded Images tab lists the newest downloads first; the Dupes column has been removed.
- Fixed the Failed Images tab showing the wrong item count.
2.31.123 September 2026
- Images that are already in the Media Library are recognised and reused instead of downloaded again: by the source URL Super Speedy Imports recorded, or by comparing the downloaded file's contents with the product's own images and any image with the same filename.
- The source URL of every image the plugin handles is recorded as
sspdii_original_image_urlon the attachment; the previousoriginal_image_urlkey is not read. - A deferred import from Super Speedy Imports now makes the product gallery equal to the feed once its images are downloaded, so a gallery image that leaves the feed leaves the gallery; the attachment stays in the Media Library.
- Optional setting "Recognise images via Super Speedy Imports' lookup table", off by default: when on, an image Super Speedy Imports recorded is reused without downloading it; when off, every image the plugin has not stamped itself is downloaded once and matched by its contents.
2.3016 September 2026
- Fixed comma-separated image fields from Super Speedy Imports being treated as one long URL when delayed downloads were enabled.
- Fixed comma-separated gallery images remaining queued after the featured image downloaded, including blank image positions and captions containing commas.
- Tested up to WordPress 7.1.
2.2914 September 2026
- Fixed Delay image downloads being ignored on Super Speedy Imports 2.80.0 and later, which downloaded every image during the import instead of queueing it.
- Fixed a fatal error when a featured image was retried while its cooldown was still running.
- Fixed two products sharing the same image URL each downloading their own copy when they hydrated at the same time. They now share one attachment.
2.28.1322 August 2026
- Updated super-speedy-settings to the latest version
2.28.1221 August 2026
- Hardened the shared settings module and removed its Jetpack Autoloader dependency
2.28.1018 August 2026
- Updated
super-speedy-settingsto 1.8.0, restoring the official WordPress MCP Adapter
2.28.914 August 2026
- Replaced the bundled MCP Adapter and AI Engine integration with one shared Abilities bridge and a dedicated least-privilege agent user
- Fixed the documented MCP abilities not loading on REST requests when Delayed Image Import was the only active Super Speedy plugin
2.28.813 August 2026
- Fixed a fatal class redeclaration when multiple active Super Speedy plugins bundled the same Jetpack Autoloader; shared MCP packages now load once from the newest settings module
2.28.713 August 2026
- Added an MCP setup page and bundled the official WordPress MCP Adapter, while deferring to the official standalone plugin when installed
2.28.612 August 2026
- Fixed a gallery image retried from the Failed Images tab or
wp dii retrynow keeps its own alt text, title, caption and description, and lands back in its original position in the gallery - The plugin version is now read from a single place, so the settings screen always reports the installed version and stops re-listing changes you already have
2.28.511 August 2026
- Updated super-speedy-settings to the latest version
2.28.46 August 2026
- Updated the shared Super Speedy settings module: the Super Speedy wordmark on the settings header is now a crisp high-resolution image with a dark-text variant
- Tested up to WordPress 7.0, with the full test suite passing
2.28.33 August 2026
- Updated the shared Super Speedy settings module: AI agents can check your licence and install other Super Speedy plugins over MCP, now also exposed on AI Engine's MCP endpoint when AI Engine is active
- New Check frequency setting - cap licence, update and changelog checks to once a day or once a week, and see when the licence was last checked
- Licence and changelog checks fail fast when superspeedyplugins.com cannot be reached, instead of hanging the settings page
- The newest installed copy of the shared Super Speedy settings code now runs site-wide, whichever plugin loads first - previously the alphabetically-first plugin's often-older copy won
- AI agents can read and change every Delayed Image Import setting over MCP and check the hydration backlog (pending, failed, throughput, ETA) - also exposed on AI Engine's MCP endpoint
2.28.227 July 2026
- Fixed the PhotoSwipe lightbox could open on a black screen for products whose images have not been downloaded yet. Hot-linked images were rendered with no
data-large_image_widthordata-large_image_height, and WooCommerce's lightbox sizes the enlarged image from those rather than measuring it. Most likely to bite on gallery slides that had not finished loading when the lightbox was opened, since lazy-loaded slides do not hold up the page load event - Fixed the hover magnifier did not activate on hot-linked images, because WooCommerce only binds it when the image is wider than the gallery container
- Dimensions are now published up front. They are taken from the supplier URL wherever it encodes them (
-800x600.jpg,?width=1500,?w=640&h=480, Cloudinaryw_1200,h_800and similar), otherwise a placeholder in your store's own product aspect ratio is used. Neither costs a remote request, so nothing is downloaded any earlier than before. The browser still corrects them to the exact size once each image loads - Fixed an image that failed to load and fell back to the placeholder SVG could publish the SVG's dimensions as though they were the product photo's
- Added filters
dii_image_dimensions,dii_placeholder_image_sizeanddii_configured_image_ratiofor overriding the published dimensions
2.28.124 July 2026
- Updated super-speedy-settings (shared across all Super Speedy plugins): added a settings-version report to the licence-check and download requests for better licence and update-download debugging
2.28.021 July 2026
- Fixed the product detail page showed the "Awaiting product image" placeholder instead of the hot-linked image. WooCommerce's single-product gallery template bypasses the thumbnail filters when there is no local image, on block themes (Twenty Twenty-Five) and classic themes alike. The featured slot, the gallery wrapper class and the placeholder are now all filtered.
- Fixed gallery images did not render on the product page while the featured image was still delayed - WooCommerce skips its thumbnails loop without a real featured image id. Fully-delayed products now render their whole gallery, and a product with gallery images but no featured image shows the first gallery image in the main slot instead of the placeholder.
- New the pre-hydration gallery now behaves like a real one - flexslider thumbnail strip, correct viewport height once the remote images load, and the PhotoSwipe lightbox works with hot-linked images.
- Fixed themes that build their own product galleries or placeholder markup (Blocksy via
wc_placeholder_img, OceanWP viawc_placeholder_img_src) now show hot-linked images too. Verified on Twenty Twenty-Five/Four/Three/One, Storefront, Astra, OceanWP, Kadence, Blocksy, GeneratePress, Neve and Hello Elementor. - New de-duplication. Imports that map the same column to both featured image and gallery no longer produce a fake "gallery" of one identical image - the duplicate is dropped from the hot-linked display AND at hydration time, so the downloaded product ends up with a featured image and no redundant gallery. Comparison is scheme-insensitive (http/https match).
- Fixed image URLs without a file extension (e.g.
picsum.photos/seed/x/800/600, CDN transform endpoints) now hydrate. The file is downloaded first and its type read from the content, with a meaningful filename built from the URL path. A URL that returns something other than an image is rejected with a cleardii_not_imageerror into the failed-images list.
2.27.220 July 2026
- Updated super-speedy-settings to the latest version
2.27.118 July 2026
- Updated super-speedy-settings to the latest version
2.27.018 July 2026
- Launching as a paid product (unlimited-site yearly/lifetime), free automatically to Super Speedy Imports customers via a licence-term-matched entitlement. No functional changes to the plugin itself in this release.
2.26.118 July 2026
- Added the shared super-speedy-settings module (its submodule was not populated), so the Super Speedy settings and licence page now appears, with the latest icons.
2.2617 July 2026
- Deferring is now ON BY DEFAULT for Super Speedy Imports: while this plugin is active, any SSI import with featured or gallery images writes the URLs to the
delayed_image_url/delayed_gallery_image_urlspost meta instead of downloading inline — even for imports saved before this plugin was activated. Un-tick the "Defer image downloads" checkbox (now in SSI's Additional Options panel) on an import to restore inline downloads for that import; the checkbox stores an explicit off. - SSI's "Only refresh images" import mode is never deferred — that mode's narrowed pipeline runs no postmeta stages, so deferring would have turned the run into a no-op. It downloads inline, which is what "refresh images now" means anyway.
- The background hydration cron is now ON BY DEFAULT (every minute, batch of 100, strict per-domain round-robin) so delayed images load slowly by themselves without anyone visiting the Settings page. Un-tick "Enable cron" to rely on visitor traffic and WP-CLI only.
- The WP-CLI bulk commands (
wp dii load-featured / load-galleries / load-all / retry) now run through the same per-domain round-robin scheduler as the cron, so a CLI drain no longer fires a single supplier's images in series.--dry-runnow prints the image count per supplier host so you can see what the round-robin will do. The--batchflag is gone (it only grouped progress ticks);--limitstill caps the number of posts drawn. - The per-host circuit breaker now has memory. Previously a dead supplier was backed off within one cron tick and then re-attempted 5 times every subsequent tick, forever. Now tripping the breaker records a persistent backoff with an escalating duration — 2 minutes on the first trip, doubling per consecutive trip, capped at 1 hour (filter
dii_host_backoff_max) — and the cron skips backed-off hosts entirely. Any successful download from the host clears it. CLI runs ignore the backoff (you explicitly asked) but still record trips. - Galleries are now hydrated one image at a time instead of as a single unit. The round-robin interleaves a large gallery's images across supplier hosts instead of bursting them at one server, interrupted runs resume exactly where they stopped, positional alt/title/caption/description meta survives until each image individually succeeds, and each failed URL moves to the timed-out list on its own with its own reason. Partially-downloaded galleries now render complete on the front end — local copies first, hot-linked remainder after.
- The hot-link blocklist now also covers featured images and preconnect hints (previously only gallery placeholders): blocked hosts render the SVG placeholder immediately instead of firing a doomed browser request on every product-card view. The "show broken image" diagnostic mode overrides the blocklist so failing URLs stay inspectable in DevTools.
- Fixed the WP-CLI commands were registered with underscores (
wp dii load_all), so the hyphenated forms in all the documentation (wp dii load-featured / load-galleries / load-all / timed-out) never actually worked. They do now. - Changed the two-phase retry (short-timeout attempts, then a separate long-timeout pass, then "permanently failed") is replaced by an escalating-timeout retry ladder. Every image climbs the same ladder: the timeout starts at your download timeout (default 3 s) and doubles on each failure — 3, 6, 12, 24 — up to a configurable ceiling (new "Maximum download timeout" setting, default 30 s, effectively no-timeout for an image). Three failures at the ceiling mark the image FAILED and auto-retry stops. Gallery URLs finally get the same multi-attempt treatment as featured images (previously one real attempt). Retry spacing escalates too: each failure waits attempt-count times the cooldown before the next try, and the cron respects that spacing. Existing timed-out images are migrated automatically on upgrade: they rejoin the queue at the ceiling rung, and previously "permanently failed" images become properly failed with their recorded reasons intact. The Phase 2 timeout and max-attempts settings are gone (subsumed by the ladder).
- New the Timed-Out Images tab is now Failed Images, and it's actionable. Each row shows a live thumbnail of the remote URL (a thumb that renders next to a server-side failure reason means the supplier blocks servers but not browsers — the whole diagnosis at a glance), the post linked to its edit screen, the URL clickable into a new tab, the failure reason, attempt count and when it failed, plus Retry now and Dismiss actions per row and a Retry All Failed button. The Delayed Images tab gets post edit links, clickable URLs and a per-post "Hydrate now" action.
- New the Status tab shows real progress. Image-level counts (a post with 6 gallery URLs now counts as 6 — previously counts were per post, under-reporting the backlog), a progress bar with percentage, downloads-per-hour throughput and an ETA at the current rate, and a cron heartbeat line that goes red with advice when WP-Cron has stalled with a backlog. The counts auto-refresh every 10 seconds while images are pending, so you can watch the backlog drain.
- New one-click controls. "Process a batch now" runs a batch synchronously from the Status tab or the new dashboard widget — it works even where WP-Cron is broken, which is exactly when you need a button. "Pause hydration / Resume hydration" is a prominent toggle with the paused state shown everywhere (Status, dashboard widget, admin-bar badge). A per-supplier-host table shows pending / failed / last-hour stats per host with its backoff state, plus one-click "add to hot-link blocklist" and "retry this host's failures".
- New ambient visibility. A dashboard widget (pending / failed / loaded, Process-now button), an admin-bar badge with the pending count while a backlog exists (can be disabled in Settings), two Site Health checks (stalled cron with a backlog; failed images awaiting review), a success notice when the backlog fully drains, and a warning notice when recent downloads are failing heavily, naming the worst host.
- New WP-CLI commands
wp dii status(one-line health summary,--watchto poll),wp dii failed(the failed list with reasons/attempts,--host=filter) andwp dii retry-failed(re-queue failed URLs with fresh ladders,--host=to target one supplier).wp dii pendingnow reports image-level counts.wp dii timed-outandwp dii retryremain as deprecated aliases;--phase-twois gone. - Fixed un-ticking Diagnostic logging in Settings never saved (the unchecked box wasn't coerced to off).
2.25.220 May 2026
- Updated the Super Speedy Settings page: added a "Recheck Licenses" button above the licence table so customers have a clear way to refresh licence status after a renewal or upgrade without scrolling down to the licence-key field. The button is disabled until a licence key is entered/saved and re-enables as you type.
- The Recheck flow now also force-bypasses the auth-server's own 1-hour licence cache (via a
force=1flag on thewpiapi/check_product_keycall), so renewals/upgrades that completed less than an hour before a recheck no longer show as expired/exceeded. Normal admin page loads continue to use both caches as before — only an explicit Recheck click bypasses them. (Lives in thesuper-speedy-settingssubmodule, so the change propagates to every Super Speedy plugin.)
2.25.1
- Loaded Images tab redesigned. New columns: Attachment (linked to the attachment edit screen), Image (60×60 thumbnail preview, lazy-loaded), Owner post (linked to the parent post / product edit screen), Local URL (the served URL — shows the filename, full URL on hover, opens in new tab), Remote URL (the originally-imported supplier URL — kept for dedup investigation, truncated to fit with hover for full), and Dupes (count of how many attachments share the same
original_image_url). Default sort: Dupes desc, then attachment ID asc — surfaces any duplicates at the top of the list.
2.25
- Removed the abandoned sync-strategy / AJAX / REST / mu-plugin scaffolding. The plugin now does exactly one thing on the front end: render the supplier's URL for un-hydrated images and let cron / CLI handle the actual downloads. Files deleted:
mu-plugin/dii-mu-loader.php,includes/class-rest-controller.php,assets/js/dii-frontend.js,assets/css/dii-frontend.css. Settings deleted:frontend_strategy,keep_medium_size,rest_consumer_behaviour. Renderer methods deleted:tag_thumbs_pending,ajax_placeholder_html,external_url_html,should_render_fallback,enqueue_frontend, plus the strategy-branch inrender_gallery_placeholder. Image-processor sideload no longer takes a$generate_thumbsparameter and always generates full sub-sizes inline (admin / CLI / cron contexts only — front-end never sideloads). - Two-phase retry for timed-out downloads.
- Phase 1 (normal short timeout) —
process_featurednow tracks attempts via a newdii_attempt_count_featuredpost-meta. After N failures (default 3, configurable via the Max featured-image attempts setting or thedii_max_attempts_featuredfilter),delayed_image_urlis moved totimed_out_delayed_image_urlso the Phase 1 queue stops thrashing on a stubborn URL. - Phase 2 (long timeout) —
DII_Cron::run_tickfalls through to Phase 2 once Phase 1 has nothing left to do. Phase 2 picks up timed-out posts that aren't already permanently failed, applies a long HTTP timeout (default 60 s, configurable via Phase 2 timeout setting ordii_phase_two_timeoutfilter), and retries. Anything that fails Phase 2 too is markeddii_permanently_failed_featured/dii_permanently_failed_galleryand stops being retried automatically. wp dii retry --phase-tworuns the long-timeout retry on demand and clears the permanently-failed marks so operator-initiated retries always get a fresh chance.
2.24.5
- Fixed clicking pagination on the Loaded Images / Delayed Images / Timed-Out Images tabs kicked the user back to the Status tab. WP_List_Table emits pagination anchors as
?paged=Nwith no URL fragment; on page load our tab activator readswindow.location.hash, finds it empty, and falls back to the default tab. Pagination links inside.dii-tab-panenow have#<tab-slug>appended to their href so the active tab survives navigation. The page-jump input (the small box that lets you type a page number and press Enter) is now also intercepted so it builds the right URL with the hash instead of submitting the wrapping settings form.
2.24.4
- Fixed every call to
sideload()was creating a fresh attachment row + disk file even when the same remote URL had already been downloaded for a different post. A 999-product import with shared images across products produced ~123k attachments instead of ~11k (the worst-offender URL was downloaded 120 separate times).sideload()now pre-checksoriginal_image_urlpost-meta for an existing attachment with the same URL and reuses it. Theoriginal_image_urlmeta-write moved from the callers intosideload()itself so the marker is in place before any subsequent dedup lookup. Concurrent CLI runs on the same URL can still race-create duplicates (no per-URL lock); sequential CLI / cron is safe.
2.24.3
- Fixed the WC Blocks placeholder swap added in 2.24.2 wasn't finding the post ID. The
render_block_{name}filter passes the WP_Block instance (which carries block context) as the third argument; the second argument is just the parsed-block array which has no context. Updated the callback signature to accept all three args and read$block_instance->context['postId']. Falls back toget_the_ID()and the global$postfor older WP / unusual call chains.
2.24.2
- Fixed archive pages built with the WC Blocks ProductCollection block were showing WooCommerce's default
placeholder.webpinstead of the remote supplier URL for un-hydrated products. The block's ProductImage renderer (src/Blocks/BlockTypes/ProductImage.php) callswc_placeholder_img()directly when_thumbnail_idis unset, bypassing thewoocommerce_product_get_imagefilter that handles the classic shop loop. Added arender_block_woocommerce/product-imagefilter that intercepts the rendered block, detects WC's placeholder marker, and swaps the<img>tag with the remote-URL markup whendelayed_image_urlexists. Classic-loop themes were already working via the existing filter.
2.24.1
- New Diagnostics setting On-error fallback for remote images. Default is to swap a failing remote image to the local SVG via
onerror(existing behaviour). The new "show broken" option leaves the failed URL visible so you can inspect which suppliers are blocking hot-linking and add them to the blocklist. - Added render-time diagnostic logging to
DII_Frontend_Renderer— whendiagnostic_loggingis on, every render decision (pass-through, emit-remote, emit-SVG) is logged with the post ID, branch reason, and (for remote emits) the URL. Helps diagnose "I see SVG placeholders" symptoms by showing whether they come from our SVG path, WC's default placeholder, or somewhere else.
2.24
- Remote-first rendering for un-hydrated images. New setting
frontend_strategy(Settings → Front-end rendering) with two values:remote(default) andsync. Underremote, the renderer emits the supplier's URL directly in<img src>for any un-hydrated featured or gallery image, with anonerrorSVG fallback for hot-link-blocking suppliers. Visitor sees real images instantly; no synchronous downloads on the page-render path; no AJAX, no REST, no JavaScript. - Hit-counter prioritisation. A
dii_image_hitspost-meta key increments once per request per un-hydrated post whenever the renderer emits a remote URL for it. Cron (DII_Cron::build_tuples), WP-CLI (DII_CLI::collect_post_ids), and the Delayed Images admin list table all order by hit count desc (then post_id asc) — most-viewed un-hydrated posts get downloaded first. Counter is cleared on successful hydration. - Delayed Images list table gets a new "Hits" column for the
delayedview, default-sorted by hits desc to mirror the cron / CLI processing order. the_posthandler bails for front-end contexts (frontend_singular,frontend_archive) whenfrontend_strategy = remote— no sync downloads on the visitor's request. Admin single-edit, CLI, and cron paths are unaffected and continue to do the actual downloading.- The sync strategy remains available for users who explicitly want the 2.23.x behaviour (e.g. tiny catalogs with ultra-fast suppliers).
- The Phase 3 placeholder + REST hydration path for gallery images on singular pages is now only used under
frontend_strategy = sync. Kept as legacy for sync-strategy users.
2.23.5
- Improved singular-vs-archive classification in
DII_Context::get()now usesget_queried_object_id()to identify "is this post the one the visitor is actually viewing?". Previously the classification was driven by the firingWP_Query's ownis_singular()flag with a fallback to the global one — both of which fail in legitimate edge cases (per-product single-post queries used to render archive cards on the one hand, related-products / sidebar posts on a singular page on the other). The queried-object-id comparison is the bulletproof signal: on a singular page, only the main product matches; on an archive, nothing matches. Diagnostic log now includesqueried_idso you can see this at a glance.
2.23.4
- Fixed featured images not loading on archive pages whose theme renders the shop loop via a custom WP_Query rather than the standard main-query
while ( have_posts() ) the_post()pattern. Common with WooCommerce Blocks, Gutenberg-block-based shop layouts, multi-grid landing pages, and many modern themes. DII previously treated every non-main query as a sidebar widget and bailed without processing — meaning entire archives could render with placeholders. - Removed the main-query / secondary-query distinction entirely. If a post is rendered on a page (regardless of which loop iterates it), its image needs to appear — there's no scenario where the right answer is "don't load it because it's in a secondary query." The per-request cache (
DII_Request_State::is_processed) ensures a post appearing in multiple loops is still processed only once. - For finer-grained control, the existing
dii_should_processfilter is still invoked per-post inon_the_post()— return false there to skip processing for specific posts based on any criteria you choose.
2.23.3
- Fixed "The link you followed has expired." error when saving Settings while any of the Delayed / Loaded / Timed-Out list tabs had items. WP_List_Table emits its own
_wpnoncefield (for bulk actions) which was overwriting our settings nonce in$_POST. Switched to a unique field name (dii_save_settings_nonce) so the two coexist. Latent since 2.20.
2.23.2
- Diagnostic logging toggle in the Settings tab. When on,
DII-prefixed lines are written to PHP's error log on everythe_post/process_featured/process_gallerycall, recording context resolution, bail reasons, cooldown / lock state, and sideload outcome. Off by default — turn on only while debugging. Helps diagnose why archive image hydration isn't running on a specific install. - Clear cooldowns and Clear locks buttons in the Settings tab. One click deletes all
dii_skip_*/dii_lock_*/dii_thumbgen_*transients respectively, so a stuck-after-crash transient state can be reset without manual SQL. - No behaviour change — diagnostic surface only.
2.23.1
- Fixed featured images not loading on shop / category archives when sidebar widgets or related-products loops fired the_post for delayed products before the main archive loop reached them.
DII_Request_State::mark_processedwas being set before the context bail-out check, so the sidebar's call locked the post out of its own real processing pass. Mark now happens only after we've committed to processing.
2.23
- Image meta parity. Attachment alt text, title, caption, and description can now be deferred alongside the image URL and applied to the attachment when it's downloaded — for both featured images and gallery images. Mirrors SSI's native behaviour so a deferred import produces attachments identical to an immediate one.
- New post-meta keys (write these from any importer to defer image meta):
- Featured:
delayed_image_alt,delayed_image_title,delayed_image_caption,delayed_image_description— each a plain string. - Gallery:
delayed_gallery_image_alts,delayed_gallery_image_titles,delayed_gallery_image_captions,delayed_gallery_image_descriptions— each a pipe-separated string positionally aligned withdelayed_gallery_image_urls. - Super Speedy Imports integration extended (requires SSI 2.35+). When the "Defer image downloads" toggle is on, DII now also redirects the four featured-image meta sub-fields and the four gallery-image meta sub-fields from
config['media']into the correspondingdelayed_*post-meta keys. SSI's existing post-meta stages do the writes; DII applies the meta to the attachment at hydration time. No new SSI stage, no duplicated code paths. - New filter hook
dii_attachment_meta— receives the meta array (alt,title,caption,description), the attachment ID, and the context (featured/gallery). Use this to override or compute meta values from elsewhere before they're written. - Empty values are skipped, mirroring SSI: unmapped meta columns never blank existing attachment metadata.
- Known limitation: when a gallery hydration partially fails, the meta strings are cleared along with the URL list. Retrying via
wp dii retryre-downloads the failed URLs but won't re-apply meta. Full position-aware retry is a future enhancement.
2.22
- Super Speedy Imports integration. When SSI 2.34+ is active, a "Defer image downloads" checkbox appears in the Media section of the SSI import config UI. With it enabled, SSI's existing pipeline writes image URLs into DII's
delayed_image_url/delayed_gallery_image_urlspost-meta keys instead of downloading the images at import time — imports finish in a fraction of the time, images appear on first visit (or are pre-warmed viawp dii load-all). - The integration uses two SSI hooks: the PHP action
ssi_apply_additional_options(DII rewritesconfig['media']→config['post_meta']to redirect through SSI's existing post-meta stages) and the JS eventssi:media_section_rendered(DII appends the checkbox). No new SSI stage, no duplicated code paths — SSI'sinsert-postmeta.phpandupdate-postmeta.phpdo the writes via bulk SQL exactly as they do for any other post-meta mapping. - The integration is auto-detected: if SSI isn't installed or active, the hooks never fire and DII costs nothing.
- New file
includes/class-ssi-integration.php. New asset:assets/js/dii-ssi-admin.js.
2.21
- Galleries on singular pages now hydrate via REST/AJAX after page paint. Previously a singular product visit would synchronously download every gallery image inline (with no thumbnails); now the page renders immediately with placeholder
<img>tags, JavaScript fires onePOST /wp-json/dii/v1/hydraterequest per product, and each placeholder swaps to the real attachment when the response arrives. No polling — the server holds the connection while the download completes. - Asynchronous thumbnail generation. Featured-image sync (Phase 1) and gallery hydration (Phase 3) both skip
wp_generate_attachment_metadatato keep latency low. Attachments are taggeddii_thumbs_pending=1, JS finds those<img>tags after page paint and batches them into aPOST /wp-json/dii/v1/generate-thumbnailscall (max 25 IDs per request). Sub-sizes get filled in andsrcset/sizesswap into the live DOM. - Gallery-on-archive corner case for themes that render gallery thumbnails on archive cards. The supplier's external URL is rendered as
<img src>(withonerrorfallback to the local SVG), avoiding a sync-download N-image storm. The hot-link blocklist correctly applies only to this path: hosts on the list render SVG immediately and rely on cron / CLI for the local copy. - Precise
<link rel="preconnect">hints in<head>for any supplier hostname referenced by delayed meta on posts in the main query. Improves DNS/TLS warm-up for the AJAX hydration call and the gallery-on-archive external<img>requests. Emits one preconnect per unique host, only when delayed meta exists on the page. - Background WP-Cron hydration with round-robin domain throttling. When enabled,
dii_process_batchruns on the configured interval (default 1 minute), pulls a batch of delayed posts, expands to (post_id, type, url) tuples, buckets by image hostname, and rotates between hosts (max-consecutive-same-domain configurable, default 1 = strict round-robin). Per-host circuit breaker: 5+ failures from one host inside a tick puts that host on hold for the rest of the tick. - Cache-purge integrations.
dii_image_hydrated(post-level) anddii_thumbs_generated(attachment-level) now fire built-in purges for WP Rocket, W3 Total Cache, and LiteSpeed Cache. Plus a genericdii_purge_postaction for third-party / Cloudflare integrations. - Optional ultra-AJAX mu-loader. Drop
dii-mu-loader.phpintowp-content/mu-plugins/(one-click install button on the Status tab) to skip every other plugin's bootstrap for/wp-json/dii/v1/*requests. Reduces per-request latency without touching correctness — REST endpoints continue to work via the standard boot path if the loader is absent. Manual-install instructions are shown whenwp-content/mu-plugins/is not writable. - Hot-link blocklist scope corrected. The 2.20 implementation incorrectly suppressed server-side downloads for hosts on the blocklist — that was a misfeature (server-side fetches don't trigger hot-link blocking). The blocklist now applies only where it actually matters: the gallery-on-archive external-URL fallback. Server-side downloads (page-render featured, REST hydration, CLI, cron) ignore the list.
- New REST endpoints
POST /wp-json/dii/v1/hydrateandPOST /wp-json/dii/v1/generate-thumbnails. Custom-action nonce (dii_hydrate) sent viaX-DII-Nonceheader. Per-IP rate limit: 60 requests/minute. - New filter hooks
dii_circuit_breaker_threshold,dii_purge_post,dii_thumbs_generated. Existingdii_image_hydratednow fires for both featured and gallery hydration. - Status tab now shows live mu-loader install state (with install / update / remove buttons) and next-cron-tick timestamp.
2.20
- Refactored into
includes/per-class files. Behaviour identical to 2.10; the single-file plugin is now a bootstrap that wires upDII_Image_Processor,DII_Frontend_Renderer,DII_Admin_Screen_Router,DII_Settings,DII_Admin_Page,DII_Admin_List_Table,DII_CLI, andDII_Request_State. - Added the shared
super-speedy-settingsgit submodule and mounted the settings page under the Super Speedy admin banner alongside the rest of the family. The legacy Media → Delayed Images menu is preserved as a deep-link to the new tabbed page; bookmarks of the old?page=delayed-imagesURL redirect transparently. - Tabbed admin page (Status / Settings / Delayed Images / Loaded Images / Timed-Out Images). Hash-driven JS tab switching, single Save button persists every setting from every tab into one option (
delayed_image_import_settings). - Settings UI for: HTTP timeout (default 3 s, range 1–10), stampede wait (ms), failure cooldown (s), keep-medium-thumbnail toggle, force-HTTPS toggle, hot-link blocklist (newline-separated hostnames), REST consumer behaviour, plus cron toggle / interval / batch / round-robin parameters (cron itself lands in 3.x).
- Hot-link blocklist now causes page-render to skip sync and render SVG immediately for known-blocking hosts; CLI / cron paths still attempt the download.
- Six WP-CLI commands:
wp dii pending,wp dii load-featured,wp dii load-galleries,wp dii load-all,wp dii timed-out,wp dii retry. CLI / cron paths generate thumbnails inline (nodii_thumbs_pendingmarker), unlike page-render paths. wp dii retryclears stale failure-reason meta and cooldown transients before re-processing so the retry isn't suppressed by a previous-request cooldown.- New constants
DII_VERSION,DII_PLUGIN_FILE,DII_PLUGIN_DIR,DII_PLUGIN_URL.
2.10
- Context-aware processing: archive pages no longer trigger gallery downloads (sidebars, RSS feeds, related-posts widgets, REST embeds and other secondary loops are skipped entirely).
- Page-render path now caps the featured-image download at 3 seconds (filterable via
dii_http_timeout) instead of the previous 1 second hard-coded global. The timeout is scoped to the URL being downloaded so it no longer affects unrelated HTTP traffic during the sideload window. - Thumbnail sub-sizes are no longer generated during the page request —
intermediate_image_sizes_advancedis filtered to an empty array. Attachments are marked withdii_thumbs_pendingfor a future REST endpoint to fill in. Filterable viadii_keep_medium_sizeif you want themediumsize kept synchronously. - On download failure: error reason captured to
dii_last_errorpost meta, a 60-second cooldown transient prevents retry-on-every-page-view, and a placeholder SVG (assets/fallback.svg) renders in place of the broken image. Thedelayed_image_urlmeta is retained for the next visit to retry. - Stampede guard: a transient lock prevents two concurrent visitors from both attempting to download the same image. The second visitor briefly waits (default 3 seconds, filterable via
dii_lock_wait_ms) for the in-flight download to finish so they get the real image rather than the placeholder. - Per-request static cache prevents re-evaluating the same post's delayed meta multiple times in a single request (sidebars, related posts, etc.).
- Switched URL parser to pipe-only — see "Important" in the description above.
- New filter hooks
dii_should_process(gate per-post),dii_image_url(rewrite the URL before download),dii_after_hydrate(act after a successful download),dii_http_timeout,dii_keep_medium_size. - Removed dead code
process_admin_post_listno longer re-attaches an already-attachedthe_postlistener. - Activation no longer seeds empty meta on the first non-trash post (which was a discoverability hack for importers that scan
wp_postmeta); old empty meta from previous installs is cleaned up. Importers should refer to the meta key documentation in this readme.
2.01
- Fixed missing media function error when loading from front-end
2.0
- Renamed to Delayed Image Import
1.6
- Fixed an issue where the admin interface did not display delayed images due to incorrect column header settings.
- Defined the primary column in the admin interface for compatibility with WordPress's WP_List_Table.
- Updated the admin interface to display delayed images correctly.
- Added a screenshot to the plugin readme for better visualization.
1.5
- Removed custom cURL function and switched to WordPress's standard image handling functions.
- Set image download timeout to 1 second using the 'http_request_args' filter.
- Updated admin interface to include tabs and pagination for managing large datasets.
- Ensured compliance with Plugin Check Pro (PCP) by following WordPress coding standards.
1.4
- Implemented custom image download function with reduced timeout to speed up image processing.
- Added handling for images that timeout during download.
- Created an admin interface under Media to manage delayed images.
- Improved security and error handling throughout the plugin.
1.3
- Improved activation performance on sites with large numbers of posts by optimizing the query.
1.2
- Added activation hook to create empty meta_keys for the first post so that import plugins that scan wp_postmeta can find these keys
1.1
- Initial release.
