Copied WebP Convertor files from Zed Suite repo to make it free to all users

This commit is contained in:
ZAKS Web 2025-07-25 12:23:05 +02:00
parent f546a9277a
commit a2dd186183
47 changed files with 969 additions and 0 deletions

View File

@ -0,0 +1,53 @@
<?php
/**
* Plugin Name: ZedSuite WebP Converter
* Description: Automatically converts uploaded JPEG and PNG images to WebP with 75% compression, adds them to the media library, and deletes the original file.
* Version: 0.1.1
* Author: Ze'ev Schurmann
* License: GPL3 or later
*/
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}
add_filter('wp_handle_upload', 'zs_convert_to_webp', 10, 2);
function zs_convert_to_webp($upload, $context) {
$file_path = $upload['file'];
$file_type = $upload['type'];
if (!in_array($file_type, ['image/jpeg', 'image/png'])) {
return $upload; // Skip non-JPEG/PNG files
}
$image = ($file_type === 'image/jpeg') ? imagecreatefromjpeg($file_path) : imagecreatefrompng($file_path);
if (!$image) {
return $upload; // If image creation fails, return original upload
}
$webp_path = preg_replace('/\.(jpe?g|png)$/i', '.webp', $file_path);
if (imagewebp($image, $webp_path, 75)) {
imagedestroy($image);
// Validate WebP file
if (file_exists($webp_path) && getimagesize($webp_path)['mime'] === 'image/webp') {
unlink($file_path); // Delete the original file
// Update media library to point to the WebP file
$upload['file'] = $webp_path;
$upload['type'] = 'image/webp';
$upload['url'] = preg_replace('/\.(jpe?g|png)$/i', '.webp', $upload['url']);
} else {
unlink($webp_path); // Remove invalid WebP file
add_action('admin_notices', function () {
echo '<div class="notice notice-error"><p><strong>Error:</strong> Image conversion to WebP failed. The original file has been kept.</p></div>';
});
}
}
return $upload;
}

56
0.2.1/css/webp-test.css Normal file
View File

@ -0,0 +1,56 @@
.img-comp-container {
position: relative;
height: auto;
overflow: hidden;
display: none;
}
.img-comp-img {
width: auto;
height: 100%;
display: block;
position: relative;
}
.img-comp-overlay {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
height: 100%;
width: 50%; /* Default */
}
/*.img-comp-overlay img {
width: 100%;
height: 100%;
display: block;
position: relative;
}*/
.slider-handle {
position: absolute;
top: 0;
bottom: 0;
width: 24px;
background: rgba(255,255,255,0.5);
border: 1px solid #999;
cursor: ew-resize;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.img-comp-labels {
display: flex;
width: 100%; /* or any fixed width */
height: auto; /* example height */
}
.img-comp-labels-child {
flex: 1; /* both boxes take equal space */
padding: 20px;
box-sizing: border-box;
}
.img-comp-labels-left {
text-align: left;
}
.img-comp-labels-right {
text-align: right;
}

94
0.2.1/js/webp-test.js Normal file
View File

@ -0,0 +1,94 @@
jQuery(document).ready(function($){
const sliderContainer = $('#zs_webp_test_slider_container');
const beforeImg = $('#before_img');
const afterImg = $('#after_img');
const sliderHandle = $('.slider-handle');
const overlay = $('.img-comp-overlay');
const sourceInfo = $('#zs_webp_source_info');
const labelWebP = $('#webp-label');
const labelPNG = $('#png-label');
let dragging = false;
function waitForImagesToLoad(callback) {
let loadedCount = 0;
function checkLoaded() {
loadedCount++;
if (loadedCount === 2) {
callback();
}
}
// If already loaded (from cache)
if (beforeImg[0].complete) checkLoaded();
else beforeImg.on('load', checkLoaded).on('error', function () {
console.error("Before image failed to load.");
});
if (afterImg[0].complete) checkLoaded();
else afterImg.on('load', checkLoaded).on('error', function () {
console.error("After image failed to load.");
});
}
//function setOverlayWidth(percent) {
// overlay.width(percent + '%');
// sliderHandle.css('left', percent + '%');
//}
function setOverlayWidth(percent) {
$('.img-comp-overlay').css('width', percent + '%');
$('.slider-handle').css('left', percent + '%');
}
// Then call this function
waitForImagesToLoad(function () {
$('.img-comp-container').fadeIn();
//initializeSlider(); // Your custom slider logic
// Initialize slider position to 50%
setOverlayWidth(50);
// Draggable handle
sliderHandle.on('mousedown touchstart', function(e) {
e.preventDefault();
dragging = true;
});
$(document).on('mouseup touchend', function() {
dragging = false;
});
$(document).on('mousemove touchmove', function(e) {
if (!dragging) return;
let containerOffset = sliderContainer.offset().left;
let pageX = e.pageX || e.originalEvent.touches[0].pageX;
let pos = pageX - containerOffset;
let width = sliderContainer.width();
let percent = Math.min(Math.max(pos / width * 100, 0), 100);
setOverlayWidth(percent);
});
// Click on any webp sample to load before/after
$('#zs_webp_test_images img').click(function(){
let basename = $(this).data('basename');
$.post(zsWebpTest.ajax_url, {
action: 'zs_convert_png_to_webp',
nonce: zsWebpTest.nonce,
basename: basename,
quality: $('#zs_webp_quality_slider').val()
}, function(response){
if (response.success) {
let originalPNG = zsWebpTest.plugin_url + 'samples/' + basename + '.png';
let convertedWebP = response.data.webp_url;
beforeImg.attr('src', originalPNG);
afterImg.attr('src', convertedWebP);
labelWebP.html('WebP [' + response.data.webp_size + ' KBytes]');
labelPNG.html('PNG [' + response.data.png_size + ' KBytes]');
sourceInfo.html('Source: <a href="' + response.data.source_info + '" target="_blank">' + response.data.source_info + '</a>');
sliderContainer.show();
setOverlayWidth(50);
} else {
alert(response.data || 'Error loading images');
}
});
});
});
});

View File

@ -0,0 +1 @@
https://pixabay.com/photos/business-idea-planning-board-3683781/

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 KiB

View File

@ -0,0 +1 @@
https://pixabay.com/photos/mexico-cdmx-reform-chapultepec-7596566/

BIN
0.2.1/samples/building.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

View File

@ -0,0 +1 @@
https://pixabay.com/photos/boat-lake-mountains-snow-italy-6686952/

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

1
0.2.1/samples/desk.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/coffee-phone-paper-business-792113/

BIN
0.2.1/samples/desk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

View File

@ -0,0 +1 @@
https://pixabay.com/photos/chick-ducklings-nature-cute-animal-8160008/

BIN
0.2.1/samples/duckling.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 KiB

1
0.2.1/samples/girl.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/waiting-unhappy-woman-rain-china-9588284/

BIN
0.2.1/samples/girl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 KiB

View File

@ -0,0 +1 @@
https://pixabay.com/photos/meeting-brainstorming-business-594091/

BIN
0.2.1/samples/meeting.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

View File

@ -0,0 +1 @@
https://pixabay.com/photos/coffee-phone-paper-business-792113/

BIN
0.2.1/samples/mountains.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 KiB

1
0.2.1/samples/plane.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/aircraft-pilatus-pc-24-corporate-jet-5611528/

BIN
0.2.1/samples/plane.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

1
0.2.1/samples/street.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/street-japan-city-urban-road-5130030/

BIN
0.2.1/samples/street.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 KiB

View File

@ -0,0 +1,298 @@
<?php
/**
* Plugin Name: ZedSuite WebP Converter
* Description: Automatically converts uploaded JPEG and PNG images to WebP with 75% compression (or you can set your own compression), adds them to the media library, and deletes the original file.
* Version: 0.2.1
* Author: Ze'ev Schurmann
* License: GPL3 or later
*/
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}
if (is_admin()) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
add_filter('wp_handle_upload', 'zs_convert_to_webp', 10, 2);
function zs_convert_to_webp($upload, $context) {
$file_path = $upload['file'];
$file_type = $upload['type'];
if (!in_array($file_type, ['image/jpeg', 'image/png'])) {
return $upload; // Skip non-JPEG/PNG files
}
if ($file_type === 'image/jpeg') {
$image = imagecreatefromjpeg($file_path);
} else {
$image = imagecreatefrompng($file_path);
// Force palette-based PNGs to truecolor (indexed → RGB)
if ($image && imageistruecolor($image) === false) {
$truecolor = imagecreatetruecolor(imagesx($image), imagesy($image));
imagecopy($truecolor, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
imagedestroy($image);
$image = $truecolor;
}
}
if (!$image) {
return $upload; // If image creation fails, return original upload
}
$webp_path = preg_replace('/\.(jpe?g|png)$/i', '.webp', $file_path);
if ($file_type === 'image/png') {
imagealphablending($image, false);
imagesavealpha($image, true);
}
// Get quality setting from wp_options
$quality = get_option('zedsuite_webpconv_quality', 75);
// If quality is 101 then save with lossless compression
$webp_saved = ($quality == 101)
? imagewebp($image, $webp_path, true)
: imagewebp($image, $webp_path, $quality);
if ($webp_saved) {
imagedestroy($image);
// Validate WebP file
if (file_exists($webp_path) && getimagesize($webp_path)['mime'] === 'image/webp') {
unlink($file_path); // Delete the original file
// Update media library to point to the WebP file
$upload['file'] = $webp_path;
$upload['type'] = 'image/webp';
$upload['url'] = preg_replace('/\.(jpe?g|png)$/i', '.webp', $upload['url']);
} else {
unlink($webp_path); // Remove invalid WebP file
add_action('admin_notices', function () {
echo '<div class="notice notice-error"><p><strong>Error:</strong> Image conversion to WebP failed. The original file has been kept.</p></div>';
});
}
}
return $upload;
}
add_action('admin_menu', 'zs_webp_settings_menu');
function zs_webp_settings_menu() {
add_submenu_page(
'upload.php', // Parent slug (Media menu)
'ZS WebP Settings', // Page title
'ZS WebP Settings', // Menu title
'manage_options', // Capability (admin only)
'zs-webp-settings', // Menu slug
'zs_webp_settings_page' // Callback to render page
);
}
function zs_webp_settings_page() {
if (!current_user_can('manage_options')) {
wp_die('Unauthorized user');
}
$option_key = 'zedsuite_webpconv_quality';
$default_quality = 75;
// Folder paths
$plugin_dir = plugin_dir_path(__FILE__);
$webp_dir = $plugin_dir . 'samples/'; // Your 10 images here
// Scan webp files
$webp_files = glob($webp_dir . '*.webp');
// Create option on first load
if (get_option($option_key) === false) {
add_option($option_key, $default_quality);
}
// Handle form actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['zs_webp_reset'])) {
update_option($option_key, $default_quality);
} elseif (isset($_POST['zs_webp_save']) && isset($_POST['webp_quality'])) {
$q = intval($_POST['webp_quality']);
if ($q >= 0 && $q <= 101) {
update_option($option_key, $q);
}
}
}
$quality = intval(get_option($option_key));
$plugin_data = get_plugin_data(__FILE__);
$version = esc_html($plugin_data['Version']);
?>
<div class="wrap">
<h1>ZS WebP Converter Settings</h1>
<p><strong>Version:</strong> <?php echo $version; ?></p>
<p>This plugin automatically converts JPEG and PNG uploads to WebP format. You can adjust the quality of the output image below. Use a lower value for smaller files, or 101 for lossless compression.</p>
<form method="post">
<table class="form-table">
<tr>
<th scope="row">
<label for="webp_quality">WebP Quality</label>
</th>
<td>
<input style="width: 300px;" type="range" min="0" max="101" id="webp_quality" name="webp_quality" value="<?php echo $quality; ?>" oninput="this.nextElementSibling.value = this.value">
<output><?php echo $quality; ?></output>
<p class="description">0 = lowest quality, 101 = lossless</p>
</td>
</tr>
</table>
<div style="display: flex; justify-content: flex-start; gap: 10px;">
<button type="submit" name="zs_webp_reset" class="button button-secondary">Reset</button>
<button type="submit" name="zs_webp_save" class="button button-primary">Save</button>
</div>
</form>
</div>
<h2>Test</h2>
<div id="zs_webp_test_images" style="display:flex; flex-wrap: wrap; gap: 10px;">
<?php foreach ($webp_files as $webp_file):
$basename = basename($webp_file, '.webp');
$url = plugin_dir_url(__FILE__) . "samples/$basename.webp";
?>
<img src="<?php echo esc_url($url); ?>" data-basename="<?php echo esc_attr($basename); ?>" style="cursor:pointer; width: 100px; height: auto; border: 1px solid #ccc;" />
<?php endforeach; ?>
</div>
<?php
if (file_exists($png_path)) {
$sizeBytes = filesize($png_path); // Size in bytes
$PNGsizeKB = round($sizeBytes / 1024, 2); // Convert to KB, 2 decimal places
}
if (file_exists($webp_tmp_path)) {
$sizeBytes = filesize($webp_tmp_path); // Size in bytes
$WebPsizeKB = round($sizeBytes / 1024, 2); // Convert to KB, 2 decimal places
}
?>
<div id="zs_webp_test_slider_container" style="margin-top: 20px; display:none;">
<h3>Comparison</h3>
<div class="img-comp-labels" style="max-width: 600px; margin: auto;">
<div id="webp-label" class="img-comp-labels-child img-comp-labels-left">WebP [<?php echo esc_attr($WebPsizeKB); ?> KBytes]</div>
<div id="png-label" class="img-comp-labels-child img-comp-labels-right">PNG [<?php echo esc_attr($PNGsizeKB); ?> KBytes]</div>
</div>
<div id="zs_webp_slider" class="img-comp-container" style="max-width: 600px; margin: auto;">
<img id="before_img" src="" alt="Before Image" class="img-comp-img" />
<div class="img-comp-overlay">
<img id="after_img" src="" alt="After Image" class="img-comp-img" />
</div>
<div class="slider-handle"></div>
</div>
<p id="zs_webp_source_info"></p>
</div>
</div>
<style>
#wpfooter {
position: relative !important;
margin-top: 800px !important; /* Adjust height as needed */
}
</style>
<?php
}
add_action('admin_enqueue_scripts', 'zs_enqueue_webp_test_scripts');
function zs_enqueue_webp_test_scripts($hook) {
if ($hook !== 'media_page_zs-webp-settings') return;
wp_enqueue_style('zs-webp-test-style', plugin_dir_url(__FILE__) . 'css/webp-test.css', [], '1.0');
wp_enqueue_script('zs-webp-test-script', plugin_dir_url(__FILE__) . 'js/webp-test.js', ['jquery'], '1.0', true);
// Pass ajax URL and nonce for PNG conversion
wp_localize_script('zs-webp-test-script', 'zsWebpTest', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('zs_webp_test_nonce'),
'quality' => intval(get_option('zedsuite_webpconv_quality', 75)),
'plugin_url' => plugin_dir_url(__FILE__),
]);
}
add_action('wp_ajax_zs_convert_png_to_webp', 'zs_ajax_convert_png_to_webp');
function zs_ajax_convert_png_to_webp() {
check_ajax_referer('zs_webp_test_nonce', 'nonce');
$basename = sanitize_file_name($_POST['basename'] ?? '');
#$quality = intval($_POST['quality'] ?? 75);
$quality = get_option('zedsuite_webpconv_quality', 75);
if ($basename === '') {
wp_send_json_error('Invalid basename');
}
$plugin_dir = plugin_dir_path(__FILE__);
$samples_dir = $plugin_dir . 'samples/';
$temp_dir = $plugin_dir . 'temp/';
$png_path = $samples_dir . $basename . '.png';
$webp_tmp_path = $temp_dir . $basename . '_temp.webp';
if (!file_exists($png_path)) {
wp_send_json_error('PNG file missing');
}
$image = imagecreatefrompng($png_path);
if (!$image) {
wp_send_json_error('Could not load PNG');
}
if ($quality === 101) {
$result = imagewebp($image, $webp_tmp_path, 100);
// Set lossless flag
// PHP GD does not natively support lossless, but imagick can.
// For simplicity, treat 101 as 100 quality here.
} else {
$result = imagewebp($image, $webp_tmp_path, $quality);
}
imagedestroy($image);
if (!$result) {
wp_send_json_error('Conversion failed');
}
$webp_url = plugin_dir_url(__FILE__) . "temp/{$basename}_temp.webp";
$png_url = plugin_dir_url(__FILE__) . "samples/{$basename}.png";
// Read NFO file content
$nfo_path = $samples_dir . $basename . '.nfo';
$source_info = '';
if (file_exists($nfo_path)) {
$source_info = trim(file_get_contents($nfo_path));
}
if (file_exists($png_path)) {
$sizeBytes = filesize($png_path); // Size in bytes
$PNGsizeKB = round($sizeBytes / 1024, 2); // Convert to KB, 2 decimal places
}
if (file_exists($webp_tmp_path)) {
$sizeBytes = filesize($webp_tmp_path); // Size in bytes
$WebPsizeKB = round($sizeBytes / 1024, 2); // Convert to KB, 2 decimal places
}
wp_send_json_success([
'webp_url' => $webp_url,
'webp_size' => $WebPsizeKB,
'png_url' => $png_url,
'png_size' => $PNGsizeKB,
'source_info' => $source_info,
]);
}

56
0.2/css/webp-test.css Normal file
View File

@ -0,0 +1,56 @@
.img-comp-container {
position: relative;
height: auto;
overflow: hidden;
display: none;
}
.img-comp-img {
width: auto;
height: 100%;
display: block;
position: relative;
}
.img-comp-overlay {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
height: 100%;
width: 50%; /* Default */
}
/*.img-comp-overlay img {
width: 100%;
height: 100%;
display: block;
position: relative;
}*/
.slider-handle {
position: absolute;
top: 0;
bottom: 0;
width: 24px;
background: rgba(255,255,255,0.5);
border: 1px solid #999;
cursor: ew-resize;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.img-comp-labels {
display: flex;
width: 100%; /* or any fixed width */
height: auto; /* example height */
}
.img-comp-labels-child {
flex: 1; /* both boxes take equal space */
padding: 20px;
box-sizing: border-box;
}
.img-comp-labels-left {
text-align: left;
}
.img-comp-labels-right {
text-align: right;
}

94
0.2/js/webp-test.js Normal file
View File

@ -0,0 +1,94 @@
jQuery(document).ready(function($){
const sliderContainer = $('#zs_webp_test_slider_container');
const beforeImg = $('#before_img');
const afterImg = $('#after_img');
const sliderHandle = $('.slider-handle');
const overlay = $('.img-comp-overlay');
const sourceInfo = $('#zs_webp_source_info');
const labelWebP = $('#webp-label');
const labelPNG = $('#png-label');
let dragging = false;
function waitForImagesToLoad(callback) {
let loadedCount = 0;
function checkLoaded() {
loadedCount++;
if (loadedCount === 2) {
callback();
}
}
// If already loaded (from cache)
if (beforeImg[0].complete) checkLoaded();
else beforeImg.on('load', checkLoaded).on('error', function () {
console.error("Before image failed to load.");
});
if (afterImg[0].complete) checkLoaded();
else afterImg.on('load', checkLoaded).on('error', function () {
console.error("After image failed to load.");
});
}
//function setOverlayWidth(percent) {
// overlay.width(percent + '%');
// sliderHandle.css('left', percent + '%');
//}
function setOverlayWidth(percent) {
$('.img-comp-overlay').css('width', percent + '%');
$('.slider-handle').css('left', percent + '%');
}
// Then call this function
waitForImagesToLoad(function () {
$('.img-comp-container').fadeIn();
//initializeSlider(); // Your custom slider logic
// Initialize slider position to 50%
setOverlayWidth(50);
// Draggable handle
sliderHandle.on('mousedown touchstart', function(e) {
e.preventDefault();
dragging = true;
});
$(document).on('mouseup touchend', function() {
dragging = false;
});
$(document).on('mousemove touchmove', function(e) {
if (!dragging) return;
let containerOffset = sliderContainer.offset().left;
let pageX = e.pageX || e.originalEvent.touches[0].pageX;
let pos = pageX - containerOffset;
let width = sliderContainer.width();
let percent = Math.min(Math.max(pos / width * 100, 0), 100);
setOverlayWidth(percent);
});
// Click on any webp sample to load before/after
$('#zs_webp_test_images img').click(function(){
let basename = $(this).data('basename');
$.post(zsWebpTest.ajax_url, {
action: 'zs_convert_png_to_webp',
nonce: zsWebpTest.nonce,
basename: basename,
quality: $('#zs_webp_quality_slider').val()
}, function(response){
if (response.success) {
let originalPNG = zsWebpTest.plugin_url + 'samples/' + basename + '.png';
let convertedWebP = response.data.webp_url;
beforeImg.attr('src', originalPNG);
afterImg.attr('src', convertedWebP);
labelWebP.html('WebP [' + response.data.webp_size + ' KBytes]');
labelPNG.html('PNG [' + response.data.png_size + ' KBytes]');
sourceInfo.html('Source: <a href="' + response.data.source_info + '" target="_blank">' + response.data.source_info + '</a>');
sliderContainer.show();
setOverlayWidth(50);
} else {
alert(response.data || 'Error loading images');
}
});
});
});
});

View File

@ -0,0 +1 @@
https://pixabay.com/photos/business-idea-planning-board-3683781/

BIN
0.2/samples/blackboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 KiB

1
0.2/samples/building.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/mexico-cdmx-reform-chapultepec-7596566/

BIN
0.2/samples/building.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

View File

@ -0,0 +1 @@
https://pixabay.com/photos/boat-lake-mountains-snow-italy-6686952/

BIN
0.2/samples/citylights.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

1
0.2/samples/desk.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/coffee-phone-paper-business-792113/

BIN
0.2/samples/desk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

1
0.2/samples/duckling.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/chick-ducklings-nature-cute-animal-8160008/

BIN
0.2/samples/duckling.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 KiB

1
0.2/samples/girl.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/waiting-unhappy-woman-rain-china-9588284/

BIN
0.2/samples/girl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 KiB

1
0.2/samples/meeting.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/meeting-brainstorming-business-594091/

BIN
0.2/samples/meeting.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

View File

@ -0,0 +1 @@
https://pixabay.com/photos/coffee-phone-paper-business-792113/

BIN
0.2/samples/mountains.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 KiB

1
0.2/samples/plane.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/aircraft-pilatus-pc-24-corporate-jet-5611528/

BIN
0.2/samples/plane.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

1
0.2/samples/street.nfo Normal file
View File

@ -0,0 +1 @@
https://pixabay.com/photos/street-japan-city-urban-road-5130030/

BIN
0.2/samples/street.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 KiB

298
0.2/zs-convert-to-webp.php Normal file
View File

@ -0,0 +1,298 @@
<?php
/**
* Plugin Name: ZedSuite WebP Converter
* Description: Automatically converts uploaded JPEG and PNG images to WebP with 75% compression (or you can set your own compression), adds them to the media library, and deletes the original file.
* Version: 0.2
* Author: Ze'ev Schurmann
* License: GPL3 or later
*/
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}
if (is_admin()) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
add_filter('wp_handle_upload', 'zs_convert_to_webp', 10, 2);
function zs_convert_to_webp($upload, $context) {
$file_path = $upload['file'];
$file_type = $upload['type'];
if (!in_array($file_type, ['image/jpeg', 'image/png'])) {
return $upload; // Skip non-JPEG/PNG files
}
if ($file_type === 'image/jpeg') {
$image = imagecreatefromjpeg($file_path);
} else {
$image = imagecreatefrompng($file_path);
// Force palette-based PNGs to truecolor (indexed → RGB)
if ($image && imageistruecolor($image) === false) {
$truecolor = imagecreatetruecolor(imagesx($image), imagesy($image));
imagecopy($truecolor, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
imagedestroy($image);
$image = $truecolor;
}
}
if (!$image) {
return $upload; // If image creation fails, return original upload
}
$webp_path = preg_replace('/\.(jpe?g|png)$/i', '.webp', $file_path);
if ($file_type === 'image/png') {
imagealphablending($image, false);
imagesavealpha($image, true);
}
// Get quality setting from wp_options
$quality = get_option('zedsuite_webpconv_quality', 75);
// If quality is 101 then save with lossless compression
$webp_saved = ($quality == 101)
? imagewebp($image, $webp_path, true)
: imagewebp($image, $webp_path, $quality);
if ($webp_saved) {
imagedestroy($image);
// Validate WebP file
if (file_exists($webp_path) && getimagesize($webp_path)['mime'] === 'image/webp') {
unlink($file_path); // Delete the original file
// Update media library to point to the WebP file
$upload['file'] = $webp_path;
$upload['type'] = 'image/webp';
$upload['url'] = preg_replace('/\.(jpe?g|png)$/i', '.webp', $upload['url']);
} else {
unlink($webp_path); // Remove invalid WebP file
add_action('admin_notices', function () {
echo '<div class="notice notice-error"><p><strong>Error:</strong> Image conversion to WebP failed. The original file has been kept.</p></div>';
});
}
}
return $upload;
}
add_action('admin_menu', 'zs_webp_settings_menu');
function zs_webp_settings_menu() {
add_submenu_page(
'upload.php', // Parent slug (Media menu)
'ZS WebP Settings', // Page title
'ZS WebP Settings', // Menu title
'manage_options', // Capability (admin only)
'zs-webp-settings', // Menu slug
'zs_webp_settings_page' // Callback to render page
);
}
function zs_webp_settings_page() {
if (!current_user_can('manage_options')) {
wp_die('Unauthorized user');
}
$option_key = 'zedsuite_webpconv_quality';
$default_quality = 75;
// Folder paths
$plugin_dir = plugin_dir_path(__FILE__);
$webp_dir = $plugin_dir . 'samples/'; // Your 10 images here
// Scan webp files
$webp_files = glob($webp_dir . '*.webp');
// Create option on first load
if (get_option($option_key) === false) {
add_option($option_key, $default_quality);
}
// Handle form actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['zs_webp_reset'])) {
update_option($option_key, $default_quality);
} elseif (isset($_POST['zs_webp_save']) && isset($_POST['webp_quality'])) {
$q = intval($_POST['webp_quality']);
if ($q >= 0 && $q <= 101) {
update_option($option_key, $q);
}
}
}
$quality = intval(get_option($option_key));
$plugin_data = get_plugin_data(__FILE__);
$version = esc_html($plugin_data['Version']);
?>
<div class="wrap">
<h1>ZS WebP Converter Settings</h1>
<p><strong>Version:</strong> <?php echo $version; ?></p>
<p>This plugin automatically converts JPEG and PNG uploads to WebP format. You can adjust the quality of the output image below. Use a lower value for smaller files, or 101 for lossless compression.</p>
<form method="post">
<table class="form-table">
<tr>
<th scope="row">
<label for="webp_quality">WebP Quality</label>
</th>
<td>
<input style="width: 300px;" type="range" min="0" max="101" id="webp_quality" name="webp_quality" value="<?php echo $quality; ?>" oninput="this.nextElementSibling.value = this.value">
<output><?php echo $quality; ?></output>
<p class="description">0 = lowest quality, 101 = lossless</p>
</td>
</tr>
</table>
<div style="display: flex; justify-content: flex-start; gap: 10px;">
<button type="submit" name="zs_webp_reset" class="button button-secondary">Reset</button>
<button type="submit" name="zs_webp_save" class="button button-primary">Save</button>
</div>
</form>
</div>
<h2>Test</h2>
<div id="zs_webp_test_images" style="display:flex; flex-wrap: wrap; gap: 10px;">
<?php foreach ($webp_files as $webp_file):
$basename = basename($webp_file, '.webp');
$url = plugin_dir_url(__FILE__) . "samples/$basename.webp";
?>
<img src="<?php echo esc_url($url); ?>" data-basename="<?php echo esc_attr($basename); ?>" style="cursor:pointer; width: 100px; height: auto; border: 1px solid #ccc;" />
<?php endforeach; ?>
</div>
<?php
if (file_exists($png_path)) {
$sizeBytes = filesize($png_path); // Size in bytes
$PNGsizeKB = round($sizeBytes / 1024, 2); // Convert to KB, 2 decimal places
}
if (file_exists($webp_tmp_path)) {
$sizeBytes = filesize($webp_tmp_path); // Size in bytes
$WebPsizeKB = round($sizeBytes / 1024, 2); // Convert to KB, 2 decimal places
}
?>
<div id="zs_webp_test_slider_container" style="margin-top: 20px; display:none;">
<h3>Comparison</h3>
<div class="img-comp-labels" style="max-width: 600px; margin: auto;">
<div id="webp-label" class="img-comp-labels-child img-comp-labels-left">WebP [<?php echo esc_attr($WebPsizeKB); ?> KBytes]</div>
<div id="png-label" class="img-comp-labels-child img-comp-labels-right">PNG [<?php echo esc_attr($PNGsizeKB); ?> KBytes]</div>
</div>
<div id="zs_webp_slider" class="img-comp-container" style="max-width: 600px; margin: auto;">
<img id="before_img" src="" alt="Before Image" class="img-comp-img" />
<div class="img-comp-overlay">
<img id="after_img" src="" alt="After Image" class="img-comp-img" />
</div>
<div class="slider-handle"></div>
</div>
<p id="zs_webp_source_info"></p>
</div>
</div>
<style>
#wpfooter {
position: relative !important;
margin-top: 800px !important; /* Adjust height as needed */
}
</style>
<?php
}
add_action('admin_enqueue_scripts', 'zs_enqueue_webp_test_scripts');
function zs_enqueue_webp_test_scripts($hook) {
if ($hook !== 'media_page_zs-webp-settings') return;
wp_enqueue_style('zs-webp-test-style', plugin_dir_url(__FILE__) . 'css/webp-test.css', [], '1.0');
wp_enqueue_script('zs-webp-test-script', plugin_dir_url(__FILE__) . 'js/webp-test.js', ['jquery'], '1.0', true);
// Pass ajax URL and nonce for PNG conversion
wp_localize_script('zs-webp-test-script', 'zsWebpTest', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('zs_webp_test_nonce'),
'quality' => intval(get_option('zedsuite_webpconv_quality', 75)),
'plugin_url' => plugin_dir_url(__FILE__),
]);
}
add_action('wp_ajax_zs_convert_png_to_webp', 'zs_ajax_convert_png_to_webp');
function zs_ajax_convert_png_to_webp() {
check_ajax_referer('zs_webp_test_nonce', 'nonce');
$basename = sanitize_file_name($_POST['basename'] ?? '');
#$quality = intval($_POST['quality'] ?? 75);
$quality = get_option('zedsuite_webpconv_quality', 75);
if ($basename === '') {
wp_send_json_error('Invalid basename');
}
$plugin_dir = plugin_dir_path(__FILE__);
$samples_dir = $plugin_dir . 'samples/';
$temp_dir = $plugin_dir . 'temp/';
$png_path = $samples_dir . $basename . '.png';
$webp_tmp_path = $temp_dir . $basename . '_temp.webp';
if (!file_exists($png_path)) {
wp_send_json_error('PNG file missing');
}
$image = imagecreatefrompng($png_path);
if (!$image) {
wp_send_json_error('Could not load PNG');
}
if ($quality === 101) {
$result = imagewebp($image, $webp_tmp_path, 100);
// Set lossless flag
// PHP GD does not natively support lossless, but imagick can.
// For simplicity, treat 101 as 100 quality here.
} else {
$result = imagewebp($image, $webp_tmp_path, $quality);
}
imagedestroy($image);
if (!$result) {
wp_send_json_error('Conversion failed');
}
$webp_url = plugin_dir_url(__FILE__) . "temp/{$basename}_temp.webp";
$png_url = plugin_dir_url(__FILE__) . "samples/{$basename}.png";
// Read NFO file content
$nfo_path = $samples_dir . $basename . '.nfo';
$source_info = '';
if (file_exists($nfo_path)) {
$source_info = trim(file_get_contents($nfo_path));
}
if (file_exists($png_path)) {
$sizeBytes = filesize($png_path); // Size in bytes
$PNGsizeKB = round($sizeBytes / 1024, 2); // Convert to KB, 2 decimal places
}
if (file_exists($webp_tmp_path)) {
$sizeBytes = filesize($webp_tmp_path); // Size in bytes
$WebPsizeKB = round($sizeBytes / 1024, 2); // Convert to KB, 2 decimal places
}
wp_send_json_success([
'webp_url' => $webp_url,
'webp_size' => $WebPsizeKB,
'png_url' => $png_url,
'png_size' => $PNGsizeKB,
'source_info' => $source_info,
]);
}