Die Bildoptimierung für WordPress ist ein entscheidender Hebel zur Performance-Steigerung….
Die Bildoptimierung für WordPress ist ein entscheidender Hebel zur Performance-Steigerung. Allerdings kann dieser Prozess sehr aufwändig sein, insbesondere bei Websites mit einer umfangreichen WordPress-Mediathek.
Wer schon einmal ein Bild in die WordPress-Mediathek hochgeladen hat, kennt das Phänomen: Man lädt ein einziges Foto hoch und plötzlich liegen fünf oder mehr Varianten davon auf dem Server.
Das Problem mit erzeugten Bildgrößen (srcset)
WordPress generiert bei jedem Upload automatisch mehrere Bildgrößen (Thumbnail, Medium, Large usw.), damit für verschiedene Bildschirmgrößen passende Versionen bereitstehen.
Was als Performance-Feature gedacht ist, wird in der Praxis schnell zum Ballast, besonders wenn die hochgeladenen Bilder ohnehin viel zu groß sind, im veralteten JPEG- oder PNG-Format vorliegen und die automatisch erzeugten Varianten niemand wirklich braucht!

Unter Einstellungen → Medien lassen sich zwar die Maße für Medium und Large auf Null setzen, um deren Erzeugung zu unterbinden, doch WordPress generiert darüber hinaus weitere Varianten, die sich über die Benutzeroberfläche nicht abschalten lassen.
Außerdem betrifft jede Änderung an diesen Einstellungen nur zukünftige Uploads. Bilder, die bereits in der Mediathek liegen, bleiben unangetastet. Je größer das Original, desto mehr Versionen erzeugt WordPress automatisch — bei einem 4000 × 3000 Pixel großen Bild können das schnell fünf oder mehr zusätzliche Dateien sein.
Zusammengefasst gibt es also drei zentrale Probleme: Erstens sind die Bilddimensionen oft viel zu groß für den Einsatz im Web. Zweitens sind Formate wie JPEG und PNG nicht mehr zeitgemäß.
WebP wird inzwischen von nahezu jedem Browser unterstützt und liefert bei gleicher Qualität deutlich kleinere Dateien. Und drittens füllen unnötige automatisch generierte Bildvarianten den Speicher, ohne echten Nutzen zu bringen.
PixRefiner – Automatisierte Bildoptimierung für WordPress
Für genau diese drei Probleme hat Imran von Web Squadron eine elegante Open-Source-Lösung entwickelt: den PixRefiner.
Dabei handelt es sich um ein PHP-Code-Snippet, das direkt in die functions.php-Datei des Child-Themes eingefügt werden kann. Alternativ kann ein Plugin wie Fluent Snippets oder WPCodeBox genutzt werden, um das Snippet in die eigene WordPress-Installation einzubinden.
Die Installation eines separaten Plugins ist also nicht erforderlich!
Der Pixrefiner optimiert für Kadence
Der PixRefiner von Imran funktioniert mit den Standardeinstellungen hervorragend im klassischen Gutenberg-Image-Block.
Wer allerdings das Kadence Theme mit Kadence Blocks nutzt, stößt auf ein spezifisches Problem: Der Kadence Advanced Image Block zeigt im Größen-Dropdown keine der vom PixRefiner erzeugten Bildvarianten an — obwohl die Dateien korrekt auf dem Server liegen und im Standard-Block problemlos verfügbar sind.
Die Lösung: Eine Kadence Edition vom PixRefiner
In der angepassten Kadence Edition des PixRefiners habe ich vier wesentliche Änderungen vorgenommen:
Erstens werden für jede generierte Bildgröße die tatsächlichen Pixel-Dimensionen (Breite und Höhe) aus der erzeugten Datei ausgelesen und in den Metadaten gespeichert. Dadurch erkennt Kadence jede Variante als gültige Bildgröße an.
Zweitens werden die benutzerdefinierten Größennamen (custom-1200, custom-600, custom-300) auf die Standard-WordPress-Bezeichnungen large, medium_large und medium gemappt. Kadence sucht bevorzugt nach diesen bekannten Namen — durch das Mapping erscheinen die Größen nun sauber und ohne Duplikate im Dropdown des Advanced Image Blocks:
- large (z. B. 1200 × 900)
- medium_large (z. B. 600 × 450)
- medium (z. B. 300 × 225)
- thumbnail (150 × 150)
- full (Originalgröße, z. B. 1920 × 1440)
Drittens wurde der gesamte Elementor-spezifische Code entfernt. Die Originalversion des PixRefiners enthält zahlreiche Funktionen, die ausschließlich für Elementor-Nutzer relevant sind – etwa das rekursive Parsen von JSON-Inhalten oder die spezielle Behandlung von Elementor-Post-Types. Für Kadence-Nutzer ist das alles überflüssiger Ballast. Die Kadence Edition ist daher deutlich schlanker und auf das reduziert, was tatsächlich gebraucht wird.
Viertens habe ich die Max Height Cap Funktion eingebaut. Sie verhindert, dass Bilder im Hochvormat nicht ausreichend optimiert werden. Ich werde später, wenn wir über die Einstellungen sprechen, genauer darauf eingehen.
Pixrefiner für Kadence – Code-Snippet
Ggf. muss das PHP Opening Tag (<?php) vor dem Einfügen entfernt werden.
<?php
/**
* PixRefiner v4.0 — Kadence Edition
*
* Converts uploaded images to WebP/AVIF, generates responsive sizes,
* and writes correct metadata so that Kadence Blocks (Adv. Image)
* can see every srcset size in its UI.
*
* Key changes from v3.6
* ---------------------
* • Stores real width AND height in every custom-size metadata entry
* (fixes Kadence's size picker which requires both dimensions).
* • Maps custom sizes → standard WP names (large / medium_large / medium)
* so Kadence's block UI shows them with familiar labels.
* • Removed all Elementor-specific code (JSON content parsing,
* elementor_ post-type skipping, recursive URL replacer).
* • Simplified "Fix URLs" to handle standard HTML post content only.
* • General code clean-up & consolidation.
*/
// ── Helpers ──────────────────────────────────────────────────────────
function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = ($bytes > 0) ? floor(log($bytes) / log(1024)) : 0;
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
/**
* Read the real pixel dimensions of an image file.
* Returns ['width' => int, 'height' => int] or false on failure.
*/
function pixrefiner_get_real_dimensions($file_path) {
if (!file_exists($file_path)) return false;
$info = @getimagesize($file_path);
if (!$info) {
// Fallback for AVIF/WebP that getimagesize may not handle on older PHP
$editor = wp_get_image_editor($file_path);
if (!is_wp_error($editor)) {
return $editor->get_size(); // ['width' => …, 'height' => …]
}
return false;
}
return ['width' => $info[0], 'height' => $info[1]];
}
// ── Settings getters ─────────────────────────────────────────────────
function wpturbo_get_max_widths() {
$value = get_option('webp_max_widths', '1920,1200,600,300');
$widths = array_map('absint', array_filter(explode(',', $value)));
$widths = array_filter($widths, function ($w) { return $w > 0 && $w <= 9999; });
return array_slice($widths, 0, 4);
}
function wpturbo_get_max_heights() {
$value = get_option('webp_max_heights', '1080,720,480,360');
$heights = array_map('absint', array_filter(explode(',', $value)));
$heights = array_filter($heights, function ($h) { return $h > 0 && $h <= 9999; });
return array_slice($heights, 0, 4);
}
function wpturbo_get_resize_mode() { return get_option('webp_resize_mode', 'width'); }
function wpturbo_get_quality() { return (int) get_option('webp_quality', 80); }
function wpturbo_get_batch_size() { return (int) get_option('webp_batch_size', 5); }
function wpturbo_get_preserve_originals() { return (bool) get_option('webp_preserve_originals', false); }
function wpturbo_get_disable_auto_conversion() { return (bool) get_option('webp_disable_auto_conversion', false); }
function wpturbo_get_min_size_kb() { return (int) get_option('webp_min_size_kb', 0); }
function wpturbo_get_use_avif() { return (bool) get_option('webp_use_avif', false); }
function wpturbo_get_max_height_cap() { return (int) get_option('webp_max_height_cap', 0); }
function wpturbo_get_excluded_images() {
$excluded = get_option('webp_excluded_images', []);
return is_array($excluded) ? array_map('absint', $excluded) : [];
}
// ── Excluded-image management ────────────────────────────────────────
function wpturbo_add_excluded_image($attachment_id) {
$attachment_id = absint($attachment_id);
$excluded = wpturbo_get_excluded_images();
if (!in_array($attachment_id, $excluded)) {
$excluded[] = $attachment_id;
update_option('webp_excluded_images', array_unique($excluded));
pixrefiner_log(sprintf('Excluded image added: Attachment ID %d', $attachment_id));
return true;
}
return false;
}
function wpturbo_remove_excluded_image($attachment_id) {
$attachment_id = absint($attachment_id);
$excluded = wpturbo_get_excluded_images();
$index = array_search($attachment_id, $excluded);
if ($index !== false) {
unset($excluded[$index]);
update_option('webp_excluded_images', array_values($excluded));
pixrefiner_log(sprintf('Excluded image removed: Attachment ID %d', $attachment_id));
return true;
}
return false;
}
// ── Logging helper ───────────────────────────────────────────────────
function pixrefiner_log($message) {
$log = get_option('webp_conversion_log', []);
$log[] = $message;
update_option('webp_conversion_log', array_slice((array) $log, -500));
}
// ── Limit default WP sizes to thumbnail only ─────────────────────────
add_filter('intermediate_image_sizes_advanced', function ($sizes) {
if (wpturbo_get_disable_auto_conversion()) return $sizes;
return [
'thumbnail' => [
'width' => 150,
'height' => 150,
'crop' => true,
],
];
}, 99);
add_action('admin_init', function () {
update_option('thumbnail_size_w', 150);
update_option('thumbnail_size_h', 150);
update_option('thumbnail_crop', 1);
});
// ── Register custom image sizes ──────────────────────────────────────
add_action('after_setup_theme', 'wpturbo_register_custom_sizes');
function wpturbo_register_custom_sizes() {
$mode = wpturbo_get_resize_mode();
if ($mode === 'width') {
foreach (array_slice(wpturbo_get_max_widths(), 1, 3) as $w) {
add_image_size("custom-$w", $w, 0, false);
}
} else {
foreach (array_slice(wpturbo_get_max_heights(), 1, 3) as $h) {
add_image_size("custom-$h", 0, $h, false);
}
}
}
// ── Disable big-image scaling (we handle our own) ────────────────────
add_filter('big_image_size_threshold', '__return_false', 999);
// ── MIME-type helper (.htaccess) ─────────────────────────────────────
function wpturbo_ensure_mime_types() {
$htaccess = ABSPATH . '.htaccess';
if (!file_exists($htaccess) || !is_writable($htaccess)) return false;
$content = file_get_contents($htaccess);
$webp_mime = 'AddType image/webp .webp';
$avif_mime = 'AddType image/avif .avif';
$changed = false;
if (strpos($content, $webp_mime) === false || strpos($content, $avif_mime) === false) {
$block = "\n# BEGIN PixRefiner MIME Types\n";
if (strpos($content, $webp_mime) === false) { $block .= "$webp_mime\n"; }
if (strpos($content, $avif_mime) === false) { $block .= "$avif_mime\n"; }
$block .= "# END PixRefiner MIME Types\n";
file_put_contents($htaccess, $content . $block);
$changed = true;
}
return $changed;
}
register_activation_hook(__FILE__, 'wpturbo_ensure_mime_types');
add_action('update_option_webp_use_avif', 'wpturbo_ensure_mime_types');
// ── Core conversion (resize + format) ────────────────────────────────
function wpturbo_convert_to_format($file_path, $dimension, &$log = null, $attachment_id = null, $suffix = '') {
$use_avif = wpturbo_get_use_avif();
$format = $use_avif ? 'image/avif' : 'image/webp';
$extension = $use_avif ? '.avif' : '.webp';
$quality = wpturbo_get_quality();
$mode = wpturbo_get_resize_mode();
$path_info = pathinfo($file_path);
$new_path = $path_info['dirname'] . '/' . $path_info['filename'] . $suffix . $extension;
if (!(extension_loaded('imagick') || extension_loaded('gd'))) {
if ($log !== null) $log[] = sprintf('Error: No image library available for %s', basename($file_path));
return false;
}
if ($use_avif) {
$has_avif = (extension_loaded('imagick') && in_array('AVIF', Imagick::queryFormats()))
|| (extension_loaded('gd') && function_exists('imageavif'));
if (!$has_avif) {
if ($log !== null) $log[] = sprintf('Error: AVIF not supported for %s', basename($file_path));
return false;
}
}
$editor = wp_get_image_editor($file_path);
if (is_wp_error($editor)) {
if ($log !== null) $log[] = sprintf('Error: Editor failed for %s – %s', basename($file_path), $editor->get_error_message());
return false;
}
$dims = $editor->get_size();
$resized = false;
if ($mode === 'width' && $dims['width'] > $dimension) {
$editor->resize($dimension, null, false);
$resized = true;
} elseif ($mode === 'height' && $dims['height'] > $dimension) {
$editor->resize(null, $dimension, false);
$resized = true;
}
// Apply height cap in width mode (prevents overly tall portrait images)
$height_cap = wpturbo_get_max_height_cap();
if ($mode === 'width' && $height_cap > 0) {
$current = $editor->get_size();
if ($current['height'] > $height_cap) {
$editor->resize(null, $height_cap, false);
$resized = true;
}
}
$result = $editor->save($new_path, $format, ['quality' => $quality]);
if (is_wp_error($result)) {
if ($log !== null) $log[] = sprintf('Error: Conversion failed for %s – %s', basename($file_path), $result->get_error_message());
return false;
}
if ($log !== null) {
$log[] = sprintf(
'Converted: %s → %s %s',
basename($file_path),
basename($new_path),
$resized ? sprintf('(resized to %dpx %s, q%d)', $dimension, $mode, $quality) : sprintf('(q%d)', $quality)
);
}
return $new_path;
}
// ── Build correct size metadata entry (real dimensions!) ─────────────
/**
* Returns a metadata-ready array for one size, with REAL width & height.
*/
function pixrefiner_size_meta($file_path, $filename, $format) {
$dims = pixrefiner_get_real_dimensions($file_path);
return [
'file' => $filename,
'width' => $dims ? (int) $dims['width'] : 0,
'height' => $dims ? (int) $dims['height'] : 0,
'mime-type' => $format,
];
}
// ── Map custom sizes → standard WP names for Kadence ─────────────────
/**
* Dynamically maps custom-{dimension} → large / medium_large / medium
* so Kadence Blocks sees them with familiar labels.
*/
add_filter('wp_get_attachment_metadata', function ($metadata, $attachment_id) {
if (empty($metadata['sizes'])) return $metadata;
$mode = wpturbo_get_resize_mode();
$max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
$additional = array_slice($max_values, 1, 3); // up to 3 extra sizes
// Map positions to standard names (largest extra → large, etc.)
$standard_names = ['large', 'medium_large', 'medium'];
foreach ($additional as $i => $dim) {
$custom_key = "custom-$dim";
$standard_key = isset($standard_names[$i]) ? $standard_names[$i] : null;
if ($standard_key && isset($metadata['sizes'][$custom_key])) {
if (!isset($metadata['sizes'][$standard_key])) {
$metadata['sizes'][$standard_key] = $metadata['sizes'][$custom_key];
}
// Remove the custom-named duplicate so Kadence doesn't list both
unset($metadata['sizes'][$custom_key]);
}
}
return $metadata;
}, 10, 2);
// ── Handle new uploads ───────────────────────────────────────────────
add_filter('wp_handle_upload', 'wpturbo_handle_upload_convert', 10, 1);
function wpturbo_handle_upload_convert($upload) {
if (wpturbo_get_disable_auto_conversion()) return $upload;
$ext = strtolower(pathinfo($upload['file'], PATHINFO_EXTENSION));
if (!in_array($ext, ['jpg', 'jpeg', 'png', 'webp', 'avif'])) return $upload;
$use_avif = wpturbo_get_use_avif();
$format = $use_avif ? 'image/avif' : 'image/webp';
$extension = $use_avif ? '.avif' : '.webp';
$file_path = $upload['file'];
$log = get_option('webp_conversion_log', []);
if (!is_writable(dirname($file_path))) {
$log[] = sprintf('Error: Directory not writable for %s', basename($file_path));
update_option('webp_conversion_log', array_slice((array) $log, -500));
return $upload;
}
// Min-size check
$file_kb = filesize($file_path) / 1024;
$min_kb = wpturbo_get_min_size_kb();
if ($min_kb > 0 && $file_kb < $min_kb) {
$log[] = sprintf('Skipped: %s (%.1f KB < %d KB)', basename($file_path), $file_kb, $min_kb);
update_option('webp_conversion_log', array_slice((array) $log, -500));
return $upload;
}
$mode = wpturbo_get_resize_mode();
$max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
// Get original dimensions to skip sizes larger than the source
$editor = wp_get_image_editor($file_path);
if (is_wp_error($editor)) {
$log[] = sprintf('Error: Editor failed for %s', basename($file_path));
update_option('webp_conversion_log', array_slice((array) $log, -500));
return $upload;
}
$orig = $editor->get_size();
$orig_dim = ($mode === 'width') ? $orig['width'] : $orig['height'];
$valid_vals = array_filter($max_values, function ($v, $i) use ($orig_dim) {
return $i === 0 || $v <= $orig_dim;
}, ARRAY_FILTER_USE_BOTH);
$new_files = [];
$success = true;
// Convert each size
foreach ($valid_vals as $index => $dimension) {
$suffix = ($index === 0) ? '' : "-{$dimension}";
$new_file = wpturbo_convert_to_format($file_path, $dimension, $log, null, $suffix);
if ($new_file) {
if ($index === 0) {
$upload['file'] = $new_file;
$upload['url'] = str_replace(basename($file_path), basename($new_file), $upload['url']);
$upload['type'] = $format;
}
$new_files[] = $new_file;
} else {
$success = false;
break;
}
}
// Generate 150×150 thumbnail
if ($success) {
$thumb_path = dirname($file_path) . '/' . pathinfo($file_path, PATHINFO_FILENAME) . '-150x150' . $extension;
$editor = wp_get_image_editor($file_path);
if (!is_wp_error($editor)) {
$editor->resize(150, 150, true);
$saved = $editor->save($thumb_path, $format, ['quality' => wpturbo_get_quality()]);
if (!is_wp_error($saved)) {
$log[] = sprintf('Generated thumbnail: %s', basename($thumb_path));
$new_files[] = $thumb_path;
} else {
$success = false;
}
} else {
$success = false;
}
}
// Rollback on failure
if (!$success) {
foreach ($new_files as $f) { if (file_exists($f)) @unlink($f); }
$log[] = sprintf('Error: Conversion failed for %s – rolling back', basename($file_path));
update_option('webp_conversion_log', array_slice((array) $log, -500));
return $upload;
}
// Update metadata with REAL dimensions
$attachment_id = attachment_url_to_postid($upload['url']);
if ($attachment_id && !empty($new_files)) {
$metadata = wp_generate_attachment_metadata($attachment_id, $upload['file']);
if (!is_wp_error($metadata)) {
$base_name = pathinfo($file_path, PATHINFO_FILENAME);
$dirname = dirname($file_path);
// Custom sizes – with real dimensions
foreach ($valid_vals as $index => $dimension) {
if ($index === 0) continue;
$size_file = "$dirname/$base_name-$dimension$extension";
if (file_exists($size_file)) {
$metadata['sizes']["custom-$dimension"] = pixrefiner_size_meta(
$size_file,
"$base_name-$dimension$extension",
$format
);
}
}
// Thumbnail
$thumb_file = "$dirname/$base_name-150x150$extension";
if (file_exists($thumb_file)) {
$metadata['sizes']['thumbnail'] = pixrefiner_size_meta(
$thumb_file,
"$base_name-150x150$extension",
$format
);
}
$metadata['webp_quality'] = wpturbo_get_quality();
$metadata['pixrefiner_stamp'] = pixrefiner_build_stamp($valid_vals);
update_attached_file($attachment_id, $upload['file']);
wp_update_post(['ID' => $attachment_id, 'post_mime_type' => $format]);
wp_update_attachment_metadata($attachment_id, $metadata);
}
}
// Delete original source file
if ($ext !== ($use_avif ? 'avif' : 'webp') && file_exists($file_path) && !wpturbo_get_preserve_originals()) {
pixrefiner_safe_delete($file_path, $log);
}
update_option('webp_conversion_log', array_slice((array) $log, -500));
return $upload;
}
// ── Fix metadata hook (runs after wp_generate_attachment_metadata) ────
add_filter('wp_generate_attachment_metadata', 'wpturbo_fix_format_metadata', 10, 2);
function wpturbo_fix_format_metadata($metadata, $attachment_id) {
$use_avif = wpturbo_get_use_avif();
$extension = $use_avif ? 'avif' : 'webp';
$format = $use_avif ? 'image/avif' : 'image/webp';
$file = get_attached_file($attachment_id);
if (pathinfo($file, PATHINFO_EXTENSION) !== $extension) return $metadata;
$uploads = wp_upload_dir();
$dirname = dirname($file);
$base_name = pathinfo(basename($file), PATHINFO_FILENAME);
$mode = wpturbo_get_resize_mode();
$max_vals = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
$metadata['file'] = str_replace($uploads['basedir'] . '/', '', $file);
$metadata['mime_type'] = $format;
foreach ($max_vals as $i => $dim) {
if ($i === 0) continue;
$size_file = "$dirname/$base_name-$dim.$extension";
if (file_exists($size_file)) {
$metadata['sizes']["custom-$dim"] = pixrefiner_size_meta(
$size_file,
"$base_name-$dim.$extension",
$format
);
}
}
$thumb = "$dirname/$base_name-150x150.$extension";
if (file_exists($thumb)) {
$metadata['sizes']['thumbnail'] = pixrefiner_size_meta(
$thumb,
"$base_name-150x150.$extension",
$format
);
}
$metadata['pixrefiner_stamp'] = pixrefiner_build_stamp($max_vals);
return $metadata;
}
// ── Stamp helpers ────────────────────────────────────────────────────
function pixrefiner_build_stamp($max_values = null) {
$mode = wpturbo_get_resize_mode();
return [
'format' => wpturbo_get_use_avif() ? 'avif' : 'webp',
'quality' => wpturbo_get_quality(),
'resize_mode' => $mode,
'max_values' => $max_values ?: (($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights()),
'height_cap' => wpturbo_get_max_height_cap(),
];
}
function pixrefiner_stamp_matches($attachment_id) {
$meta = wp_get_attachment_metadata($attachment_id);
$stamp = isset($meta['pixrefiner_stamp']) ? $meta['pixrefiner_stamp'] : null;
return !empty($stamp) && $stamp === pixrefiner_build_stamp() && empty($_GET['force_reconvert']);
}
// ── Safe delete with retries ─────────────────────────────────────────
function pixrefiner_safe_delete($file_path, &$log) {
$attempts = 0;
while ($attempts < 5 && file_exists($file_path)) {
if (!is_writable($file_path)) {
@chmod($file_path, 0644);
if (!is_writable($file_path)) {
$log[] = sprintf('Error: Cannot make %s writable – skipping', basename($file_path));
return false;
}
}
if (@unlink($file_path)) {
$log[] = sprintf('Deleted original: %s', basename($file_path));
return true;
}
$attempts++;
sleep(1);
}
if (file_exists($file_path)) {
$log[] = sprintf('Error: Failed to delete %s after retries', basename($file_path));
}
return false;
}
// ── Batch conversion (AJAX) ──────────────────────────────────────────
function wpturbo_convert_single_image() {
check_ajax_referer('webp_converter_nonce', 'nonce');
if (!current_user_can('manage_options') || !isset($_POST['offset'])) {
wp_send_json_error('Permission denied or invalid offset');
}
$offset = absint($_POST['offset']);
$batch_size = wpturbo_get_batch_size();
$mode = wpturbo_get_resize_mode();
$max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
$current_ext = wpturbo_get_use_avif() ? 'avif' : 'webp';
$format = wpturbo_get_use_avif() ? 'image/avif' : 'image/webp';
$quality = wpturbo_get_quality();
wp_raise_memory_limit('image');
set_time_limit(max(30, 10 * $batch_size));
$attachments = get_posts([
'post_type' => 'attachment',
'post_mime_type' => ['image/jpeg', 'image/png', 'image/webp', 'image/avif'],
'posts_per_page' => $batch_size,
'offset' => $offset,
'fields' => 'ids',
'post__not_in' => wpturbo_get_excluded_images(),
]);
$log = get_option('webp_conversion_log', []);
if (empty($attachments)) {
update_option('webp_conversion_complete', true);
$log[] = "<strong style='color:#281E5D;'>Conversion Complete</strong>: No more images to process";
update_option('webp_conversion_log', array_slice((array) $log, -500));
wp_send_json_success(['complete' => true]);
}
foreach ($attachments as $attachment_id) {
$file_path = get_attached_file($attachment_id);
if (!file_exists($file_path)) continue;
// Skip if already optimised with matching settings
if (pixrefiner_stamp_matches($attachment_id)) {
$log[] = sprintf('Skipped: Already optimized ID %d', $attachment_id);
continue;
}
$dirname = dirname($file_path);
$base_name = pathinfo($file_path, PATHINFO_FILENAME);
$ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
// Get original dimensions
$editor = wp_get_image_editor($file_path);
if (is_wp_error($editor)) {
$log[] = sprintf('Error: Editor failed for %s', basename($file_path));
continue;
}
$orig = $editor->get_size();
$orig_dim = ($mode === 'width') ? $orig['width'] : $orig['height'];
$valid_vals = array_filter($max_values, function ($v, $i) use ($orig_dim) {
return $i === 0 || $v <= $orig_dim;
}, ARRAY_FILTER_USE_BOTH);
// Clean up old sizes from filesystem
$meta = wp_get_attachment_metadata($attachment_id);
$old_sizes = isset($meta['sizes']) ? $meta['sizes'] : [];
$main_size = $max_values[0] ?? null;
foreach ($old_sizes as $key => $data) {
if (preg_match('/custom-(\d+)/', $key, $m)) {
$old_dim = (int) $m[1];
if (!in_array($old_dim, $max_values) || ($main_size && $old_dim === $main_size)) {
$old_file = "$dirname/$base_name-$old_dim.$current_ext";
if (file_exists($old_file)) {
@unlink($old_file);
$log[] = sprintf('Deleted outdated size: %s', basename($old_file));
}
}
}
}
// Generate new sizes
$new_files = [];
$success = true;
foreach ($valid_vals as $index => $dimension) {
$suffix = ($index === 0) ? '' : "-$dimension";
$output = wpturbo_convert_to_format($file_path, $dimension, $log, $attachment_id, $suffix);
if ($output) {
if ($index === 0) {
update_attached_file($attachment_id, $output);
wp_update_post(['ID' => $attachment_id, 'post_mime_type' => $format]);
}
$new_files[] = $output;
} else {
$success = false;
break;
}
}
// Generate thumbnail
$thumb_path = "$dirname/$base_name-150x150.$current_ext";
if ($success && !file_exists($thumb_path)) {
$editor = wp_get_image_editor($file_path);
if (!is_wp_error($editor)) {
$editor->resize(150, 150, true);
$saved = $editor->save($thumb_path, $format, ['quality' => $quality]);
if (!is_wp_error($saved)) {
$new_files[] = $thumb_path;
$log[] = sprintf('Generated thumbnail: %s', basename($thumb_path));
} else {
$success = false;
}
} else {
$success = false;
}
}
if (!$success) {
foreach ($new_files as $f) { if (file_exists($f)) @unlink($f); }
$log[] = sprintf('Error: Conversion failed for %s – rolled back', basename($file_path));
continue;
}
// Update metadata with REAL dimensions
if (!empty($new_files)) {
$meta = wp_generate_attachment_metadata($attachment_id, $new_files[0]);
if (!is_wp_error($meta)) {
$meta['sizes'] = [];
foreach ($valid_vals as $index => $dimension) {
if ($index === 0) continue;
$size_file = "$dirname/$base_name-$dimension.$current_ext";
if (file_exists($size_file)) {
$meta['sizes']["custom-$dimension"] = pixrefiner_size_meta(
$size_file,
"$base_name-$dimension.$current_ext",
$format
);
}
}
if (file_exists($thumb_path)) {
$meta['sizes']['thumbnail'] = pixrefiner_size_meta(
$thumb_path,
"$base_name-150x150.$current_ext",
$format
);
}
$meta['webp_quality'] = $quality;
$meta['pixrefiner_stamp'] = pixrefiner_build_stamp();
wp_update_attachment_metadata($attachment_id, $meta);
}
}
// Delete original
if (!wpturbo_get_preserve_originals() && file_exists($file_path) && $ext !== $current_ext) {
@unlink($file_path);
$log[] = sprintf('Deleted original: %s', basename($file_path));
}
$log[] = sprintf('Converted Attachment ID %d successfully.', $attachment_id);
}
update_option('webp_conversion_log', array_slice((array) $log, -500));
wp_send_json_success([
'complete' => false,
'offset' => $offset + $batch_size,
]);
}
// ── Status (AJAX) ────────────────────────────────────────────────────
function wpturbo_webp_conversion_status() {
check_ajax_referer('webp_converter_nonce', 'nonce');
if (!current_user_can('manage_options')) wp_send_json_error('Permission denied');
$total = wp_count_posts('attachment')->inherit;
$per_page = 50;
$converted = 0;
$skipped = 0;
for ($off = 0; $off < $total; $off += $per_page) {
$converted += count(get_posts([
'post_type' => 'attachment', 'post_mime_type' => wpturbo_get_use_avif() ? 'image/avif' : 'image/webp',
'posts_per_page' => $per_page, 'offset' => $off, 'fields' => 'ids',
]));
}
for ($off = 0; $off < $total; $off += $per_page) {
$skipped += count(get_posts([
'post_type' => 'attachment', 'post_mime_type' => ['image/jpeg', 'image/png'],
'posts_per_page' => $per_page, 'offset' => $off, 'fields' => 'ids',
]));
}
$excluded_images = wpturbo_get_excluded_images();
$excluded_data = [];
foreach ($excluded_images as $id) {
$thumb = wp_get_attachment_image_src($id, 'thumbnail');
$excluded_data[] = ['id' => $id, 'title' => get_the_title($id), 'thumbnail' => $thumb ? $thumb[0] : ''];
}
$mode = wpturbo_get_resize_mode();
$max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
wp_send_json([
'total' => $total,
'converted' => $converted,
'skipped' => $skipped,
'remaining' => $total - $converted - $skipped,
'excluded' => count($excluded_images),
'excluded_images' => $excluded_data,
'log' => get_option('webp_conversion_log', []),
'complete' => get_option('webp_conversion_complete', false),
'resize_mode' => $mode,
'max_values' => implode(', ', $max_values),
'max_widths' => implode(', ', wpturbo_get_max_widths()),
'max_heights' => implode(', ', wpturbo_get_max_heights()),
'quality' => wpturbo_get_quality(),
'preserve_originals' => wpturbo_get_preserve_originals(),
'disable_auto_conversion' => wpturbo_get_disable_auto_conversion(),
'min_size_kb' => wpturbo_get_min_size_kb(),
'max_height_cap' => wpturbo_get_max_height_cap(),
'use_avif' => wpturbo_get_use_avif(),
]);
}
// ── Fix URLs in post content (simplified – no Elementor) ─────────────
add_action('wp_ajax_convert_post_images_to_webp', 'wpturbo_convert_post_images_to_format');
function wpturbo_convert_post_images_to_format() {
check_ajax_referer('webp_converter_nonce', 'nonce');
if (!current_user_can('manage_options')) wp_send_json_error('Permission denied');
$log = get_option('webp_conversion_log', []);
$use_avif = wpturbo_get_use_avif();
$extension = $use_avif ? 'avif' : 'webp';
$upload_dir = wp_upload_dir();
$baseurl = $upload_dir['baseurl'];
$basedir = $upload_dir['basedir'];
$log[] = sprintf('Starting post/page URL conversion to %s …', $use_avif ? 'AVIF' : 'WebP');
$post_types = array_unique(array_merge(
get_post_types(['public' => true], 'names'),
['wp_template', 'wp_template_part', 'wp_block']
));
$posts = get_posts([
'post_type' => $post_types,
'posts_per_page' => -1,
'fields' => 'ids',
]);
if (!$posts) {
$log[] = 'No posts found';
update_option('webp_conversion_log', array_slice((array) $log, -500));
wp_send_json_success(['message' => 'No posts found']);
}
$updated = 0;
$checked = 0;
$links = 0;
// Reusable URL replacer
$replace_url = function ($original_url) use ($baseurl, $basedir, $extension, &$checked) {
if (strpos($original_url, $baseurl) !== 0) return $original_url;
$checked++;
$dirname = pathinfo($original_url, PATHINFO_DIRNAME);
$filename = pathinfo($original_url, PATHINFO_FILENAME);
// Try direct match
foreach (["$filename.$extension", "$filename-scaled.$extension"] as $candidate) {
$path = str_replace($baseurl, $basedir, "$dirname/$candidate");
if (file_exists($path)) return "$dirname/$candidate";
}
// Fallback: strip dimension suffix
$base = preg_replace('/(-\d+x\d+|-scaled)$/', '', $filename);
foreach (["$base.$extension", "$base-scaled.$extension"] as $candidate) {
$path = str_replace($baseurl, $basedir, "$dirname/$candidate");
if (file_exists($path)) return "$dirname/$candidate";
}
return $original_url;
};
foreach ($posts as $post_id) {
$content = get_post_field('post_content', $post_id);
$original = $content;
// Replace <img src="…">
$content = preg_replace_callback(
'/<img[^>]+src=["\']([^"\']+\.(?:jpg|jpeg|png))["\'][^>]*>/i',
function ($m) use ($replace_url) {
$new = $replace_url($m[1]);
return ($new !== $m[1]) ? str_replace($m[1], $new, $m[0]) : $m[0];
},
$content
);
// Replace <a href="…"> linking to images
$content = preg_replace_callback(
'/<a[^>]+href=["\']([^"\']+\.(?:jpg|jpeg|png))["\'][^>]*>/i',
function ($m) use ($replace_url, &$links) {
$new = $replace_url($m[1]);
if ($new !== $m[1]) { $links++; return str_replace($m[1], $new, $m[0]); }
return $m[0];
},
$content
);
if ($content !== $original) {
wp_update_post(['ID' => $post_id, 'post_content' => $content]);
$updated++;
$log[] = sprintf('✅ Updated: %s (ID %d)', get_post_type($post_id), $post_id);
}
// Update featured image attachment if needed
$thumb_id = get_post_thumbnail_id($post_id);
if ($thumb_id && !in_array($thumb_id, wpturbo_get_excluded_images())) {
$thumb_path = get_attached_file($thumb_id);
if ($thumb_path && !str_ends_with($thumb_path, ".$extension")) {
$new_path = preg_replace('/\.(jpg|jpeg|png)$/i', ".$extension", $thumb_path);
if (file_exists($new_path)) {
update_attached_file($thumb_id, $new_path);
wp_update_post(['ID' => $thumb_id, 'post_mime_type' => $use_avif ? 'image/avif' : 'image/webp']);
$meta = wp_generate_attachment_metadata($thumb_id, $new_path);
wp_update_attachment_metadata($thumb_id, $meta);
$log[] = sprintf('✅ Updated thumbnail: %s → %s', basename($thumb_path), basename($new_path));
}
}
}
}
$log[] = sprintf('Checked %d images, updated %d posts, changed %d links', $checked, $updated, $links);
update_option('webp_conversion_log', array_slice((array) $log, -500));
wp_send_json_success(['message' => sprintf('Checked %d images, updated %d posts', $checked, $updated)]);
}
// ── Cleanup leftover originals ───────────────────────────────────────
function wpturbo_cleanup_leftover_originals() {
if (!isset($_GET['cleanup_leftover_originals']) || !current_user_can('manage_options')) return false;
$log = get_option('webp_conversion_log', []);
$uploads_dir = wp_upload_dir()['basedir'];
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($uploads_dir));
$deleted = 0;
$failed = 0;
$use_avif = wpturbo_get_use_avif();
$cur_ext = $use_avif ? 'avif' : 'webp';
$alt_ext = $use_avif ? 'webp' : 'avif';
$mode = wpturbo_get_resize_mode();
$max_values = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
$excluded = wpturbo_get_excluded_images();
// Build set of active files
$attachments = get_posts([
'post_type' => 'attachment', 'posts_per_page' => -1, 'fields' => 'ids',
'post_mime_type' => ['image/jpeg', 'image/png', 'image/webp', 'image/avif'],
]);
$active = [];
foreach ($attachments as $aid) {
$file = get_attached_file($aid);
$meta = wp_get_attachment_metadata($aid);
$dirname = dirname($file);
$bname = pathinfo($file, PATHINFO_FILENAME);
if (in_array($aid, $excluded)) {
// Keep everything for excluded images
if ($file && file_exists($file)) $active[$file] = true;
foreach (['jpg', 'jpeg', 'png', 'webp', 'avif'] as $e) {
$p = "$dirname/$bname.$e";
if (file_exists($p)) $active[$p] = true;
}
foreach ($max_values as $i => $d) {
$sfx = ($i === 0) ? '' : "-$d";
foreach (['webp', 'avif'] as $e) {
$p = "$dirname/$bname$sfx.$e";
if (file_exists($p)) $active[$p] = true;
}
}
foreach (["$dirname/$bname-150x150.webp", "$dirname/$bname-150x150.avif"] as $p) {
if (file_exists($p)) $active[$p] = true;
}
if ($meta && isset($meta['sizes'])) {
foreach ($meta['sizes'] as $sd) {
$p = "$dirname/" . $sd['file'];
if (file_exists($p)) $active[$p] = true;
}
}
continue;
}
if ($file && file_exists($file)) {
$active[$file] = true;
foreach ($max_values as $i => $d) {
$sfx = ($i === 0) ? '' : "-$d";
$p = "$dirname/$bname$sfx.$cur_ext";
if (file_exists($p)) $active[$p] = true;
}
$p = "$dirname/$bname-150x150.$cur_ext";
if (file_exists($p)) $active[$p] = true;
}
}
if (!wpturbo_get_preserve_originals()) {
foreach ($files as $fi) {
if ($fi->isDir()) continue;
$fp = $fi->getPathname();
$ext = strtolower(pathinfo($fp, PATHINFO_EXTENSION));
if (!in_array($ext, ['webp', 'avif', 'jpg', 'jpeg', 'png'])) continue;
$rel = str_replace($uploads_dir . '/', '', $fp);
$parts = explode('/', $rel);
$valid = (count($parts) === 1) || (count($parts) === 3 && is_numeric($parts[0]) && is_numeric($parts[1]));
if (!$valid || isset($active[$fp])) continue;
if (in_array($ext, ['jpg', 'jpeg', 'png']) || $ext === $alt_ext) {
if (@unlink($fp)) {
$log[] = sprintf('Cleanup: Deleted %s', basename($fp));
$deleted++;
} else {
$log[] = sprintf('Cleanup: Failed %s', basename($fp));
$failed++;
}
}
}
}
$log[] = "<span style='font-weight:bold;color:#281E5D;'>Cleanup Complete</span>: Deleted $deleted files, $failed failed";
// Regenerate missing thumbnails
foreach ($attachments as $aid) {
if (in_array($aid, $excluded)) continue;
$fp = get_attached_file($aid);
if (!file_exists($fp) || strtolower(pathinfo($fp, PATHINFO_EXTENSION)) !== $cur_ext) continue;
$meta = wp_get_attachment_metadata($aid);
$tpath = $uploads_dir . '/' . dirname($meta['file']) . '/' . pathinfo($fp, PATHINFO_FILENAME) . "-150x150.$cur_ext";
if (!file_exists($tpath)) {
$meta = wp_generate_attachment_metadata($aid, $fp);
if (!is_wp_error($meta)) {
wp_update_attachment_metadata($aid, $meta);
$log[] = sprintf('Regenerated thumbnail for %s', basename($fp));
}
}
}
$log[] = "<span style='font-weight:bold;color:#281E5D;'>Thumbnail Regeneration Complete</span>";
update_option('webp_conversion_log', array_slice((array) $log, -500));
return true;
}
// ── Custom srcset output ─────────────────────────────────────────────
add_filter('wp_calculate_image_srcset', 'wpturbo_custom_srcset', 10, 5);
function wpturbo_custom_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id) {
if (in_array($attachment_id, wpturbo_get_excluded_images())) return $sources;
$ext = wpturbo_get_use_avif() ? '.avif' : '.webp';
$mode = wpturbo_get_resize_mode();
$max_vals = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
$upload = wp_upload_dir();
$base_path = $upload['basedir'] . '/' . dirname($image_meta['file']);
$base_name = pathinfo($image_meta['file'], PATHINFO_FILENAME);
$base_url = $upload['baseurl'] . '/' . dirname($image_meta['file']);
foreach ($max_vals as $i => $dim) {
if ($i === 0) continue;
$file = "$base_path/$base_name-$dim$ext";
if (file_exists($file)) {
$real = pixrefiner_get_real_dimensions($file);
$width = $real ? $real['width'] : $dim;
$sources[$width] = [
'url' => "$base_url/$base_name-$dim$ext",
'descriptor' => 'w',
'value' => $width,
];
}
}
$thumb = "$base_path/$base_name-150x150$ext";
if (file_exists($thumb)) {
$sources[150] = [
'url' => "$base_url/$base_name-150x150$ext",
'descriptor' => 'w',
'value' => 150,
];
}
return $sources;
}
// ── Exclusion AJAX ───────────────────────────────────────────────────
add_action('wp_ajax_webp_add_excluded_image', function () {
check_ajax_referer('webp_converter_nonce', 'nonce');
if (!current_user_can('manage_options') || !isset($_POST['attachment_id'])) wp_send_json_error('Invalid');
wp_send_json(wpturbo_add_excluded_image(absint($_POST['attachment_id'])) ? ['success' => true] : ['success' => false]);
});
add_action('wp_ajax_webp_remove_excluded_image', function () {
check_ajax_referer('webp_converter_nonce', 'nonce');
if (!current_user_can('manage_options') || !isset($_POST['attachment_id'])) wp_send_json_error('Invalid');
wp_send_json(wpturbo_remove_excluded_image(absint($_POST['attachment_id'])) ? ['success' => true] : ['success' => false]);
});
// ── Export media ZIP ─────────────────────────────────────────────────
add_action('wp_ajax_webp_export_media_zip', 'wpturbo_export_media_zip');
function wpturbo_export_media_zip() {
check_ajax_referer('webp_converter_nonce', 'nonce');
if (!current_user_can('manage_options')) wp_send_json_error('Permission denied');
wp_raise_memory_limit('admin');
set_time_limit(0);
$attachments = get_posts([
'post_type' => 'attachment', 'post_mime_type' => 'image',
'posts_per_page' => -1, 'fields' => 'ids',
]);
if (empty($attachments)) wp_send_json_error('No media files found');
$temp = tempnam(sys_get_temp_dir(), 'pixr_export_');
if (!$temp) wp_send_json_error('Cannot create temp file');
$zip = new ZipArchive();
if ($zip->open($temp, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
@unlink($temp);
wp_send_json_error('Cannot create ZIP');
}
$upload_dir = wp_upload_dir()['basedir'];
foreach ($attachments as $aid) {
$fp = get_attached_file($aid);
if (!$fp || !file_exists($fp)) continue;
$dirname = dirname($fp);
$bname = pathinfo($fp, PATHINFO_FILENAME);
$rel_dir = str_replace($upload_dir . '/', '', $dirname);
$zip->addFile($fp, "$rel_dir/" . basename($fp));
$meta = wp_get_attachment_metadata($aid);
if ($meta && isset($meta['sizes'])) {
foreach ($meta['sizes'] as $sd) {
$sf = "$dirname/" . $sd['file'];
if (file_exists($sf)) $zip->addFile($sf, "$rel_dir/" . $sd['file']);
}
}
foreach (['jpg', 'jpeg', 'png', 'webp', 'avif'] as $e) {
$rf = "$dirname/$bname.$e";
if (file_exists($rf) && $rf !== $fp) $zip->addFile($rf, "$rel_dir/$bname.$e");
foreach (glob("$dirname/$bname-*.$e") as $gf) {
if ($gf !== $fp) $zip->addFile($gf, "$rel_dir/" . basename($gf));
}
}
}
$zip->close();
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="media_export_' . date('Y-m-d_H-i-s') . '.zip"');
header('Content-Length: ' . filesize($temp));
readfile($temp);
flush();
@unlink($temp);
exit;
}
// ── Settings setters (admin GET handlers) ────────────────────────────
function wpturbo_set_max_widths() {
if (!isset($_GET['set_max_width'], $_GET['max_width']) || !current_user_can('manage_options')) return false;
$arr = array_filter(array_map('absint', explode(',', sanitize_text_field($_GET['max_width']))));
$arr = array_filter($arr, function ($w) { return $w > 0 && $w <= 9999; });
$arr = array_slice($arr, 0, 4);
if (!empty($arr)) {
update_option('webp_max_widths', implode(',', $arr));
pixrefiner_log(sprintf('Max widths set to: %spx', implode(', ', $arr)));
return true;
}
return false;
}
function wpturbo_set_max_heights() {
if (!isset($_GET['set_max_height'], $_GET['max_height']) || !current_user_can('manage_options')) return false;
$arr = array_filter(array_map('absint', explode(',', sanitize_text_field($_GET['max_height']))));
$arr = array_filter($arr, function ($h) { return $h > 0 && $h <= 9999; });
$arr = array_slice($arr, 0, 4);
if (!empty($arr)) {
update_option('webp_max_heights', implode(',', $arr));
pixrefiner_log(sprintf('Max heights set to: %spx', implode(', ', $arr)));
return true;
}
return false;
}
function wpturbo_set_resize_mode() {
if (!isset($_GET['set_resize_mode'], $_GET['resize_mode']) || !current_user_can('manage_options')) return false;
$m = sanitize_text_field($_GET['resize_mode']);
if (in_array($m, ['width', 'height']) && get_option('webp_resize_mode', 'width') !== $m) {
update_option('webp_resize_mode', $m);
pixrefiner_log(sprintf('Resize mode set to: %s', $m));
}
return true;
}
function wpturbo_set_quality() {
if (!isset($_GET['set_quality'], $_GET['quality']) || !current_user_can('manage_options')) return false;
$q = absint($_GET['quality']);
if ($q >= 0 && $q <= 100 && (int) get_option('webp_quality', 80) !== $q) {
update_option('webp_quality', $q);
pixrefiner_log(sprintf('Quality set to: %d', $q));
}
return true;
}
function wpturbo_set_batch_size() {
if (!isset($_GET['set_batch_size'], $_GET['batch_size']) || !current_user_can('manage_options')) return false;
$b = absint($_GET['batch_size']);
if ($b > 0 && $b <= 50) { update_option('webp_batch_size', $b); pixrefiner_log("Batch size set to: $b"); return true; }
return false;
}
function wpturbo_set_preserve_originals() {
if (!isset($_GET['set_preserve_originals'], $_GET['preserve_originals']) || !current_user_can('manage_options')) return false;
$v = rest_sanitize_boolean($_GET['preserve_originals']);
if (wpturbo_get_preserve_originals() !== $v) { update_option('webp_preserve_originals', $v); pixrefiner_log('Preserve originals: ' . ($v ? 'Yes' : 'No')); }
return true;
}
function wpturbo_set_disable_auto_conversion() {
if (!isset($_GET['set_disable_auto_conversion'], $_GET['disable_auto_conversion']) || !current_user_can('manage_options')) return false;
$v = rest_sanitize_boolean($_GET['disable_auto_conversion']);
if (wpturbo_get_disable_auto_conversion() !== $v) { update_option('webp_disable_auto_conversion', $v); pixrefiner_log('Auto-conversion: ' . ($v ? 'Disabled' : 'Enabled')); }
return true;
}
function wpturbo_set_min_size_kb() {
if (!isset($_GET['set_min_size_kb'], $_GET['min_size_kb']) || !current_user_can('manage_options')) return false;
$v = absint($_GET['min_size_kb']);
if (wpturbo_get_min_size_kb() !== $v) { update_option('webp_min_size_kb', $v); pixrefiner_log("Min size threshold: {$v} KB"); }
return true;
}
function wpturbo_set_max_height_cap() {
if (!isset($_GET['set_max_height_cap'], $_GET['max_height_cap']) || !current_user_can('manage_options')) return false;
$v = absint($_GET['max_height_cap']);
if (wpturbo_get_max_height_cap() !== $v) { update_option('webp_max_height_cap', $v); pixrefiner_log("Max height cap: " . ($v > 0 ? "{$v}px" : 'disabled')); }
return true;
}
function wpturbo_set_use_avif() {
if (!isset($_GET['set_use_avif'], $_GET['use_avif']) || !current_user_can('manage_options')) return false;
$v = rest_sanitize_boolean($_GET['use_avif']);
if (wpturbo_get_use_avif() !== $v) {
update_option('webp_use_avif', $v);
wpturbo_ensure_mime_types();
pixrefiner_log('Format set to: ' . ($v ? 'AVIF' : 'WebP'));
pixrefiner_log('Please reconvert all images after changing formats.');
}
return true;
}
function wpturbo_clear_log() {
if (!isset($_GET['clear_log']) || !current_user_can('manage_options')) return false;
update_option('webp_conversion_log', ['Log cleared']);
return true;
}
function wpturbo_reset_defaults() {
if (!isset($_GET['reset_defaults']) || !current_user_can('manage_options')) return false;
update_option('webp_max_widths', '1920,1200,600,300');
update_option('webp_max_heights', '1080,720,480,360');
update_option('webp_resize_mode', 'width');
update_option('webp_quality', 80);
update_option('webp_batch_size', 5);
update_option('webp_preserve_originals', false);
update_option('webp_disable_auto_conversion', false);
update_option('webp_min_size_kb', 0);
update_option('webp_max_height_cap', 0);
update_option('webp_use_avif', false);
pixrefiner_log('Settings reset to defaults');
return true;
}
// ── Clean up files on attachment deletion ─────────────────────────────
add_action('wp_delete_attachment', function ($attachment_id) {
if (in_array($attachment_id, wpturbo_get_excluded_images())) return;
$file = get_attached_file($attachment_id);
if ($file && file_exists($file)) @unlink($file);
$meta = wp_get_attachment_metadata($attachment_id);
if ($meta && isset($meta['sizes'])) {
$dir = wp_upload_dir()['basedir'];
foreach ($meta['sizes'] as $s) {
$sf = $dir . '/' . dirname($meta['file']) . '/' . $s['file'];
if (file_exists($sf)) @unlink($sf);
}
}
});
// ── Custom size labels in media UI ───────────────────────────────────
add_filter('image_size_names_choose', function ($sizes) {
$mode = wpturbo_get_resize_mode();
$vals = ($mode === 'width') ? wpturbo_get_max_widths() : wpturbo_get_max_heights();
$unit = ($mode === 'width') ? 'Width' : 'Height';
// Standard names for the additional sizes (index 1, 2, 3)
$standard_names = ['large', 'medium_large', 'medium'];
$sizes = ['thumbnail' => 'Thumbnail (150×150)'];
foreach ($vals as $i => $v) {
$additional_index = $i - 1; // index 0 is the main/full size
if ($i === 0) {
// The first value is the full-size cap — shown as "full" by WP already
continue;
}
if (isset($standard_names[$additional_index])) {
// Use the standard WP name
$sizes[$standard_names[$additional_index]] = sprintf('%s %dpx', $unit, $v);
} else {
// Fallback for any extra sizes beyond the 3 mapped ones
$sizes["custom-$v"] = sprintf('%s %dpx', $unit, $v);
}
}
return $sizes;
}, 999);
// ── Patch existing stamps ────────────────────────────────────────────
add_action('admin_init', function () {
if (!current_user_can('manage_options') || !isset($_GET['patch_pixrefiner_stamp'])) return;
$ids = get_posts([
'post_type' => 'attachment', 'post_mime_type' => ['image/webp', 'image/avif'],
'posts_per_page' => -1, 'fields' => 'ids',
]);
$stamp = pixrefiner_build_stamp();
foreach ($ids as $id) {
$meta = wp_get_attachment_metadata($id);
if (empty($meta['pixrefiner_stamp'])) {
$meta['pixrefiner_stamp'] = $stamp;
wp_update_attachment_metadata($id, $meta);
}
}
echo "<div class='notice notice-success'><p>✅ PixRefiner stamp patch complete.</p></div>";
});
// ── AJAX hooks ───────────────────────────────────────────────────────
add_action('admin_init', function () {
add_action('wp_ajax_webp_status', 'wpturbo_webp_conversion_status');
add_action('wp_ajax_webp_convert_single', 'wpturbo_convert_single_image');
add_action('wp_ajax_webp_export_media_zip', 'wpturbo_export_media_zip');
if (isset($_GET['convert_existing_images_to_webp']) && current_user_can('manage_options')) {
delete_option('webp_conversion_complete');
}
});
// ── Admin notices ────────────────────────────────────────────────────
add_action('admin_notices', function () {
if (isset($_GET['convert_existing_images_to_webp']))
echo '<div class="notice notice-success"><p>Conversion started. Monitor progress in Media → PixRefiner.</p></div>';
if (isset($_GET['set_max_width']) && wpturbo_set_max_widths())
echo '<div class="notice notice-success"><p>Max widths updated.</p></div>';
if (isset($_GET['set_max_height']) && wpturbo_set_max_heights())
echo '<div class="notice notice-success"><p>Max heights updated.</p></div>';
if (isset($_GET['reset_defaults']) && wpturbo_reset_defaults())
echo '<div class="notice notice-success"><p>Settings reset.</p></div>';
if (isset($_GET['set_min_size_kb']) && wpturbo_set_min_size_kb())
echo '<div class="notice notice-success"><p>Min size updated.</p></div>';
if (isset($_GET['set_max_height_cap']) && wpturbo_set_max_height_cap())
echo '<div class="notice notice-success"><p>Max height cap updated.</p></div>';
if (isset($_GET['set_use_avif']) && wpturbo_set_use_avif())
echo '<div class="notice notice-success"><p>Format updated. Please reconvert all images.</p></div>';
});
// ══════════════════════════════════════════════════════════════════════
// ADMIN UI
// ══════════════════════════════════════════════════════════════════════
add_action('admin_menu', function () {
add_media_page('PixRefiner', 'PixRefiner', 'manage_options', 'webp-converter', 'wpturbo_webp_converter_page');
});
function wpturbo_webp_converter_page() {
wp_enqueue_media();
wp_enqueue_script('media-upload');
wp_enqueue_style('media');
// Process settings
if (isset($_GET['set_max_width'])) wpturbo_set_max_widths();
if (isset($_GET['set_max_height'])) wpturbo_set_max_heights();
if (isset($_GET['set_resize_mode'])) wpturbo_set_resize_mode();
if (isset($_GET['set_quality'])) wpturbo_set_quality();
if (isset($_GET['set_batch_size'])) wpturbo_set_batch_size();
if (isset($_GET['set_preserve_originals'])) wpturbo_set_preserve_originals();
if (isset($_GET['set_disable_auto_conversion']))wpturbo_set_disable_auto_conversion();
if (isset($_GET['set_min_size_kb'])) wpturbo_set_min_size_kb();
if (isset($_GET['set_max_height_cap'])) wpturbo_set_max_height_cap();
if (isset($_GET['set_use_avif'])) wpturbo_set_use_avif();
if (isset($_GET['cleanup_leftover_originals'])) wpturbo_cleanup_leftover_originals();
if (isset($_GET['clear_log'])) wpturbo_clear_log();
if (isset($_GET['reset_defaults'])) wpturbo_reset_defaults();
$has_image_lib = extension_loaded('imagick') || extension_loaded('gd');
$has_avif = (extension_loaded('imagick') && in_array('AVIF', Imagick::queryFormats())) || (extension_loaded('gd') && function_exists('imageavif'));
?>
<div class="wrap" style="padding:0;font-size:14px;">
<div style="display:flex;gap:10px;align-items:flex-start;">
<!-- Left column -->
<div style="width:38%;display:flex;flex-direction:column;gap:10px;">
<!-- Controls -->
<div style="background:#fff;padding:20px;border-radius:10px;box-shadow:0 1px 3px rgba(0,0,0,.1);">
<h1 style="font-size:20px;font-weight:bold;color:#333;margin:-5px 0 15px 0;">PixRefiner v4.0 — Kadence Edition</h1>
<?php if (!$has_image_lib): ?>
<div class="notice notice-error" style="margin-bottom:20px;"><p>Warning: No image library (Imagick/GD) available.</p></div>
<?php endif; ?>
<?php if (wpturbo_get_use_avif() && !$has_avif): ?>
<div class="notice notice-warning" style="margin-bottom:20px;"><p>Warning: AVIF not supported on this server.</p></div>
<?php endif; ?>
<?php if (current_user_can('manage_options')): ?>
<div style="margin-bottom:20px;">
<label for="resize-mode" style="font-weight:bold;">Resize Mode:</label><br>
<select id="resize-mode" style="width:100px;padding:0 0 0 5px;">
<option value="width" <?php selected(wpturbo_get_resize_mode(), 'width'); ?>>Width</option>
<option value="height" <?php selected(wpturbo_get_resize_mode(), 'height'); ?>>Height</option>
</select>
</div>
<div style="margin-bottom:20px;">
<label for="max-width-input" style="font-weight:bold;">Max Widths (up to 4) — 150 is automatic:</label><br>
<input type="text" id="max-width-input" value="<?php echo esc_attr(implode(', ', wpturbo_get_max_widths())); ?>" style="width:200px;padding:5px;" placeholder="1920,1200,600,300">
<button id="set-max-width" class="button">Set Widths</button>
</div>
<div style="margin-bottom:20px;">
<label for="max-height-input" style="font-weight:bold;">Max Heights (up to 4) — 150 is automatic:</label><br>
<input type="text" id="max-height-input" value="<?php echo esc_attr(implode(', ', wpturbo_get_max_heights())); ?>" style="width:200px;padding:5px;" placeholder="1080,720,480,360">
<button id="set-max-height" class="button">Set Heights</button>
</div>
<div style="margin-bottom:20px;">
<label for="min-size-kb" style="font-weight:bold;">Min Size for Conversion (KB, 0 = disabled):</label><br>
<input type="number" id="min-size-kb" value="<?php echo esc_attr(wpturbo_get_min_size_kb()); ?>" min="0" style="width:50px;padding:5px;">
<button id="set-min-size-kb" class="button">Set Min Size</button>
</div>
<div style="margin-bottom:20px;">
<label for="max-height-cap" style="font-weight:bold;">Max Height Cap in Width mode (px, 0 = disabled):</label><br>
<input type="number" id="max-height-cap" value="<?php echo esc_attr(wpturbo_get_max_height_cap()); ?>" min="0" style="width:70px;padding:5px;">
<button id="set-max-height-cap" class="button">Set Height Cap</button>
</div>
<div style="margin-bottom:20px;"><label><input type="checkbox" id="use-avif" <?php checked(wpturbo_get_use_avif()); ?>> Use AVIF (instead of WebP)</label></div>
<div style="margin-bottom:20px;"><label><input type="checkbox" id="preserve-originals" <?php checked(wpturbo_get_preserve_originals()); ?>> Preserve Original Files</label></div>
<div style="margin-bottom:20px;"><label><input type="checkbox" id="disable-auto-conversion" <?php checked(wpturbo_get_disable_auto_conversion()); ?>> Disable Auto-Conversion on Upload</label></div>
<div style="margin-bottom:20px;display:flex;gap:10px;">
<button id="start-conversion" class="button">1. Convert/Scale</button>
<button id="cleanup-originals" class="button">2. Cleanup</button>
<button id="convert-post-images" class="button">3. Fix URLs</button>
<button id="run-all" class="button button-primary">Run All (1-3)</button>
<button id="stop-conversion" class="button" style="display:none;">Stop</button>
</div>
<div style="margin-bottom:20px;display:flex;gap:10px;">
<button id="clear-log" class="button">Clear Log</button>
<button id="reset-defaults" class="button">Reset Defaults</button>
<button id="export-media-zip" class="button">Export Media ZIP</button>
</div>
<?php else: ?>
<p>You need manage_options permission.</p>
<?php endif; ?>
</div>
<!-- Exclude images -->
<div style="background:#fff;padding:20px;border-radius:10px;box-shadow:0 1px 3px rgba(0,0,0,.1);">
<h2 style="font-size:16px;margin:0 0 15px 0;">Exclude Images</h2>
<button id="open-media-library" class="button" style="margin-bottom:20px;">Add from Media Library</button>
<div id="excluded-images">
<h3 style="font-size:14px;margin:0 0 10px 0;">Excluded Images</h3>
<ul id="excluded-images-list" style="list-style:none;padding:0;max-height:300px;overflow-y:auto;"></ul>
</div>
</div>
<!-- Info -->
<div style="background:#fff;padding:20px;border-radius:10px;box-shadow:0 1px 3px rgba(0,0,0,.1);">
<h2 style="font-size:16px;margin:0 0 15px 0;">Kadence Edition — What Changed</h2>
<p style="line-height:1.6;">
This build stores <b>real width & height</b> for every generated size so that Kadence Advanced Image Block can see all srcset options in its UI.<br><br>
Custom sizes are also mapped to standard WordPress names (<code>large</code>, <code>medium_large</code>, <code>medium</code>) which Kadence looks for.<br><br>
All Elementor-specific code has been removed for a leaner codebase.<br><br>
PixRefiner is a tool created by <b>Imran</b> with <b>Web Squadron</b> — please support his work!
</p>
<div style="margin-top:20px;">
<a href="https://www.paypal.com/paypalme/iamimransiddiq" target="_blank" class="button" style="border:none;" rel="noopener">Support Imran</a>
</div>
</div>
</div>
<!-- Right column: Log -->
<div style="width:62%;min-height:100vh;background:#fff;padding:20px;border-radius:10px;box-shadow:0 1px 3px rgba(0,0,0,.1);display:flex;flex-direction:column;">
<h3 style="font-size:16px;margin:0 0 10px 0;">Log (Last 500 Entries)</h3>
<pre id="log" style="background:#f9f9f9;padding:15px;flex:1;overflow-y:auto;border:1px solid #ddd;border-radius:5px;font-size:13px;"></pre>
</div>
</div>
</div>
<style>
.button.button-primary{background:#E500FF;color:#fff;padding:2px 10px;height:30px;line-height:26px;font-size:14px;font-weight:600;border:none;transition:all .2s}
.button.button-primary:hover{background:#B000CC}
.button:not(.button-primary){background:#dbe2e9;color:#444;padding:2px 10px;height:30px;line-height:26px;border:none;transition:all .2s}
.button:not(.button-primary):hover{background:#444;color:#fff}
#excluded-images-list li{display:flex;align-items:center;margin-bottom:10px}
#excluded-images-list img{max-width:50px;margin-right:10px}
input[type="text"],input[type="number"],select{padding:2px;height:30px;box-sizing:border-box}
</style>
<script>
document.addEventListener('DOMContentLoaded', function(){
let isConverting = false;
const nonce = '<?php echo wp_create_nonce("webp_converter_nonce"); ?>';
const ajaxUrl = '<?php echo admin_url("admin-ajax.php"); ?>';
const pageUrl = '<?php echo admin_url("admin.php?page=webp-converter"); ?>';
function updateStatus(){
fetch(ajaxUrl+'?action=webp_status&nonce='+nonce)
.then(r=>r.json()).then(d=>{
document.getElementById('log').innerHTML=d.log.reverse().join('<br>');
document.getElementById('resize-mode').value=d.resize_mode;
document.getElementById('max-width-input').value=d.max_widths;
document.getElementById('max-height-input').value=d.max_heights;
document.getElementById('preserve-originals').checked=d.preserve_originals;
document.getElementById('disable-auto-conversion').checked=d.disable_auto_conversion;
document.getElementById('min-size-kb').value=d.min_size_kb;
document.getElementById('max-height-cap').value=d.max_height_cap;
document.getElementById('use-avif').checked=d.use_avif;
updateExcludedImages(d.excluded_images);
}).catch(e=>console.error('Status error:',e));
}
function updateExcludedImages(list){
const ul=document.getElementById('excluded-images-list');
ul.innerHTML='';
list.forEach(img=>{
const li=document.createElement('li');
li.innerHTML=`<img decoding="async" src="${img.thumbnail}" alt="${img.title}"><span>${img.title} (ID: ${img.id})</span> <button class="remove-excluded button" data-id="${img.id}">Remove</button>`;
ul.appendChild(li);
});
document.querySelectorAll('.remove-excluded').forEach(b=>{
b.addEventListener('click',()=>{
fetch(ajaxUrl+'?action=webp_remove_excluded_image&nonce='+nonce,{
method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},
body:'attachment_id='+b.getAttribute('data-id')
}).then(r=>r.json()).then(()=>updateStatus());
});
});
}
function convertNext(offset){
if(!isConverting)return;
fetch(ajaxUrl+'?action=webp_convert_single&nonce='+nonce,{
method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},
body:'offset='+offset
}).then(r=>r.json()).then(d=>{
if(d.success){
updateStatus();
if(!d.data.complete&&isConverting)convertNext(d.data.offset);
else document.getElementById('stop-conversion').style.display='none';
}
}).catch(e=>{console.error(e);document.getElementById('stop-conversion').style.display='none';});
}
<?php if(current_user_can('manage_options')): ?>
const mf=wp.media({title:'Select Images to Exclude',button:{text:'Add to Excluded List'},multiple:true,library:{type:'image'}});
document.getElementById('open-media-library').addEventListener('click',()=>mf.open());
mf.on('select',()=>{
mf.state().get('selection').each(a=>{
fetch(ajaxUrl+'?action=webp_add_excluded_image&nonce='+nonce,{
method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},
body:'attachment_id='+a.id
}).then(r=>r.json()).then(()=>updateStatus());
});
});
document.getElementById('set-max-width').addEventListener('click',()=>{
fetch(pageUrl+'&set_max_width=1&max_width='+encodeURIComponent(document.getElementById('max-width-input').value)).then(()=>updateStatus());
});
document.getElementById('set-max-height').addEventListener('click',()=>{
fetch(pageUrl+'&set_max_height=1&max_height='+encodeURIComponent(document.getElementById('max-height-input').value)).then(()=>updateStatus());
});
document.getElementById('resize-mode').addEventListener('change',()=>{
fetch(pageUrl+'&set_resize_mode=1&resize_mode='+document.getElementById('resize-mode').value).then(()=>updateStatus());
});
document.getElementById('preserve-originals').addEventListener('click',()=>{
fetch(pageUrl+'&set_preserve_originals=1&preserve_originals='+(document.getElementById('preserve-originals').checked?1:0)).then(()=>updateStatus());
});
document.getElementById('disable-auto-conversion').addEventListener('click',()=>{
fetch(pageUrl+'&set_disable_auto_conversion=1&disable_auto_conversion='+(document.getElementById('disable-auto-conversion').checked?1:0)).then(()=>updateStatus());
});
document.getElementById('set-min-size-kb').addEventListener('click',()=>{
fetch(pageUrl+'&set_min_size_kb=1&min_size_kb='+document.getElementById('min-size-kb').value).then(()=>updateStatus());
});
document.getElementById('set-max-height-cap').addEventListener('click',()=>{
fetch(pageUrl+'&set_max_height_cap=1&max_height_cap='+document.getElementById('max-height-cap').value).then(()=>updateStatus());
});
document.getElementById('use-avif').addEventListener('click',()=>{
const c=document.getElementById('use-avif').checked;
if(c&&!confirm('Switching to AVIF requires reconverting all images. Continue?')){document.getElementById('use-avif').checked=false;return;}
fetch(pageUrl+'&set_use_avif=1&use_avif='+(c?1:0)).then(()=>updateStatus());
});
document.getElementById('start-conversion').addEventListener('click',()=>{
isConverting=true;document.getElementById('stop-conversion').style.display='inline-block';
fetch(pageUrl+'&convert_existing_images_to_webp=1').then(()=>{updateStatus();convertNext(0);});
});
document.getElementById('cleanup-originals').addEventListener('click',()=>{
fetch(pageUrl+'&cleanup_leftover_originals=1').then(()=>updateStatus());
});
document.getElementById('convert-post-images').addEventListener('click',()=>{
if(confirm('Update all post image URLs to the selected format?')){
fetch(ajaxUrl+'?action=convert_post_images_to_webp&nonce='+nonce,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'}})
.then(r=>r.json()).then(d=>{alert(d.success?d.data.message:'Error');updateStatus();});
}
});
document.getElementById('run-all').addEventListener('click',()=>{
if(!confirm('Run all steps?'))return;
isConverting=true;document.getElementById('stop-conversion').style.display='inline-block';
fetch(pageUrl+'&convert_existing_images_to_webp=1').then(()=>{
convertNext(0);
return new Promise(res=>{
const iv=setInterval(()=>{
fetch(ajaxUrl+'?action=webp_status&nonce='+nonce).then(r=>r.json()).then(d=>{
updateStatus();if(d.complete){clearInterval(iv);res();}
}).catch(()=>{clearInterval(iv);res();});
},1000);
});
}).then(()=>{
return fetch(ajaxUrl+'?action=convert_post_images_to_webp&nonce='+nonce,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'}})
.then(r=>r.json()).then(d=>{updateStatus();alert(d.success?d.data.message:'Error');});
}).then(()=>fetch(pageUrl+'&cleanup_leftover_originals=1')).then(()=>{
isConverting=false;document.getElementById('stop-conversion').style.display='none';updateStatus();alert('All steps completed!');
}).catch(e=>{console.error(e);isConverting=false;document.getElementById('stop-conversion').style.display='none';});
});
document.getElementById('stop-conversion').addEventListener('click',()=>{isConverting=false;document.getElementById('stop-conversion').style.display='none';});
document.getElementById('clear-log').addEventListener('click',()=>{fetch(pageUrl+'&clear_log=1').then(()=>updateStatus());});
document.getElementById('reset-defaults').addEventListener('click',()=>{if(confirm('Reset all settings?'))fetch(pageUrl+'&reset_defaults=1').then(()=>updateStatus());});
document.getElementById('export-media-zip').addEventListener('click',()=>{if(confirm('Export all media as ZIP?'))window.location.href=ajaxUrl+'?action=webp_export_media_zip&nonce='+nonce;});
<?php endif; ?>
updateStatus();
});
</script>
<?php
}
PixRefiner – Mehr Performance durch clevere Bildoptimierung
Alle relevanten Maßnahmen der Bildoptimierung, die für schnelle Ladezeiten bei WordPress erforderlich sind, übernimmt der PixRefiner vollkommen automatisiert!
Bildformat-Konvertierung für Wordpress
Alle Bilder, sowohl bestehende als auch zukünftige Uploads, werden automatisch ins WebP-Format konvertiert. Optional steht auch AVIF als moderneres Format zur Verfügung, wobei WebP aufgrund der breiteren Browser-Kompatibilität derzeit die sicherere Wahl ist.
Kontrolle über Bilddimensionen
Statt sich auf die WordPress-Standardgrößen zu verlassen, legt man selbst bis zu vier Zielbreiten fest – etwa 1920, 1200, 600 und 300 Pixel. Der erste Wert bestimmt die maximale Größe des Originalbilds, die weiteren Werte definieren die responsiven Varianten für verschiedene Bildschirmgrößen.
Ein 4000-Pixel breites Foto wird so beispielsweise auf maximal 1920 Pixel reduziert, während die Höhe proportional angepasst wird. Das 150 × 150 Pixel große Thumbnail bleibt dabei immer erhalten.
Optimierung bestehender Mediatheken auf der WP-Datenbank
Der PixRefiner arbeitet nicht nur bei neuen Uploads. Über die Funktion Convert/Scale lassen sich sämtliche bereits vorhandenen Bilder in der Mediathek nachträglich optimieren — inklusive Formatumwandlung und Größenanpassung.
Die Funktion Cleanup entfernt anschließend überflüssige Bildvarianten und alte Originale vom Server. Und Fix URLs sorgt dafür, dass alle Bild-Links in Beiträgen und Seiten auf die neuen WebP-Dateien aktualisiert werden.
Automatische Optimierung zukünftiger Uploads
Sobald der PixRefiner aktiv ist, wird jedes neu hochgeladene Bild automatisch anhand der festgelegten Werte optimiert – unabhängig davon, ob es sich um ein riesiges JPEG vom Smartphone oder ein PNG mit transparentem Hintergrund handelt.
Es landen nur noch optimierte Bilder in der Mediathek!
Flexible Ausschlussoptionen
Einzelne Bilder lassen sich gezielt von der Optimierung ausschließen — etwa Icons oder Logos, die in ihrem Originalformat erhalten bleiben sollen. Zusätzlich kann eine Mindestgröße in Kilobyte definiert werden, unter der Bilder nicht angetastet werden.
Wichtiger Hinweis: Sicherheit geht vor
Bevor der PixRefiner auf einer Live-Website eingesetzt wird, empfiehlt es sich dringend, die Optimierung zunächst auf einer Staging-Umgebung zu testen.
Ein vollständiges Backup der Datenbank und der Mediathek (z. B. über die Export-Funktion des PixRefiners selbst) sollte in jedem Fall vorab erstellt werden. So lässt sich bei unerwünschten Ergebnissen jederzeit der Ausgangszustand wiederherstellen.
PixRefiner für Kadence – Empfohlene Einstellungen

Resize Mode: Width – Als Resize Mode empfiehlt sich Width, da die meisten Bilder üblicherweise im Querformat vorliegen und die Breite der entscheidende Faktor für die Darstellung im Web ist.
Solltest du überwiegend Bilder im Hochformat auf der Website haben, solltest du natürlich Height auswählen, da in diesem Fall die Bildhöhe das Maximalmaß vorgibt.
Max-Width: Bei den Max Widths haben sich 1600, 1200 und 600 Pixel als praxistauglich erwiesen. 1600px deckt die volle Content-Breite auf Desktop-Bildschirmen ab und liefert auch auf Retina-Displays genügend Auflösung. 1200px dient als Zwischenstufe für Tablets und hochauflösende Mobilgeräte — gerade auf Smartphones mit 2x- oder 3x-Pixeldichte greift der Browser via srcset gerne auf diese Variante zurück. 600px bedient kleinere Darstellungen und Vorschaubilder. Das 150 × 150 Pixel große Thumbnail wird automatisch erzeugt und muss nicht konfiguriert werden.
Max-Height: Nur eintragen, wenn zuvor Height ausgewählt wurde. Die Maximalwerte entsprechen denen der Max-Width bei Querformat-Bildern – 1600, 1200 und 600 Pixel.
Min-size for Conversion: 0 – Wenn man möchte, kann man hier festlegen, dass Bilder erst ab einer bestimmten Größe (KB) optimiert werden. Sofern z.B. Bilder unter 20 KB grundsätzlich von der Optimierung ausgeschlossen werden sollen, da sie bereits recht klein sind, so müsste hier 20 eingegeben werden.
Max Height Cap in Width mode: 1200 – Diese Funktion habe ich zusätzlich implementiert. Sie sorgt im Width-Modus dafür, dass Hochformat-Bilder nach dem Width-Resize nochmal in der Höhe gekappt werden.
- Querformat (z. B. 4000 × 3000) → wird auf 1600 × 1200 reduziert — der Height Cap greift hier nicht, da die Höhe exakt 1200px ist
- Hochformat (z. B. 2000 × 3000) → wird erst auf 1600 × 2400 reduziert (Width), dann auf 800 × 1200 gekappt (Height Cap)
Für Hochformat-Bilder empfiehlt sich ein Max Height Cap von 1200px — damit wird verhindert, dass Porträtfotos nach dem Width-Resize unverhältnismäßig hoch bleiben und unnötig Speicherplatz belegen.
Use AVIF (instead of WebP): Deaktiviert – Als Format bleibt WebP die sichere Wahl, da es von praktisch allen aktuellen Browsern unterstützt wird.
Preserve Original Files: Deaktiviert – Sinn und Zweck dieses Verfahrens ist es ja, die Mediathek (und damit die Datenbank) von diesen Übergrößen zu bereinigen.
Disable Auto-Conversion on Upload: Deaktiviert – Die Auto-Conversion sollte aktiviert bleiben – also hier nicht deaktivieren, damit jeder zukünftige Upload automatisch nach diesen Vorgaben optimiert wird.
Nun wünsche ich dir viel Erfolg bei der automatisierten Bildoptimierung!
