Documentation · Technical Documentation
Image Importer And Queue
This document describes how the ADP Car Market Hub plugin downloads, deduplicates and attaches vehicle images, and how the asynchronous image queue moves long-running image work out of the synchronous import path.
When to use this document
Read this document if you need to:
- Understand which images end up in the WordPress Media Library and which are reused or skipped.
- Diagnose missing, stale or duplicate vehicle images.
- Plan WebP conversion or storage capacity for a large catalogue.
- Tune cron-mode image processing for slow or restricted hosting.
For the surrounding import flow, see Import Engine. For the cron-side scheduling, see Cron Events And Scheduler.
Overview
Two classes share responsibility for image handling:
AS24CI\Image_Importer— downloads a list of image URLs, optionally converts them to WebP, attaches them to a vehicle post viamedia_handle_sideload(), and records the source URL in attachment postmeta for deduplication.AS24CI\Scheduler— owns the asynchronous image queue. The worker hookas24ci_image_queue_processprocesses pending items in batches with its own transient lock and time budget.
The importer (Import Engine) decides whether to import images synchronously or to defer most of them to the queue, based on the cron mode of the current run.
Source of truth for images
For any imported attachment, the canonical "where did this come
from?" marker is the _as24ci_source_url postmeta on the
attachment post. The plugin uses this marker to:
- Deduplicate downloads — if an attachment with the same source URL already exists, the plugin reuses it rather than downloading again.
- Rebuild the vehicle's
_as24ci_image_idslist authoritatively after the queue worker finishes, by querying child attachments with this meta key.
Two postmeta keys on the vehicle post track imported images:
_as24ci_image_ids— array of WordPress attachment IDs that belong to the importer's gallery._as24ci_images_hash— MD5 hash of the source image list. May hold the sentinel'pending_queue'while items are still in the queue. The next import treats'pending_queue'as a mismatch so the image list is always re-evaluated until it is fully imported.
Manually added gallery attachments are tracked separately in
_as24ci_manual_image_ids and are never deleted by the plugin.
Synchronous image import
Image_Importer::import_images( int $post_id, array $images, string
$listing_id ) performs the synchronous work:
- Lazily includes WordPress media admin files
(
wp-admin/includes/media.php,file.php,image.php). - Defensively re-applies the
as24ci_max_imagescap. - Pre-fetches the post's known source URLs once via
prefetch_known_urls_for_post()to avoid N per-image database queries. - For each image (string URL or array with
url/src/href/original/imageUrlkeys): - Reuses an existing attachment if the source URL is already known (in-memory map first, then a_as24ci_source_urlpostmeta lookup). - Otherwise callsdownload_url( $url, 30 )(30-second timeout), optionally converts to WebP, and callsmedia_handle_sideload()to create the attachment. - Writes_as24ci_source_urlon the new attachment and adds it to the in-memory deduplication map. - Logs failures (download errors, sideload errors) and moves on to the next image. - Merges the new attachment IDs into
_as24ci_image_ids(preserving previously imported items) and sets the post thumbnail to the first attachment if the post does not already have one.
Existing IDs are merged rather than overwritten so that queue-worker calls accumulate attachments for the same post over multiple runs.
WebP conversion
When as24ci_convert_to_webp is 1, the importer attempts to
convert each downloaded image to WebP before sideloading:
- Quality is read from
as24ci_webp_quality(default80if unset) and clamped to the range1–100. Theas24ci_webp_qualityfilter can override the value. - GD's
imagewebp()is tried first; Imagick is used as a fallback. - If neither extension is available, conversion is skipped and the original image is sideloaded. A log line is emitted.
- If conversion produces an empty file or fails, the temp file is cleaned up and the original image is used.
The converted file inherits the original filename with the extension
replaced by .webp.
Cron-mode (queued) import
When the scheduler runs an import (cron or REST), it sets
Importer::set_cron_image_queue( true ) if
as24ci_cron_image_queue is 1 (the default). For each vehicle
with more than one image, the importer:
- Downloads only the first image immediately via the synchronous path so the vehicle has a thumbnail.
- Pre-checks whether every remaining image is already attached
(via the deduplication map). If yes, no queue entry is created
and the final
_as24ci_images_hashis written immediately. - Otherwise calls
enqueue_remaining_images()to append items of the form{ post_id, listing_id, url, images_hash }to theas24ci_image_queueoption. The vehicle's_as24ci_images_hashis set to the sentinel'pending_queue'so future imports keep re-evaluating until the queue completes.
The queue is hard-capped at Scheduler::IMAGE_QUEUE_SIZE_LIMIT
items. New entries are dropped when the cap is reached and a log
line is emitted.
Queue worker
AS24CI\Scheduler::run_image_queue() is bound to the
as24ci_image_queue_process cron hook and runs the actual deferred
downloads:
- Calls
set_time_limit(300)(subject to host restrictions). - Acquires the
as24ci_image_queue_runningtransient lock with theScheduler::IMAGE_QUEUE_LOCK_TTLTTL. A stale lock (older than the TTL) is automatically cleared. - Loads the queue from the
as24ci_image_queueoption. - Processes items in batches of
Scheduler::IMAGE_QUEUE_BATCH_SIZEwhile the queue is non-empty and the time budget (IMAGE_QUEUE_LOCK_TTL − 60s) is not exhausted. - For each item, calls
Image_Importer::import_images()for the single URL. Errors are caught and logged; the item is dropped to avoid a poison-pill loop. - Persists the remaining queue and writes per-run stats to
as24ci_image_queue_last_run({ processed, failed, remaining, timestamp }). - For every post fully drained from the queue this run, rebuilds
_as24ci_image_idsauthoritatively from the actual child attachments tagged with_as24ci_source_urland stores the final_as24ci_images_hash. This guarantees the meta is complete and consistent even if individual queue items failed. - Schedules the next run via
maybe_schedule_image_queue_worker()if items remain.
The worker's images_hash value is validated to be a 32-character
lowercase MD5 string before use. Corrupted entries are treated as
absent so the post hash is not finalised from bad data.
Configuration reference
| Option / constant | Effect | Default |
|---|---|---|
as24ci_import_images | Master toggle for the import-images path. | (toggle in admin) |
as24ci_max_images | Plugin-side cap per vehicle. Enforced by both the importer and the image importer. | 30 |
as24ci_convert_to_webp | Convert downloaded images to WebP if possible. | 0 |
as24ci_webp_quality | WebP quality (1–100). Filter: as24ci_webp_quality. | 80 |
as24ci_cron_image_queue | Use the image queue during cron/REST imports. | 1 |
as24ci_image_queue (option) | Persistent pending image queue payload. | (empty array) |
as24ci_image_queue_last_run (option) | Stats from the last queue worker run. | (empty) |
as24ci_image_queue_running (transient) | Run-lock for the queue worker. | n/a |
Scheduler::IMAGE_QUEUE_HOOK | Cron hook for the queue worker (as24ci_image_queue_process). | n/a |
Scheduler::IMAGE_QUEUE_LOCK_TTL | Worker lock TTL (auto-clears a stale lock). | (constant in code) |
Scheduler::IMAGE_QUEUE_BATCH_SIZE | Items processed per inner batch. | (constant in code) |
Scheduler::IMAGE_QUEUE_SIZE_LIMIT | Hard cap on queue size. | (constant in code) |
The exact values of IMAGE_QUEUE_LOCK_TTL, IMAGE_QUEUE_BATCH_SIZE
and IMAGE_QUEUE_SIZE_LIMIT are defined as constants on
AS24CI\Scheduler and may change between plugin versions. Verify
them in the current source if you need to plan for a specific load.
Operational notes
- WP-Cron is the default trigger. On low-traffic sites consider a
server-side cron / system cron pointing at
wp-cron.php(withDISABLE_WP_CRONset totrue) so the queue drains predictably. - Image downloads are performed via
download_url()which uses WordPress's HTTP API. Hosts that block outbound HTTP must whitelist the AutoScout24 image hosts. - Image deduplication is based on the exact source URL. URLs that differ only in query string or signature parameters will be treated as distinct sources and downloaded separately.
- Manual gallery attachments tracked in
_as24ci_manual_image_idsare never overwritten or removed by the importer or the queue worker. - The queue worker rebuilds
_as24ci_image_idsfrom the actual child attachments at the end of each fully-drained post; if you manipulate the meta directly between worker runs, your changes may be reverted.
Troubleshooting
- Vehicles appear without any images. Confirm
as24ci_import_imagesis enabled. Check thatas24ci_max_imagesis non-zero where intended. In cron-mode the primary image is imported synchronously; if no images appear at all, look for download errors in the plugin log. - Only the first image is present after a cron import. This is
expected during cron-mode imports until the
as24ci_image_queue_processworker finishes. Checkas24ci_image_queue_last_runfor progress and the queue size. - The queue grows but never drains. Verify that WP-Cron is
running (or that a server cron triggers
wp-cron.php). Check for a stuckas24ci_image_queue_runningtransient older than the worker's TTL. The worker auto-clears stale locks on the next run. - WebP conversion does not happen. Make sure
as24ci_convert_to_webpis1and that either GD with WebP support (imagewebp) or Imagick is installed. The plugin logs the reason when conversion is skipped. - Duplicate-looking images appear in the gallery. The dedupe key is the exact source URL. CDNs that append unique query parameters per request will defeat dedupe. Verify the URLs in the API payload via verbose logging.
- Images disappeared after deleting a vehicle. The
Vehicle_Deleterremoves attachments tracked in_as24ci_image_ids. Manual gallery attachments (_as24ci_manual_image_ids) are preserved. Verify which list contained the affected attachment ID.